package logger

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

	"go.uber.org/zap"
)

// TestLogRotation 测试日志轮转功能（T027）
func TestLogRotation(t *testing.T) {
	// 创建临时目录
	tempDir := t.TempDir()

	appLogFile := filepath.Join(tempDir, "app-rotation.log")

	// 初始化日志系统，设置较小的 MaxSize 以便测试
	err := InitLoggers("info", false,
		LogRotationConfig{
			Filename:   appLogFile,
			MaxSize:    1, // 1MB，写入足够数据后会触发轮转
			MaxBackups: 3,
			MaxAge:     7,
			Compress:   false, // 不压缩以便检查
		},
		LogRotationConfig{
			Filename:   filepath.Join(tempDir, "access-rotation.log"),
			MaxSize:    1,
			MaxBackups: 3,
			MaxAge:     7,
			Compress:   false,
		},
	)
	if err != nil {
		t.Fatalf("InitLoggers failed: %v", err)
	}

	logger := GetAppLogger()

	// 写入大量日志数据以触发轮转（每条约100字节，写入15000条约1.5MB）
	largeMessage := strings.Repeat("a", 100)
	for i := 0; i < 15000; i++ {
		logger.Info(largeMessage,
			zap.Int("iteration", i),
			zap.String("data", largeMessage),
		)
	}

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

	// 等待一小段时间确保文件写入完成
	time.Sleep(100 * time.Millisecond)

	// 验证主日志文件存在
	if _, err := os.Stat(appLogFile); os.IsNotExist(err) {
		t.Error("Main log file should exist")
	}

	// 检查是否有备份文件（轮转后的文件）
	files, err := filepath.Glob(filepath.Join(tempDir, "app-rotation-*.log"))
	if err != nil {
		t.Fatalf("Failed to glob backup files: %v", err)
	}

	// 由于写入了超过1MB的数据，应该触发至少一次轮转
	if len(files) == 0 {
		// 可能系统写入速度或lumberjack行为导致未立即轮转，检查主文件大小
		info, err := os.Stat(appLogFile)
		if err != nil {
			t.Fatalf("Failed to stat main log file: %v", err)
		}
		if info.Size() == 0 {
			t.Error("Log file should have content")
		}
		// 不强制要求必须轮转，因为取决于具体实现
		t.Logf("No rotation occurred, but main log file size: %d bytes", info.Size())
	} else {
		t.Logf("Found %d rotated backup file(s)", len(files))
	}
}

// TestMaxBackups 测试最大备份数限制（T027）
func TestMaxBackups(t *testing.T) {
	// 创建临时目录
	tempDir := t.TempDir()

	appLogFile := filepath.Join(tempDir, "app-backups.log")

	// 初始化日志系统，设置 MaxBackups=2
	err := InitLoggers("info", false,
		LogRotationConfig{
			Filename:   appLogFile,
			MaxSize:    1, // 1MB
			MaxBackups: 2, // 最多保留2个备份
			MaxAge:     7,
			Compress:   false,
		},
		LogRotationConfig{
			Filename:   filepath.Join(tempDir, "access-backups.log"),
			MaxSize:    1,
			MaxBackups: 2,
			MaxAge:     7,
			Compress:   false,
		},
	)
	if err != nil {
		t.Fatalf("InitLoggers failed: %v", err)
	}

	logger := GetAppLogger()

	// 写入足够的数据触发多次轮转（每次1.5MB，共4.5MB应该触发3次轮转）
	largeMessage := strings.Repeat("b", 100)
	for round := 0; round < 3; round++ {
		for i := 0; i < 15000; i++ {
			logger.Info(largeMessage,
				zap.Int("round", round),
				zap.Int("iteration", i),
			)
		}
		_ = Sync()
		time.Sleep(100 * time.Millisecond)
	}

	// 等待轮转完成
	time.Sleep(200 * time.Millisecond)

	// 检查备份文件数量
	files, err := filepath.Glob(filepath.Join(tempDir, "app-backups-*.log"))
	if err != nil {
		t.Fatalf("Failed to glob backup files: %v", err)
	}

	// 由于 MaxBackups=2，即使触发了多次轮转，也只应保留最多2个备份文件
	// （实际行为取决于 lumberjack 的实现细节，可能小于等于2）
	if len(files) > 2 {
		t.Errorf("Expected at most 2 backup files due to MaxBackups=2, got %d", len(files))
	}
	t.Logf("Found %d backup file(s) with MaxBackups=2", len(files))
}

// TestCompressionConfig 测试压缩配置（T027）
func TestCompressionConfig(t *testing.T) {
	// 创建临时目录
	tempDir := t.TempDir()

	tests := []struct {
		name     string
		compress bool
	}{
		{
			name:     "compression enabled",
			compress: true,
		},
		{
			name:     "compression disabled",
			compress: false,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			logFile := filepath.Join(tempDir, "app-"+tt.name+".log")

			err := InitLoggers("info", false,
				LogRotationConfig{
					Filename:   logFile,
					MaxSize:    1,
					MaxBackups: 3,
					MaxAge:     7,
					Compress:   tt.compress,
				},
				LogRotationConfig{
					Filename:   filepath.Join(tempDir, "access-"+tt.name+".log"),
					MaxSize:    1,
					MaxBackups: 3,
					MaxAge:     7,
					Compress:   tt.compress,
				},
			)
			if err != nil {
				t.Fatalf("InitLoggers failed: %v", err)
			}

			logger := GetAppLogger()

			// 写入一些日志
			for i := 0; i < 1000; i++ {
				logger.Info("test compression",
					zap.Int("id", i),
					zap.String("data", strings.Repeat("c", 50)),
				)
			}

			_ = Sync()
			time.Sleep(100 * time.Millisecond)

			// 验证日志文件存在
			if _, err := os.Stat(logFile); os.IsNotExist(err) {
				t.Error("Log file should exist")
			}
		})
	}
}

// TestMaxAge 测试日志文件保留时间（T027）
func TestMaxAge(t *testing.T) {
	// 创建临时目录
	tempDir := t.TempDir()

	// 初始化日志系统，设置 MaxAge=1 天
	err := InitLoggers("info", false,
		LogRotationConfig{
			Filename:   filepath.Join(tempDir, "app-maxage.log"),
			MaxSize:    10,
			MaxBackups: 3,
			MaxAge:     1, // 1天
			Compress:   false,
		},
		LogRotationConfig{
			Filename:   filepath.Join(tempDir, "access-maxage.log"),
			MaxSize:    10,
			MaxBackups: 3,
			MaxAge:     1,
			Compress:   false,
		},
	)
	if err != nil {
		t.Fatalf("InitLoggers failed: %v", err)
	}

	logger := GetAppLogger()

	// 写入日志
	logger.Info("test max age", zap.String("config", "maxage=1"))
	_ = Sync()

	// 验证配置已应用（无法在单元测试中验证实际的清理行为，因为需要等待1天）
	// 这里只验证初始化没有错误
	if logger == nil {
		t.Error("Logger should be initialized with MaxAge config")
	}
}

// TestNewLumberjackLogger 测试 Lumberjack logger 创建（T027）
func TestNewLumberjackLogger(t *testing.T) {
	// 创建临时目录
	tempDir := t.TempDir()

	tests := []struct {
		name   string
		config LogRotationConfig
	}{
		{
			name: "standard config",
			config: LogRotationConfig{
				Filename:   filepath.Join(tempDir, "test1.log"),
				MaxSize:    10,
				MaxBackups: 3,
				MaxAge:     7,
				Compress:   true,
			},
		},
		{
			name: "minimal config",
			config: LogRotationConfig{
				Filename:   filepath.Join(tempDir, "test2.log"),
				MaxSize:    1,
				MaxBackups: 1,
				MaxAge:     1,
				Compress:   false,
			},
		},
		{
			name: "large config",
			config: LogRotationConfig{
				Filename:   filepath.Join(tempDir, "test3.log"),
				MaxSize:    100,
				MaxBackups: 10,
				MaxAge:     30,
				Compress:   true,
			},
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			logger := newLumberjackLogger(tt.config)
			if logger == nil {
				t.Error("newLumberjackLogger should not return nil")
			}

			// 验证配置已正确设置
			if logger.Filename != tt.config.Filename {
				t.Errorf("Filename = %v, want %v", logger.Filename, tt.config.Filename)
			}
			if logger.MaxSize != tt.config.MaxSize {
				t.Errorf("MaxSize = %v, want %v", logger.MaxSize, tt.config.MaxSize)
			}
			if logger.MaxBackups != tt.config.MaxBackups {
				t.Errorf("MaxBackups = %v, want %v", logger.MaxBackups, tt.config.MaxBackups)
			}
			if logger.MaxAge != tt.config.MaxAge {
				t.Errorf("MaxAge = %v, want %v", logger.MaxAge, tt.config.MaxAge)
			}
			if logger.Compress != tt.config.Compress {
				t.Errorf("Compress = %v, want %v", logger.Compress, tt.config.Compress)
			}
			if !logger.LocalTime {
				t.Error("LocalTime should be true")
			}
		})
	}
}

// TestConcurrentLogging 测试并发日志写入（T027）
func TestConcurrentLogging(t *testing.T) {
	// 创建临时目录
	tempDir := t.TempDir()

	// 初始化日志系统
	err := InitLoggers("info", false,
		LogRotationConfig{
			Filename:   filepath.Join(tempDir, "app-concurrent.log"),
			MaxSize:    10,
			MaxBackups: 3,
			MaxAge:     7,
			Compress:   false,
		},
		LogRotationConfig{
			Filename:   filepath.Join(tempDir, "access-concurrent.log"),
			MaxSize:    10,
			MaxBackups: 3,
			MaxAge:     7,
			Compress:   false,
		},
	)
	if err != nil {
		t.Fatalf("InitLoggers failed: %v", err)
	}

	logger := GetAppLogger()

	// 启动多个 goroutine 并发写入日志
	done := make(chan bool)
	goroutines := 10
	messagesPerGoroutine := 100

	for i := 0; i < goroutines; i++ {
		go func(id int) {
			for j := 0; j < messagesPerGoroutine; j++ {
				logger.Info("concurrent log message",
					zap.Int("goroutine", id),
					zap.Int("message", j),
				)
			}
			done <- true
		}(i)
	}

	// 等待所有 goroutine 完成
	for i := 0; i < goroutines; i++ {
		<-done
	}

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

	// 验证日志文件存在且有内容
	logFile := filepath.Join(tempDir, "app-concurrent.log")
	info, err := os.Stat(logFile)
	if err != nil {
		t.Fatalf("Failed to stat log file: %v", err)
	}
	if info.Size() == 0 {
		t.Error("Log file should have content after concurrent writes")
	}

	t.Logf("Concurrent logging test completed, log file size: %d bytes", info.Size())
}
