// Package logs 提供了高度可配置的日志功能，基于 uber-go/zap 实现。
// 它支持将日志同时输出到控制台和文件，并提供日志滚动归档功能。
// 该包还特别为 Gin 和 GORM 框架提供了开箱即用的日志接管能力。
package logs

import (
	"context"
	"fmt"
	"io"
	"os"
	"strings"
	"sync"
	"time"

	"git.huangwc.com/pig/pig-farm-controller/internal/infra/config"
	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
	"gopkg.in/natefinch/lumberjack.v2"
	gormlogger "gorm.io/gorm/logger"
)

// ANSI 颜色代码常量
const (
	reset   = "\033[0m"
	red     = "\033[31m"
	green   = "\033[32m"
	yellow  = "\033[33m"
	blue    = "\033[34m"
	magenta = "\033[35m"
	cyan    = "\033[36m"
	white   = "\033[37m"
	bold    = "\033[1m"
)

var (
	defaultLogger *Logger
	logConfig     config.LogConfig
	once          sync.Once
)

// Logger 是一个封装了 zap.SugaredLogger 的日志记录器。
// 它提供了结构化日志记录的各种方法，并实现了 io.Writer 接口以兼容 Gin。
type Logger struct {
	sl         *zap.SugaredLogger
	traceValue string
}

// NewLogger 根据提供的配置创建一个新的 Logger 实例。
// 这是实现依赖注入的关键，在应用启动时调用一次。
func NewLogger(cfg config.LogConfig) *Logger {
	// 1. 设置日志编码器
	encoder := GetEncoder(cfg.Format)

	// 2. 设置日志写入器 (支持文件和控制台)
	writeSyncer := getWriteSyncer(cfg)

	// 3. 设置日志级别
	level := zap.NewAtomicLevel()
	if err := level.UnmarshalText([]byte(cfg.Level)); err != nil {
		level.SetLevel(zap.InfoLevel) // 解析失败则默认为 Info 级别
	}

	// 4. 创建 Zap 核心
	core := zapcore.NewCore(encoder, writeSyncer, level)

	// 5. 构建 Logger
	// zap.AddCaller() 会记录调用日志的代码行
	// zap.AddCallerSkip(1) 可以向上跳一层调用栈，如果我们将 logger.Info 等方法再封装一层，这个选项会很有用
	zapLogger := zap.New(core, zap.AddCaller(), zap.AddCallerSkip(1))

	return &Logger{sl: zapLogger.Sugar()}
}

// InitDefaultLogger 初始化包级单例的 defaultLogger。
// 此函数应在应用程序启动时调用一次。
func InitDefaultLogger(cfg config.LogConfig) {
	once.Do(func() {
		logConfig = cfg
		defaultLogger = NewLogger(cfg)
	})
}

// GetLogger 从 Context 中提取调用链信息，并返回一个携带这些信息的 Logger 副本。
// 如果 Context 中没有调用链信息，或配置禁用了调用链，则直接返回默认的 Logger。
func GetLogger(ctx context.Context) *Logger {
	if defaultLogger == nil {
		// 在调用 InitDefaultLogger 之前，提供一个备用的、仅输出到控制台的 Logger
		fallbackCfg := config.LogConfig{Level: "info", Format: "console", EnableTrace: true}
		return NewLogger(fallbackCfg)
	}

	// 如果禁用了调用链追踪，则直接返回默认 logger
	if !logConfig.EnableTrace {
		return defaultLogger
	}

	chain := GetTraceStr(ctx)
	if chain == "" {
		return defaultLogger
	}

	// 返回一个新的 Logger 实例，它携带了调用链信息
	return &Logger{sl: defaultLogger.sl, traceValue: chain}
}

// Trace 是构建和记录调用链的核心函数。
// 它首先使用 AddFuncName 创建一个包含新调用节点的新 Context，
// 然后返回这个新 Context 和一个包含完整调用链的 Logger 实例。
func Trace(upstreamCtx context.Context, selfCtx context.Context, funcName string) (context.Context, *Logger) {
	newCtx := AddFuncName(upstreamCtx, selfCtx, funcName)
	logger := GetLogger(newCtx)
	return newCtx, logger
}

// TraceLogger 类似于 Trace，但只返回一个包含完整调用链的 Logger 实例，
// 而不返回更新后的 Context。适用于调用链的末端或不需要向下传递 Context 的场景。
func TraceLogger(upstreamCtx context.Context, selfCtx context.Context, funcName string) *Logger {
	newCtx := AddFuncName(upstreamCtx, selfCtx, funcName)
	logger := GetLogger(newCtx)
	return logger
}

// GetEncoder 根据指定的格式返回一个 zapcore.Encoder。
func GetEncoder(format string) zapcore.Encoder {
	encoderConfig := zap.NewProductionEncoderConfig()
	encoderConfig.EncodeTime = zapcore.RFC3339TimeEncoder   // 时间格式: 2006-01-02T15:04:05Z07:00
	encoderConfig.EncodeLevel = zapcore.CapitalLevelEncoder // 日志级别大写: INFO (由 coloredConsoleEncoder 处理颜色)

	if format == "json" {
		return zapcore.NewJSONEncoder(encoderConfig)
	}
	// 默认或 "console" 格式，使用自定义的带颜色编码器
	return NewColoredConsoleEncoder(encoderConfig)
}

// getWriteSyncer 根据配置创建日志写入目标。
func getWriteSyncer(cfg config.LogConfig) zapcore.WriteSyncer {
	writers := []zapcore.WriteSyncer{os.Stdout}

	if cfg.EnableFile {
		// 使用 lumberjack 实现日志滚动
		fileWriter := &lumberjack.Logger{
			Filename:   cfg.FilePath,
			MaxSize:    cfg.MaxSize,
			MaxBackups: cfg.MaxBackups,
			MaxAge:     cfg.MaxAge,
			Compress:   cfg.Compress,
		}
		writers = append(writers, zapcore.AddSync(fileWriter))
	}

	return zapcore.NewMultiWriteSyncer(writers...)
}

// Write 实现了 io.Writer 接口，用于接管 Gin 的默认输出。
// Gin 的日志（如 [GIN-debug] Listening and serving HTTP on :8080）会通过这个方法写入。
func (l *Logger) Write(p []byte) (n int, err error) {
	msg := strings.TrimSpace(string(p))
	if msg != "" {
		l.Info(msg) // 使用我们自己的 logger 来打印 Gin 的日志
	}
	return len(p), nil
}

// --- GORM 日志适配器 ---

// GormLogger 是一个实现了 gormlogger.Interface 的适配器，
// 它将 GORM 的日志重定向到我们的 zap Logger 中。
type GormLogger struct {
	ZapLogger             *Logger
	SlowThreshold         time.Duration
	SkipErrRecordNotFound bool // 是否跳过 "record not found" 错误
}

// NewGormLogger 创建一个新的 GORM 日志记录器实例。
func NewGormLogger(zapLogger *Logger) *GormLogger {
	return &GormLogger{
		ZapLogger:             zapLogger,
		SlowThreshold:         200 * time.Millisecond, // 慢查询阈值，超过200ms则警告
		SkipErrRecordNotFound: true,                   // 通常我们不关心 "record not found" 错误
	}
}

// LogMode 设置日志模式，这里我们总是使用 zap 的级别控制，所以这个方法可以为空。
func (g *GormLogger) LogMode(level gormlogger.LogLevel) gormlogger.Interface {
	// GORM 的 LogLevel 在这里不起作用，因为我们完全由 Zap 控制
	return g
}

// Info 打印 Info 级别的日志。
func (g *GormLogger) Info(ctx context.Context, msg string, data ...interface{}) {
	GetLogger(ctx).Infof(msg, data...)
}

// Warn 打印 Warn 级别的日志。
func (g *GormLogger) Warn(ctx context.Context, msg string, data ...interface{}) {
	GetLogger(ctx).Warnf(msg, data...)
}

// Error 打印 Error 级别的日志。
func (g *GormLogger) Error(ctx context.Context, msg string, data ...interface{}) {
	GetLogger(ctx).Errorf(msg, data...)
}

// Trace 打印 SQL 查询日志，这是 GORM 日志的核心。
func (g *GormLogger) Trace(ctx context.Context, begin time.Time, fc func() (sql string, rowsAffected int64), err error) {
	elapsed := time.Since(begin)
	sql, rows := fc()

	fields := []interface{}{
		"sql", sql,
		"rows", rows,
		"elapsed", fmt.Sprintf("%.3fms", float32(elapsed.Nanoseconds())/1e6),
	}

	if err != nil {
		// 如果是 "record not found" 错误且我们配置了跳过，则直接返回
		if g.SkipErrRecordNotFound && strings.Contains(err.Error(), "record not found") {
			return
		}
		// 否则，记录为错误日志
		GetLogger(ctx).Errorw(fmt.Sprintf("[GORM] error: %s", err), fields...)
		return
	}

	// 如果查询时间超过慢查询阈值，则记录警告
	if g.SlowThreshold != 0 && elapsed > g.SlowThreshold {
		GetLogger(ctx).Warnw("[GORM] slow query", fields...)
		return
	}

	// 正常情况，记录 Debug 级别的 SQL 查询
	GetLogger(ctx).Debugw("[GORM] trace", fields...)
}

// NewSilentLogger 创建一个不输出任何日志的 Logger 实例, 用于测试中屏蔽日志
func NewSilentLogger() *Logger {
	// 创建一个不输出日志的真实 logs.Logger 实例
	discardSyncer := zapcore.AddSync(io.Discard)
	encoderConfig := zap.NewProductionEncoderConfig()
	encoder := zapcore.NewConsoleEncoder(encoderConfig)
	core := zapcore.NewCore(encoder, discardSyncer, zap.DebugLevel) // 设置为 DebugLevel 以确保所有日志都被处理（并丢弃）
	zapLogger := zap.New(core)
	sugaredLogger := zapLogger.Sugar()
	return &Logger{sl: sugaredLogger}
}
