/*
 * Copyright 2021 CloudWeGo Authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package klog

import (
	"bytes"
	"context"
	"os"
	"testing"

	"github.com/cloudwego/kitex/internal/test"
)

// test package level functions without format
func normalOutput(t *testing.T, testLevel Level, want string, args ...interface{}) {
	buf := new(bytes.Buffer)
	SetOutput(buf)
	defer SetOutput(os.Stderr)
	switch testLevel {
	case LevelTrace:
		Trace(args...)
		test.Assert(t, buf.String() == want)
	case LevelDebug:
		Debug(args...)
		test.Assert(t, buf.String() == want)
	case LevelInfo:
		Info(args...)
		test.Assert(t, buf.String() == want)
	case LevelNotice:
		Notice(args...)
		test.Assert(t, buf.String() == want)
	case LevelWarn:
		Warn(args...)
		test.Assert(t, buf.String() == want)
	case LevelError:
		Error(args...)
		test.Assert(t, buf.String() == want)
	case LevelFatal:
		t.Fatal("fatal method cannot be tested")
	default:
		t.Errorf("unknown level: %d", testLevel)
	}
}

// test package level Ctx-related functions with 'format'
func ctxOutput(t *testing.T, testLevel Level, want, format string, args ...interface{}) {
	buf := new(bytes.Buffer)
	SetOutput(buf)
	defer SetOutput(os.Stderr)

	// the default logger implementation of CtxLogger is same as FormatLogger, no context handle now
	ctx := context.Background()

	switch testLevel {
	case LevelTrace:
		CtxTracef(ctx, format, args...)
		test.Assert(t, buf.String() == want)
	case LevelDebug:
		CtxDebugf(ctx, format, args...)
		test.Assert(t, buf.String() == want)
	case LevelInfo:
		CtxInfof(ctx, format, args...)
		test.Assert(t, buf.String() == want)
	case LevelNotice:
		CtxNoticef(ctx, format, args...)
		test.Assert(t, buf.String() == want)
	case LevelWarn:
		CtxWarnf(ctx, format, args...)
		test.Assert(t, buf.String() == want)
	case LevelError:
		CtxErrorf(ctx, format, args...)
		test.Assert(t, buf.String() == want)
	case LevelFatal:
		t.Fatal("fatal method cannot be tested")
	default:
		t.Errorf("unknown level: %d", testLevel)
	}
}

// test package level functions with 'format'
func formatOutput(t *testing.T, testLevel Level, want, format string, args ...interface{}) {
	buf := new(bytes.Buffer)
	SetOutput(buf)
	defer SetOutput(os.Stderr)
	switch testLevel {
	case LevelTrace:
		Tracef(format, args...)
		test.Assert(t, buf.String() == want)
	case LevelDebug:
		Debugf(format, args...)
		test.Assert(t, buf.String() == want)
	case LevelInfo:
		Infof(format, args...)
		test.Assert(t, buf.String() == want)
	case LevelNotice:
		Noticef(format, args...)
		test.Assert(t, buf.String() == want)
	case LevelWarn:
		Warnf(format, args...)
		test.Assert(t, buf.String() == want)
	case LevelError:
		Errorf(format, args...)
		test.Assert(t, buf.String() == want)
	case LevelFatal:
		t.Fatal("fatal method cannot be tested")
	default:
		t.Errorf("unknown level: %d", testLevel)
	}
}

func TestOutput(t *testing.T) {
	l := DefaultLogger().(*defaultLogger)
	oldFlags := l.stdlog.Flags()
	l.stdlog.SetFlags(0)
	defer l.stdlog.SetFlags(oldFlags)
	defer SetLevel(LevelInfo)

	tests := []struct {
		format      string
		args        []interface{}
		testLevel   Level
		loggerLevel Level
		want        string
	}{
		{"%s", []interface{}{"LevelNotice test"}, LevelNotice, LevelInfo, strs[LevelNotice] + "LevelNotice test\n"},
		{"%s %s", []interface{}{"LevelInfo", "test"}, LevelInfo, LevelWarn, ""},
		{"%s%s", []interface{}{"LevelDebug", "Test"}, LevelDebug, LevelDebug, strs[LevelDebug] + "LevelDebugTest\n"},
		{"%s", []interface{}{"LevelTrace test"}, LevelTrace, LevelTrace, strs[LevelTrace] + "LevelTrace test\n"},
		{"%s", []interface{}{"LevelError test"}, LevelError, LevelInfo, strs[LevelError] + "LevelError test\n"},
		{"%s", []interface{}{"LevelWarn test"}, LevelWarn, LevelWarn, strs[LevelWarn] + "LevelWarn test\n"},
	}

	for _, tt := range tests {
		SetLevel(tt.loggerLevel)
		normalOutput(t, tt.testLevel, tt.want, tt.args...)
		formatOutput(t, tt.testLevel, tt.want, tt.format, tt.args...)
		ctxOutput(t, tt.testLevel, tt.want, tt.format, tt.args...)
	}
}
