package logger

import (
	"os"
	"path/filepath"
	"testing"

	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
)

// TestInitLoggers 测试日志初始化（T026）
func TestInitLoggers(t *testing.T) {
	// 创建临时目录用于日志文件
	tempDir := t.TempDir()

	tests := []struct {
		name            string
		level           string
		development     bool
		appLogConfig    LogRotationConfig
		accessLogConfig LogRotationConfig
		wantErr         bool
		validateFunc    func(t *testing.T)
	}{
		{
			name:        "production mode with info level",
			level:       "info",
			development: false,
			appLogConfig: LogRotationConfig{
				Filename:   filepath.Join(tempDir, "app-prod.log"),
				MaxSize:    10,
				MaxBackups: 3,
				MaxAge:     7,
				Compress:   true,
			},
			accessLogConfig: LogRotationConfig{
				Filename:   filepath.Join(tempDir, "access-prod.log"),
				MaxSize:    10,
				MaxBackups: 3,
				MaxAge:     7,
				Compress:   true,
			},
			wantErr: false,
			validateFunc: func(t *testing.T) {
				if appLogger == nil {
					t.Error("appLogger should not be nil")
				}
				if accessLogger == nil {
					t.Error("accessLogger should not be nil")
				}
				// 写入一条日志以触发文件创建
				GetAppLogger().Info("test log creation")
				_ = Sync()
				// 验证日志文件创建
				if _, err := os.Stat(filepath.Join(tempDir, "app-prod.log")); os.IsNotExist(err) {
					t.Error("app log file should be created after writing")
				}
			},
		},
		{
			name:        "development mode with debug level",
			level:       "debug",
			development: true,
			appLogConfig: LogRotationConfig{
				Filename:   filepath.Join(tempDir, "app-dev.log"),
				MaxSize:    5,
				MaxBackups: 2,
				MaxAge:     3,
				Compress:   false,
			},
			accessLogConfig: LogRotationConfig{
				Filename:   filepath.Join(tempDir, "access-dev.log"),
				MaxSize:    5,
				MaxBackups: 2,
				MaxAge:     3,
				Compress:   false,
			},
			wantErr: false,
			validateFunc: func(t *testing.T) {
				if appLogger == nil {
					t.Error("appLogger should not be nil in dev mode")
				}
				if accessLogger == nil {
					t.Error("accessLogger should not be nil in dev mode")
				}
			},
		},
		{
			name:        "warn level logging",
			level:       "warn",
			development: false,
			appLogConfig: LogRotationConfig{
				Filename:   filepath.Join(tempDir, "app-warn.log"),
				MaxSize:    10,
				MaxBackups: 3,
				MaxAge:     7,
				Compress:   true,
			},
			accessLogConfig: LogRotationConfig{
				Filename:   filepath.Join(tempDir, "access-warn.log"),
				MaxSize:    10,
				MaxBackups: 3,
				MaxAge:     7,
				Compress:   true,
			},
			wantErr: false,
			validateFunc: func(t *testing.T) {
				if appLogger == nil {
					t.Error("appLogger should not be nil")
				}
			},
		},
		{
			name:        "error level logging",
			level:       "error",
			development: false,
			appLogConfig: LogRotationConfig{
				Filename:   filepath.Join(tempDir, "app-error.log"),
				MaxSize:    10,
				MaxBackups: 3,
				MaxAge:     7,
				Compress:   true,
			},
			accessLogConfig: LogRotationConfig{
				Filename:   filepath.Join(tempDir, "access-error.log"),
				MaxSize:    10,
				MaxBackups: 3,
				MaxAge:     7,
				Compress:   true,
			},
			wantErr: false,
			validateFunc: func(t *testing.T) {
				if appLogger == nil {
					t.Error("appLogger should not be nil")
				}
			},
		},
		{
			name:        "invalid level defaults to info",
			level:       "invalid",
			development: false,
			appLogConfig: LogRotationConfig{
				Filename:   filepath.Join(tempDir, "app-invalid.log"),
				MaxSize:    10,
				MaxBackups: 3,
				MaxAge:     7,
				Compress:   true,
			},
			accessLogConfig: LogRotationConfig{
				Filename:   filepath.Join(tempDir, "access-invalid.log"),
				MaxSize:    10,
				MaxBackups: 3,
				MaxAge:     7,
				Compress:   true,
			},
			wantErr: false,
			validateFunc: func(t *testing.T) {
				if appLogger == nil {
					t.Error("appLogger should not be nil even with invalid level")
				}
			},
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			err := InitLoggers(tt.level, tt.development, tt.appLogConfig, tt.accessLogConfig)
			if (err != nil) != tt.wantErr {
				t.Errorf("InitLoggers() error = %v, wantErr %v", err, tt.wantErr)
				return
			}

			if tt.validateFunc != nil {
				tt.validateFunc(t)
			}
		})
	}
}

// TestGetAppLogger 测试获取应用日志记录器（T026）
func TestGetAppLogger(t *testing.T) {
	// 创建临时目录
	tempDir := t.TempDir()

	tests := []struct {
		name      string
		setupFunc func()
		wantNil   bool
	}{
		{
			name: "after initialization",
			setupFunc: func() {
				_ = InitLoggers("info", false,
					LogRotationConfig{
						Filename:   filepath.Join(tempDir, "app-get.log"),
						MaxSize:    10,
						MaxBackups: 3,
						MaxAge:     7,
						Compress:   true,
					},
					LogRotationConfig{
						Filename:   filepath.Join(tempDir, "access-get.log"),
						MaxSize:    10,
						MaxBackups: 3,
						MaxAge:     7,
						Compress:   true,
					},
				)
			},
			wantNil: false,
		},
		{
			name: "before initialization returns nop logger",
			setupFunc: func() {
				// 重置全局变量
				appLogger = nil
			},
			wantNil: false, // GetAppLogger 应该返回 nop logger，不是 nil
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			tt.setupFunc()
			logger := GetAppLogger()
			if logger == nil {
				t.Error("GetAppLogger() should never return nil, should return nop logger instead")
			}
		})
	}
}

// TestGetAccessLogger 测试获取访问日志记录器（T028）
func TestGetAccessLogger(t *testing.T) {
	// 创建临时目录
	tempDir := t.TempDir()

	tests := []struct {
		name      string
		setupFunc func()
		wantNil   bool
	}{
		{
			name: "after initialization",
			setupFunc: func() {
				_ = InitLoggers("info", false,
					LogRotationConfig{
						Filename:   filepath.Join(tempDir, "app-access.log"),
						MaxSize:    10,
						MaxBackups: 3,
						MaxAge:     7,
						Compress:   true,
					},
					LogRotationConfig{
						Filename:   filepath.Join(tempDir, "access-access.log"),
						MaxSize:    10,
						MaxBackups: 3,
						MaxAge:     7,
						Compress:   true,
					},
				)
			},
			wantNil: false,
		},
		{
			name: "before initialization returns nop logger",
			setupFunc: func() {
				// 重置全局变量
				accessLogger = nil
			},
			wantNil: false, // GetAccessLogger 应该返回 nop logger，不是 nil
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			tt.setupFunc()
			logger := GetAccessLogger()
			if logger == nil {
				t.Error("GetAccessLogger() should never return nil, should return nop logger instead")
			}
		})
	}
}

// TestSync 测试日志缓冲区刷新（T028）
func TestSync(t *testing.T) {
	// 创建临时目录
	tempDir := t.TempDir()

	tests := []struct {
		name      string
		setupFunc func()
		wantErr   bool
	}{
		{
			name: "sync after initialization",
			setupFunc: func() {
				_ = InitLoggers("info", false,
					LogRotationConfig{
						Filename:   filepath.Join(tempDir, "app-sync.log"),
						MaxSize:    10,
						MaxBackups: 3,
						MaxAge:     7,
						Compress:   true,
					},
					LogRotationConfig{
						Filename:   filepath.Join(tempDir, "access-sync.log"),
						MaxSize:    10,
						MaxBackups: 3,
						MaxAge:     7,
						Compress:   true,
					},
				)
			},
			wantErr: false,
		},
		{
			name: "sync before initialization",
			setupFunc: func() {
				appLogger = nil
				accessLogger = nil
			},
			wantErr: false, // 应该优雅地处理 nil 情况
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			tt.setupFunc()
			err := Sync()
			if (err != nil) != tt.wantErr {
				t.Errorf("Sync() error = %v, wantErr %v", err, tt.wantErr)
			}
		})
	}
}

// TestParseLevel 测试日志级别解析（T026）
func TestParseLevel(t *testing.T) {
	tests := []struct {
		name  string
		level string
		want  zapcore.Level
	}{
		{
			name:  "debug level",
			level: "debug",
			want:  zapcore.DebugLevel,
		},
		{
			name:  "info level",
			level: "info",
			want:  zapcore.InfoLevel,
		},
		{
			name:  "warn level",
			level: "warn",
			want:  zapcore.WarnLevel,
		},
		{
			name:  "error level",
			level: "error",
			want:  zapcore.ErrorLevel,
		},
		{
			name:  "invalid level defaults to info",
			level: "invalid",
			want:  zapcore.InfoLevel,
		},
		{
			name:  "empty level defaults to info",
			level: "",
			want:  zapcore.InfoLevel,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			got := parseLevel(tt.level)
			if got != tt.want {
				t.Errorf("parseLevel() = %v, want %v", got, tt.want)
			}
		})
	}
}

// TestDualLoggerSystem 测试双日志系统（T028）
func TestDualLoggerSystem(t *testing.T) {
	// 创建临时目录
	tempDir := t.TempDir()

	appLogFile := filepath.Join(tempDir, "app-dual.log")
	accessLogFile := filepath.Join(tempDir, "access-dual.log")

	// 初始化双日志系统
	err := InitLoggers("info", false,
		LogRotationConfig{
			Filename:   appLogFile,
			MaxSize:    10,
			MaxBackups: 3,
			MaxAge:     7,
			Compress:   false, // 不压缩以便检查内容
		},
		LogRotationConfig{
			Filename:   accessLogFile,
			MaxSize:    10,
			MaxBackups: 3,
			MaxAge:     7,
			Compress:   false,
		},
	)
	if err != nil {
		t.Fatalf("InitLoggers failed: %v", err)
	}

	// 写入应用日志
	appLog := GetAppLogger()
	appLog.Info("test app log message",
		zap.String("module", "test"),
		zap.Int("code", 200),
	)

	// 写入访问日志
	accessLog := GetAccessLogger()
	accessLog.Info("test access log message",
		zap.String("method", "GET"),
		zap.String("path", "/api/test"),
		zap.Int("status", 200),
		zap.Duration("latency", 100),
	)

	// 刷新缓冲区
	if err := Sync(); err != nil {
		t.Fatalf("Sync failed: %v", err)
	}

	// 验证应用日志文件存在并有内容
	appLogContent, err := os.ReadFile(appLogFile)
	if err != nil {
		t.Fatalf("Failed to read app log file: %v", err)
	}
	if len(appLogContent) == 0 {
		t.Error("App log file should not be empty")
	}

	// 验证访问日志文件存在并有内容
	accessLogContent, err := os.ReadFile(accessLogFile)
	if err != nil {
		t.Fatalf("Failed to read access log file: %v", err)
	}
	if len(accessLogContent) == 0 {
		t.Error("Access log file should not be empty")
	}

	// 验证两个日志文件是独立的
	if string(appLogContent) == string(accessLogContent) {
		t.Error("App log and access log should have different content")
	}
}

// TestLoggerReinitialization 测试日志重新初始化（T026）
func TestLoggerReinitialization(t *testing.T) {
	// 创建临时目录
	tempDir := t.TempDir()

	// 第一次初始化
	err := InitLoggers("info", false,
		LogRotationConfig{
			Filename:   filepath.Join(tempDir, "app-reinit1.log"),
			MaxSize:    10,
			MaxBackups: 3,
			MaxAge:     7,
			Compress:   true,
		},
		LogRotationConfig{
			Filename:   filepath.Join(tempDir, "access-reinit1.log"),
			MaxSize:    10,
			MaxBackups: 3,
			MaxAge:     7,
			Compress:   true,
		},
	)
	if err != nil {
		t.Fatalf("First InitLoggers failed: %v", err)
	}

	firstAppLogger := GetAppLogger()

	// 第二次初始化（重新初始化）
	err = InitLoggers("debug", true,
		LogRotationConfig{
			Filename:   filepath.Join(tempDir, "app-reinit2.log"),
			MaxSize:    5,
			MaxBackups: 2,
			MaxAge:     3,
			Compress:   false,
		},
		LogRotationConfig{
			Filename:   filepath.Join(tempDir, "access-reinit2.log"),
			MaxSize:    5,
			MaxBackups: 2,
			MaxAge:     3,
			Compress:   false,
		},
	)
	if err != nil {
		t.Fatalf("Second InitLoggers failed: %v", err)
	}

	secondAppLogger := GetAppLogger()

	// 验证重新初始化后日志记录器已更新
	if firstAppLogger == secondAppLogger {
		t.Error("Logger should be replaced after reinitialization")
	}
}
