package mylog

import (
	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
	"io"
	"os"
	"strings"
)

type ILogger interface {
	Debugf(format string, v ...any)
	Infof(format string, v ...any)
	Warnf(format string, v ...any)
	Errorf(format string, v ...any)
}

const (
	DebugLevel = "DEBUG"
	InfoLevel  = "INFO"
	WarnLevel  = "WARN"
	ErrorLevel = "ERROR"
	FatalLevel = "FATAL"
)

var (
	levelMap = map[string]zapcore.Level{
		"DEBUG": zapcore.DebugLevel,
		"INFO":  zapcore.InfoLevel,
		"WARN":  zapcore.WarnLevel,
		"ERROR": zapcore.ErrorLevel,
		"FATAL": zapcore.FatalLevel,
	}

	defaultLogLevel = DebugLevel

	// DefaultConfig 默认配置
	DefaultConfig = &Config{
		Level:         DebugLevel,
		NeedLogFile:   false,
		ConsoleWriter: os.Stdout,
	}

	globalLog = NewLogger(DefaultConfig)
)

type (
	ZapLog struct {
		sugarLog *zap.SugaredLogger
	}

	Config struct {
		Level         string
		NeedLogFile   bool
		ConsoleWriter io.Writer
		ZapOpt        []zap.Option
		LogFile       *LogFile
	}

	LogFile struct {
		LogFilePath string
		MaxSize     int
		MaxAge      int
		MaxBackups  int
	}
)

func SetLogLevel(level string) {
	defaultLogLevel = level
}

func Init() {
	globalLog = NewLogger(&Config{
		Level:         defaultLogLevel,
		NeedLogFile:   false,
		ConsoleWriter: os.Stdout,
	})
}

// InitWithConfig 覆盖默认日志
func InitWithConfig(config *Config) {
	globalLog = NewLogger(config)
}

func NewLogger(config *Config) *ZapLog {
	if config == nil {
		config = DefaultConfig
	}
	var level zapcore.Level
	if v, ok := levelMap[strings.ToUpper(config.Level)]; ok {
		level = v
	} else {
		level = zapcore.DebugLevel
	}

	cores := make([]zapcore.Core, 0)
	// 使用控制台输出
	cfg := zap.NewProductionEncoderConfig()
	cfg.EncodeLevel = zapcore.CapitalColorLevelEncoder
	cfg.ConsoleSeparator = " | "
	// 指定日志时间格式
	cfg.EncodeTime = zapcore.TimeEncoderOfLayout("2006-01-02 15:04:05.000")
	cfg.EncodeCaller = zapcore.ShortCallerEncoder
	encoder := zapcore.NewConsoleEncoder(cfg)
	core := zapcore.NewCore(encoder, zapcore.AddSync(config.ConsoleWriter), level)
	cores = append(cores, core)

	opts := make([]zap.Option, 0)
	if config.ZapOpt != nil {
		opts = config.ZapOpt
	} else {
		opts = append(opts, zap.AddCaller(), zap.AddCallerSkip(2))
	}

	zl := zap.New(zapcore.NewTee(cores...), opts...)

	return &ZapLog{
		sugarLog: zl.Sugar(),
	}
}

func (z *ZapLog) Debug(args ...interface{}) {
	z.sugarLog.Debug(args...)
}

func (z *ZapLog) Info(args ...interface{}) {
	z.sugarLog.Info(args...)
}

func (z *ZapLog) Warn(args ...interface{}) {
	z.sugarLog.Warn(args...)
}

func (z *ZapLog) Error(args ...interface{}) {
	z.sugarLog.Error(args...)
}

func (z *ZapLog) Fatal(args ...interface{}) {
	z.sugarLog.Fatal(args...)
}

func (z *ZapLog) Debugf(format string, args ...interface{}) {
	z.sugarLog.Debugf(format, args...)
}

func (z *ZapLog) Infof(format string, args ...interface{}) {
	z.sugarLog.Infof(format, args...)
}

func (z *ZapLog) Warnf(format string, args ...interface{}) {
	z.sugarLog.Warnf(format, args...)
}

func (z *ZapLog) Errorf(format string, args ...interface{}) {
	z.sugarLog.Errorf(format, args...)
}

func (z *ZapLog) Fatalf(format string, args ...interface{}) {
	z.sugarLog.Fatalf(format, args...)
}

func (z *ZapLog) Println(args ...interface{}) {
	z.sugarLog.Info(args...)
}

func (z *ZapLog) Printf(format string, args ...interface{}) {
	z.sugarLog.Infof(format, args...)
}

func (z *ZapLog) Sync() {
	z.sugarLog.Sync()
}

func Debug(args ...interface{}) {
	globalLog.Debug(args...)
}

func Info(args ...interface{}) {
	globalLog.Info(args...)
}

func Warn(args ...interface{}) {
	globalLog.Warn(args...)
}

func Error(args ...interface{}) {
	globalLog.Error(args...)
}

func Fatal(args ...interface{}) {
	globalLog.Fatal(args...)
}

func Debugf(format string, args ...interface{}) {
	globalLog.Debugf(format, args...)
}

func Infof(format string, args ...interface{}) {
	globalLog.Infof(format, args...)
}

func Warnf(format string, args ...interface{}) {
	globalLog.Warnf(format, args...)
}

func Errorf(format string, args ...interface{}) {
	globalLog.Errorf(format, args...)
}

func Fatalf(format string, args ...interface{}) {
	globalLog.Fatalf(format, args...)
}

func Println(args ...interface{}) {
	globalLog.Info(args...)
}

func Printf(format string, args ...interface{}) {
	globalLog.Infof(format, args...)
}

func GetLogger() *ZapLog {
	return globalLog
}

func GetLoggerSkip(callSkip int) *ZapLog {
	sg := globalLog.sugarLog.WithOptions(
		zap.AddCallerSkip(callSkip),
	)

	return &ZapLog{
		sugarLog: sg,
	}
}

func Flush() {
	globalLog.Sync()
}
