package capture

import (
	"fmt"
	"io"
	"log"
	"os"
	"time"

	"github.com/sirupsen/logrus"
)

var Logger *logrus.Logger

// Hook 结构体定义了一个日志钩子,用于将日志条目输出到指定的 Writer,并使用指定的 Formatter 格式化日志条目。
// Level 字段是一个包含日志级别的切片,用于指定该钩子应处理的日志级别。
type Hook struct {
	Writer    io.Writer
	Formatter logrus.Formatter
	Level     []logrus.Level
}

// Fire 方法是 logrus.Hook 接口的实现。当日志条目符合钩子的日志级别时,
// 这个方法会被调用。它首先使用 Formatter 将日志条目格式化为字节切片,
// 然后将其写入 Writer。此版本增强了日志条目,添加了错误发生的文件名和行号。
func (h *Hook) Fire(entry *logrus.Entry) error {
	// 获取错误发生的文件名和行号
	if entry.HasCaller() {
		entry.Data["file"] = fmt.Sprintf("%s:%d", entry.Caller.File, entry.Caller.Line)
	}

	line, err := h.Formatter.Format(entry)
	if err != nil {
		return err
	}
	h.Writer.Write(line)
	return nil
}

// Levels 方法返回该钩子支持的日志级别切片。
// logrus 在写入日志时会调用这个方法来决定是否使用这个钩子。
func (h *Hook) Levels() []logrus.Level {
	return h.Level
}

// newHook 函数用于创建一个新的 Hook 实例。它接收一个 Writer,
// 一个 Formatter 和一个最低的日志级别 level 作为参数。
// 函数会为低于或等于指定级别的所有日志级别创建一个 Hook,
// 并返回该 Hook 实例。
func newHook(writer io.Writer, formatter logrus.Formatter, level logrus.Level) *Hook {
	var levels []logrus.Level
	for _, l := range logrus.AllLevels {
		if l <= level {
			levels = append(levels, l)
		}
	}
	return &Hook{
		Writer:    writer,
		Formatter: formatter,
		Level:     levels,
	}
}

// InitLogs 函数负责初始化 logrus.Logger 对象。它首先检查是否存在指定的日志目录(默认为 ./logs/)。
// 如果目录不存在,它会创建该目录。
func InitLogs(logFilePath string, logLevelStr string) *logrus.Logger {
	//dir := "./logs/"
	// 检查目录是否存在
	if _, err := os.Stat(logFilePath); os.IsNotExist(err) {
		// 目录不存在,创建目录
		err := os.MkdirAll(logFilePath, os.ModePerm)
		if err != nil {
			fmt.Println("Error creating directory:", err)
			return nil
		}
		fmt.Println("Directory created:", logFilePath)
	}

	// 尝试将 logLevelStr 转换为 logrus.Level。如果转换失败,则默认使用 InfoLevel 级别。
	logLevel, err := logrus.ParseLevel(logLevelStr)
	if err != nil {
		logLevel = logrus.InfoLevel
		log.Printf("Invalid log level: %s. Defaulting to info", logLevelStr)
	}

	// 初始化 Logger 实例,并将其输出设置为 io.Discard,即默认情况下不输出任何日志。
	// 这是为了强制所有日志输出都通过钩子来处理。
	Logger = logrus.New()
	Logger.SetOutput(io.Discard)

	// 启用记录调用者信息,这样可以记录错误发生的位置(文件名和行号)。
	Logger.SetReportCaller(true)

	// 使用当前日期生成日志文件名,格式为 `log-YYYY-MM-DD.log`
	currentDate := time.Now().Format("2006-01-02")
	logFileName := fmt.Sprintf("%s/log-%s.log", logFilePath, currentDate)

	// 打开或创建日志文件。日志文件的路径由 logFilePath 参数指定。
	logFile, err := os.OpenFile(logFileName, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
	if err != nil {
		log.Printf("Failed to open log file: %s", logFileName)
		panic(err)
	}

	// 添加一个新的 Hook,将日志输出到日志文件中,并使用 JSONFormatter 进行格式化。
	Logger.AddHook(newHook(
		logFile,
		&logrus.JSONFormatter{},
		logLevel,
	))

	// 添加另一个 Hook,将日志输出到标准错误输出(通常是控制台),
	// 并使用 TextFormatter 格式化日志,显示完整时间戳并强制使用颜色。
	Logger.AddHook(newHook(
		os.Stderr,
		&logrus.TextFormatter{
			FullTimestamp: true,
			ForceColors:   true,
		},
		logLevel,
	))
	return Logger
}