package logger

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

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

// stdoutWriter 包装 os.Stdout，忽略 Sync() 错误。
// 在非 TTY 环境（如 go test）中，os.Stdout 被重定向为 pipe，
// 底层 fsync 会返回 "bad file descriptor"。zap 社区标准做法。
type stdoutWriter struct{}

func (stdoutWriter) Write(p []byte) (int, error) { return os.Stdout.Write(p) }
func (stdoutWriter) Sync() error                 { return nil }

// Config 日志配置
type Config struct {
	Level      string // 日志级别: debug, info, warn, error
	Path       string // 日志文件目录，为空则仅输出到 stdout
	MaxSize    int    // 单个日志文件最大尺寸 (MB)
	MaxBackups int    // 保留的旧日志文件最大数量
	MaxAge     int    // 保留旧日志文件的最大天数
	Compress   bool   // 是否压缩旧日志文件
}

// New 根据配置创建 zap.Logger
// 如果 Path 为空，仅输出到 stdout；
// 如果 Path 已设置，同时输出到 stdout 和文件（文件使用 JSON 格式，stdout 使用 console 格式）
func New(cfg Config) (*zap.Logger, error) {
	level, err := parseLevel(cfg.Level)
	if err != nil {
		return nil, err
	}

	// stdout encoder — console 格式
	stdoutEncoder := zapcore.NewConsoleEncoder(zapcore.EncoderConfig{
		TimeKey:        "ts",
		LevelKey:       "level",
		NameKey:        "logger",
		CallerKey:      "caller",
		FunctionKey:    zapcore.OmitKey,
		MessageKey:     "msg",
		StacktraceKey:  "stacktrace",
		LineEnding:     zapcore.DefaultLineEnding,
		EncodeLevel:    zapcore.CapitalColorLevelEncoder,
		EncodeTime:     zapcore.ISO8601TimeEncoder,
		EncodeDuration: zapcore.StringDurationEncoder,
		EncodeCaller:   zapcore.ShortCallerEncoder,
		EncodeName:     encodeLoggerName,
	})

	stdoutCore := zapcore.NewCore(
		stdoutEncoder,
		zapcore.AddSync(stdoutWriter{}),
		level,
	)

	// 仅 stdout 模式
	if cfg.Path == "" {
		return zap.New(stdoutCore, zap.AddCaller(), zap.AddStacktrace(zap.ErrorLevel)), nil
	}

	// 文件 encoder — JSON 格式
	fileEncoder := zapcore.NewJSONEncoder(zapcore.EncoderConfig{
		TimeKey:        "ts",
		LevelKey:       "level",
		NameKey:        "logger",
		CallerKey:      "caller",
		FunctionKey:    zapcore.OmitKey,
		MessageKey:     "msg",
		StacktraceKey:  "stacktrace",
		LineEnding:     zapcore.DefaultLineEnding,
		EncodeLevel:    zapcore.LowercaseLevelEncoder,
		EncodeTime:     zapcore.ISO8601TimeEncoder,
		EncodeDuration: zapcore.StringDurationEncoder,
		EncodeCaller:   zapcore.ShortCallerEncoder,
	})

	rotateWriter := newRotateWriter(cfg)
	fileCore := zapcore.NewCore(
		fileEncoder,
		zapcore.AddSync(rotateWriter),
		level,
	)

	core := zapcore.NewTee(stdoutCore, fileCore)
	return zap.New(core, zap.AddCaller(), zap.AddStacktrace(zap.ErrorLevel)), nil
}

// parseLevel 将字符串解析为 zapcore.Level
func parseLevel(s string) (zapcore.Level, error) {
	switch s {
	case "debug":
		return zapcore.DebugLevel, nil
	case "info":
		return zapcore.InfoLevel, nil
	case "warn":
		return zapcore.WarnLevel, nil
	case "error":
		return zapcore.ErrorLevel, nil
	default:
		return zapcore.InfoLevel, nil
	}
}

// logFileName 生成当日日志文件名: nex-YYYY-MM-DD.log
func logFileName() string {
	return "nex-" + time.Now().Format("2006-01-02") + ".log"
}

// logFilePath 拼接完整日志文件路径
func logFilePath(dir string) string {
	return filepath.Join(dir, logFileName())
}

// encodeLoggerName 自定义 logger 名称编码器，输出 [name] 格式
func encodeLoggerName(name string, enc zapcore.PrimitiveArrayEncoder) {
	if name != "" {
		enc.AppendString("[" + name + "]")
	}
}
