package log

import (
	"github.com/natefinch/lumberjack"
	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
	"os"
	"path/filepath"

	"github.com/adamweixuan/getty"
)

var (
	runLogger   getty.Logger
	eventLogger getty.Logger
	traceLogger *zap.Logger
	errorLogger getty.Logger
)

const (
	logDir = "./output/log/"
	run    = "run.log"
	trace  = "trace.log"
	error  = "error.log"
)

// debug 模式下会将日志输出到控制台和文件，其他模式只输出到文件
func getLogWriter(path string, level zapcore.Level) zapcore.WriteSyncer {
	lumberJackLogger := &lumberjack.Logger{
		Filename:   path,
		MaxSize:    10,
		MaxBackups: 5,
		MaxAge:     30,
		Compress:   false,
	}
	if level == zapcore.DebugLevel {
		return zapcore.NewMultiWriteSyncer(zapcore.AddSync(os.Stdout), zapcore.AddSync(lumberJackLogger))
	}
	return zapcore.AddSync(lumberJackLogger)
}

func getEncoder() zapcore.Encoder {
	encoderConfig := zap.NewProductionEncoderConfig()
	encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
	encoderConfig.EncodeLevel = zapcore.CapitalLevelEncoder
	return zapcore.NewConsoleEncoder(encoderConfig)
}

func getCurPath() string {
	exePath, err := os.Executable()
	if err != nil {
		panic(err)
	}
	return filepath.Dir(exePath)
}

func Init(level string) {
	curPath := getCurPath()
	base := filepath.Join(curPath, logDir)
	if _, err := os.Stat(base); err != nil {
		if os.IsNotExist(err) {
			if err := os.MkdirAll(base, os.ModePerm); err != nil {
				panic(err)
			}
		} else {
			panic(err)
		}
	}

	if len(curPath) == 0 {
		panic("invalid current path")
	}

	runLogger = initSugaredLogger(filepath.Join(base, run), level)
	errorLogger = initSugaredLogger(filepath.Join(base, error), level)
	traceLogger = initLogger(filepath.Join(base, trace), false, level)
	// 框架的日志也输入到 run.log 中
	getty.SetLogger(runLogger)
}

func initLogger(path string, caller bool, level string) *zap.Logger {
	zapLevel := zapcore.InfoLevel
	// 忽略错误，如果传入的字符串有误默认 info 级别
	_ = zapLevel.Set(level)

	encoder := getEncoder()
	writeSyncer := getLogWriter(path, zapLevel)
	core := zapcore.NewCore(encoder, writeSyncer, zapLevel)
	if caller {
		return zap.New(core, zap.AddCaller())
	} else {
		return zap.New(core)
	}
}

func initSugaredLogger(path string, level string) *zap.SugaredLogger {
	logger := initLogger(path, true, level)
	return logger.Sugar()
}

func Run() getty.Logger {
	return runLogger
}

func Trace() *zap.Logger {
	return traceLogger
}

func Error() getty.Logger {
	return errorLogger
}
