package logs

import (
	"fmt"
	"github.com/natefinch/lumberjack"
	"github.com/sirupsen/logrus"
	"log"
	"os"
	"path"
	"path/filepath"
	"strings"
	"time"
)

const (
	defaultTimestamp    = "2006-01-02 15:04:05.999"
	defaultLogDelimeter = "|"
	defaultLogLevel     = logrus.ErrorLevel
	defaultLogFileSize  = 50
	// 日志保存天数
	defaultMaxAge = 3
	// 每天最多保存的日志数量
	defaultMaxBackups = 3
)

type LogConfig struct {
	Path       string
	AppInfo    string
	MaxSize    int // 日志文件最大尺寸，单位MB
	MaxBackups int // 最多保留的旧日志文件数
	MaxAge     int // 日志文件保留的最长时间，单位天
	// 控制台输出
	Console bool
}

// 定义Hook结构体
type FileHook struct {
	level     logrus.Level
	formatter logrus.Formatter
	logger    *log.Logger
}

func InitLog(level string, config LogConfig) error {
	FormatterInit()
	if config.Console {
		logrus.SetOutput(os.Stdout)
	} else {
		logrus.SetOutput(&NoOut{})
	}
	if level == "" {
		level = "debug"
	}
	logrusLevel, err := logrus.ParseLevel(level)
	if err == nil {
		logrus.SetLevel(logrusLevel)
	} else {
		logrus.SetLevel(defaultLogLevel)
		logrus.WithError(err).Error("log level parse error, use default log level error")
	}
	logrus.SetFormatter(NewFormatter())
	logrus.SetReportCaller(true)

	if config.Path == "" {
		config.Path = log_config.Path
	}

	if config.Path == "" {
		return fmt.Errorf("config.Path is nil")
	}

	log_config = config
	RegisterLogHook(config)
	return nil
}

// 实现Hook接口
func (hook *FileHook) Levels() []logrus.Level {
	return logrus.AllLevels[:hook.level+1]
}

func (hook *FileHook) Fire(entry *logrus.Entry) error {
	// 将日志消息格式化为字符串
	bytes, err := hook.formatter.Format(entry)
	hook.logger.Print(string(bytes))
	if err != nil {
		return fmt.Errorf("failed to format log entry: %v", err)
	}
	if err != nil {
		return fmt.Errorf("failed to write log entry: %v", err)
	}
	return nil
}

type NoOut struct {
}

func (no *NoOut) Write(p []byte) (n int, err error) {
	return 0, nil
}

func RegisterLogHook(logConf LogConfig) {
	hooks := make(logrus.LevelHooks)
	logrus.StandardLogger().ReplaceHooks(hooks)
	// 日志文件最大尺寸，不超过100MB
	if logConf.MaxSize > defaultLogFileSize || logConf.MaxSize < 1 {
		logConf.MaxSize = defaultLogFileSize
	}
	// 最多保留的旧日志文件数
	if logConf.MaxBackups > defaultMaxBackups || logConf.MaxBackups == 0 {
		logConf.MaxBackups = defaultMaxBackups
	}
	// 日志文件保留的最长时间，单位天
	if logConf.MaxAge > defaultMaxAge || logConf.MaxAge == 0 {
		logConf.MaxAge = defaultMaxAge
	}

	for _, val := range logrus.AllLevels {
		if val <= logrus.GetLevel() {
			fileName := filepath.Join(logConf.Path, logConf.AppInfo+"."+val.String()+".log")
			archive := &lumberjack.Logger{
				Filename:   fileName,
				MaxSize:    logConf.MaxSize,    // 日志文件最大尺寸，单位MB
				MaxBackups: logConf.MaxBackups, // 最多保留的旧日志文件数
				MaxAge:     logConf.MaxAge,     // 日志文件保留的最长时间，单位天
				LocalTime:  true,               // 使用本地时间
				Compress:   true,
			}
			sysLogger := &log.Logger{}
			sysLogger.SetOutput(archive)
			hooker := &FileHook{
				level:     val,
				formatter: NewFormatter(),
				logger:    sysLogger,
			}
			hooks.Add(hooker)
		}
	}
}

func FormatterInit() {
	logrus.SetFormatter(NewFormatter())
	logrus.SetReportCaller(true)
}

func NewFormatter() *MyFormatter {
	return &MyFormatter{
		Pid:             os.Getpid(),
		TimestampFormat: defaultTimestamp,
	}
}

// Custom log format definition
type MyFormatter struct {
	Pid             int
	TimestampFormat string
}

func (s *MyFormatter) Format(entry *logrus.Entry) ([]byte, error) {
	//var callerFun string
	var fileInfo string
	var entryDataInfo string

	for key, val := range entry.Data {
		entryDataInfo += fmt.Sprintf(" %s=\"%v\"", key, val)
	}
	if entry.HasCaller() {
		//callerFun = entry.Caller.Function
		fileInfo = fmt.Sprintf("%s:%d", path.Base(entry.Caller.File), entry.Caller.Line)
	}

	timestamp := time.Now().Local().Format(s.TimestampFormat)
	msg := fmt.Sprintf("%s|%s|%d|%s] %s%s\n",
		timestamp, strings.ToUpper(entry.Level.String()), s.Pid, fileInfo, entry.Message, entryDataInfo)
	return []byte(msg), nil
}

var log_config LogConfig

func GetLogConf() LogConfig {
	return log_config
}
