package logger

import (
	"bytes"
	"fmt"
	"os"
	"path/filepath"
	"reflect"
	"testing"
	"time"

	"github.com/spf13/viper"
)

func TestConfigToYAML(t *testing.T) {
	cfg := Config{
		EnableLog:        true,
		LogDir:           "/var/log/auditlog",
		AuditLogFileName: "ops-audit.log",
		MaxSize:          10,
		MaxLifetime:      86400,
		MaxBackups:       10,
		MaxAge:           7,
		ManualRotateTime: time.Now().Format(time.RFC3339),
	}

	data, err := cfg.ToYAML()
	if err != nil {
		t.Fatalf("Failed to convert config to YAML: %v", err)
	}
	t.Log(string(data))

	vp := viper.New()
	vp.SetConfigType("yaml")
	err = vp.ReadConfig(bytes.NewReader(data))
	if err != nil {
		t.Fatalf("Failed to read config: %v", err)
	}

	cfgBack := new(Config)
	err = vp.Unmarshal(cfgBack)
	if err != nil {
		t.Fatalf("Failed to unmarshal config: %v", err)
	}

	data2, err := cfgBack.ToYAML()
	if err != nil {
		t.Fatalf("Failed to convert config back to YAML: %v", err)
	}

	if !reflect.DeepEqual(data, data2) {
		t.Errorf("YAML mismatch:\nExpected: %s\nGot: %s", data, data2)
	}
}

func TestLogger(t *testing.T) {
	dir := t.TempDir()
	cfgFile, err := os.OpenFile(dir+"/config.yaml", os.O_CREATE|os.O_RDWR, 0o644)
	if err != nil {
		t.Fatalf("Failed to create config file: %v", err)
	}
	defer cfgFile.Close()

	// Write a config to the file
	_, err = fmt.Fprintf(cfgFile, `
enableLog: true
logDir: %s
auditLogFileName: ops-audit.log
maxSize: 10
maxLifetime: 86400
maxBackups: 10
maxAge: 7
manualRotateTime: "`+time.Now().Format(time.RFC3339)+`"
`, dir)
	if err != nil {
		t.Fatalf("Failed to write config file: %v", err)
	}

	// Reload the config
	viper.SetConfigFile(cfgFile.Name())
	if err := viper.ReadInConfig(); err != nil {
		t.Fatalf("Failed to read config file: %v", err)
	}

	// 1. write a log entry
	trl := NewTimeRotatingLogger()
	_, _ = trl.Write([]byte("This is a test log entry."))

	// 2. read it back, and check
	logData, err := os.ReadFile(filepath.Join(dir, "ops-audit.log"))
	if err != nil {
		t.Fatalf("Failed to read log file: %v", err)
	}
	if logData == nil || !bytes.Contains(logData, []byte("This is a test log entry.")) {
		t.Errorf("Log file does not contain expected log entry")
	}

	// 3. disable to trigger rotation
	viper.Set("enableLog", false)
	trl.loadConfig()

	// 4. read again, the new file is empty
	logData, err = os.ReadFile(filepath.Join(dir, "ops-audit.log"))
	if err != nil {
		t.Fatalf("Failed to read log file: %v", err)
	}
	if len(logData) != 0 {
		t.Errorf("Log file should be empty after disabling logging, got: %s", string(logData))
	}

	// 5. enable logging again
	viper.Set("enableLog", true)
	trl.loadConfig()

	// 6. write another log entry
	_, _ = trl.Write([]byte("This is another test log entry."))

	// 7. read it back, and check
	logData, err = os.ReadFile(filepath.Join(dir, "ops-audit.log"))
	if err != nil {
		t.Fatalf("Failed to read log file: %v", err)
	}
	if logData == nil || !bytes.Contains(logData, []byte("This is another test log entry.")) {
		t.Errorf("Log file does not contain expected log entry")
	}

	// 8. use `manualRotateTime` to trigger a manual rotation
	time.Sleep(2 * time.Second) // ensure the time is different
	viper.Set("manualRotateTime", time.Now().Format(time.RFC3339))
	trl.loadConfig()
}
