// Package logging 提供日志管理功能，支持访问日志和错误日志分离。
//
// 该文件包含日志相关的核心逻辑，包括：
//   - 访问日志记录（请求方法、路径、状态码、耗时）
//   - 错误日志记录（Debug、Info、Warn、Error 级别）
//   - 日志格式配置（text 或 json）
//   - 应用生命周期日志（启动、停止、信号）
//
// 主要用途：
//
//	用于记录服务器运行时的各类日志信息，便于监控和排查问题。
//
// 注意事项：
//   - 支持 zerolog 高性能日志库
//   - 访问日志和错误日志可分离输出到不同文件
//
// 作者：xfy
package logging

import (
	"fmt"
	"io"
	"os"
	"strings"
	"time"

	"github.com/rs/zerolog"
	"github.com/valyala/fasthttp"
	"rua.plus/lolly/internal/config"
	"rua.plus/lolly/internal/netutil"
	"rua.plus/lolly/internal/variable"
)

// Logger 日志管理器，分离访问日志和错误日志。
type Logger struct {
	accessLog    zerolog.Logger
	errorLog     zerolog.Logger
	accessWriter io.Writer
	accessFile   *os.File
	errorFile    *os.File
	accessFormat string
}

// AppLogger 应用日志管理器，统一管理启动/停止日志。
type AppLogger struct {
	errorLog zerolog.Logger
	writer   io.Writer
	format   string
}

var log zerolog.Logger

const formatJSON = "json"

const formatText = "text"

// Init 初始化全局日志系统（兼容旧接口）。
//
// 该函数用于快速初始化全局 log 实例，支持 console、text 和 json 三种格式。
// 不指定输出路径时默认输出到标准输出。
//
// 参数：
//   - level: 日志级别，支持 debug、info、warn、error（不区分大小写）
//   - format: 日志格式，"console" 为带时间彩色、"text" 为纯文本、"json" 为结构化 JSON
func Init(level string, format string) {
	l := parseLevel(level)
	w := getOutput("") // stdout

	switch format {
	case "console":
		log = zerolog.New(zerolog.ConsoleWriter{Out: w, TimeFormat: time.RFC3339}).Level(l).With().Timestamp().Logger()
	case formatText:
		// text 格式：使用 ConsoleWriter 但不带颜色
		log = zerolog.New(zerolog.ConsoleWriter{Out: w, TimeFormat: time.RFC3339, NoColor: true}).Level(l).With().Timestamp().Logger()
	default:
		// json 或空格式：使用 JSON
		log = zerolog.New(w).Level(l).With().Timestamp().Logger()
	}
}

// New 创建日志管理器，支持访问/错误日志分离。
//
// 根据配置创建 Logger 实例，访问日志和错误日志可分别输出到不同路径。
// 配置为 nil 时使用默认设置（全部输出到标准输出）。
//
// 参数：
//   - cfg: 日志配置，包含访问日志和错误日志的输出路径、级别、格式等
//
// 返回值：
//   - *Logger: 初始化的日志管理器实例
func New(cfg *config.LoggingConfig) *Logger {
	if cfg == nil {
		cfg = &config.LoggingConfig{}
	}

	accessWriter := getOutput(cfg.Access.Path)
	errorWriter := getOutput(cfg.Error.Path)

	logger := &Logger{
		accessFormat: cfg.Access.Format,
		accessWriter: accessWriter,
		accessFile:   writerFile(accessWriter),
		errorFile:    writerFile(errorWriter),
		accessLog:    zerolog.New(accessWriter).With().Timestamp().Logger(),
		errorLog:     zerolog.New(errorWriter).Level(parseLevel(cfg.Error.Level)).With().Timestamp().Logger(),
	}

	return logger
}

// getOutput 获取输出目标（stdout/stderr/文件）。
//
// 根据路径返回对应的输出 writer：
//   - "stdout" 或空字符串：返回 os.Stdout
//   - "stderr"：返回 os.Stderr
//   - 其他：尝试打开文件，失败返回 os.Stdout
//
// 参数：
//   - path: 输出路径
//
// 返回值：
//   - io.Writer: 输出 writer
func getOutput(path string) io.Writer {
	path = strings.TrimSpace(path)
	if path == "" || path == "stdout" {
		return os.Stdout
	}
	if path == "stderr" {
		return os.Stderr
	}

	f, err := os.OpenFile(path, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0o644)
	if err != nil {
		return os.Stdout
	}
	return f
}

// writerFile 从 io.Writer 中提取底层的 *os.File，如果不是或为标准流则返回 nil。
func writerFile(w io.Writer) *os.File {
	if f, ok := w.(*os.File); ok && f != os.Stdout && f != os.Stderr {
		return f
	}
	return nil
}

// LogAccess 记录访问日志（全局实例）。
//
// 使用全局 log 实例记录 HTTP 请求的基本信息，包括方法、路径、状态码、
// 响应大小、耗时和客户端地址。
//
// LogAccess 记录访问日志，支持模板格式或 JSON。
func (l *Logger) LogAccess(ctx *fasthttp.RequestCtx, status int, size int64, duration time.Duration) {
	// JSON 格式或空格式：输出结构化 JSON
	if l.accessFormat == formatJSON || l.accessFormat == "" {
		method := ctx.Method()
		path := ctx.Path()
		req := make([]byte, 0, len(method)+1+len(path))
		req = append(req, method...)
		req = append(req, ' ')
		req = append(req, path...)
		l.accessLog.Info().
			Str("remote_addr", netutil.FormatRemoteAddr(ctx)).
			Bytes("request", req).
			Int("status", status).
			Int64("body_bytes_sent", size).
			Dur("request_time", duration).
			Str("http_referrer", string(ctx.Request.Header.Peek("Referer"))).
			Str("http_user_agent", string(ctx.Request.Header.Peek("User-Agent"))).
			Msg("")
		return
	}

	// 模板格式：直接输出纯文本
	output := l.formatAccessLog(ctx, status, size, duration)
	_, _ = fmt.Fprintln(l.accessWriter, output)
}

// formatAccessLog 根据模板格式化访问日志。
//
// 使用变量系统展开模板字符串，支持以下变量：
//   - $remote_addr: 客户端地址
//   - $remote_user: 认证用户
//   - $request: 请求方法和路径
//   - $status: HTTP 状态码
//   - $body_bytes_sent: 响应体大小
//   - $request_time: 请求处理时间
//   - $http_referer: Referer 头
//   - $http_user_agent: User-Agent 头
//   - $time_local, $time_iso8601: 时间
//   - $host, $uri, $args: 请求信息
//
// 参数：
//   - ctx: FastHTTP 请求上下文
//   - status: HTTP 状态码
//   - size: 响应体大小
//   - duration: 请求处理时间
//
// 返回值：
//   - string: 格式化后的日志字符串
func (l *Logger) formatAccessLog(ctx *fasthttp.RequestCtx, status int, size int64, duration time.Duration) string {
	// 获取认证用户名，无认证时为 "-"
	remoteUser := "-"
	if user := ctx.UserValue("remote_user"); user != nil {
		if username, ok := user.(string); ok && username != "" {
			remoteUser = username
		}
	}

	// 创建变量上下文
	vc := variable.NewContext(ctx)
	defer variable.ReleaseContext(vc)

	// 设置响应信息（同时设置到 ctx 供 builtin getter 使用）
	vc.SetResponseInfo(status, size, duration.Nanoseconds())
	variable.SetResponseInfoInContext(ctx, status, size, duration.Nanoseconds())

	// 设置自定义变量（用于兼容旧的变量名）
	vc.Set("remote_user", remoteUser)
	vc.Set("request", string(ctx.Method())+" "+string(ctx.Path())+" "+string(ctx.Request.Header.Protocol()))
	vc.Set("http_referer", string(ctx.Request.Header.Peek("Referer")))
	vc.Set("http_user_agent", string(ctx.Request.Header.Peek("User-Agent")))
	// 添加 $time 别名（兼容旧格式）
	vc.Set("time", time.Now().Format(time.RFC3339))

	// 展开模板
	return vc.Expand(l.accessFormat)
}

// Debug 返回 Debug 级别日志记录器。
func (l *Logger) Debug() *zerolog.Event {
	return l.errorLog.Debug()
}

// Info 返回 Info 级别日志记录器。
func (l *Logger) Info() *zerolog.Event {
	return l.errorLog.Info()
}

// Warn 返回 Warn 级别日志记录器。
func (l *Logger) Warn() *zerolog.Event {
	return l.errorLog.Warn()
}

// Error 返回 Error 级别日志记录器。
func (l *Logger) Error() *zerolog.Event {
	return l.errorLog.Error()
}

// Close 关闭日志文件。
func (l *Logger) Close() error {
	var err error
	if l.accessFile != nil {
		err = l.accessFile.Close()
	}
	if l.errorFile != nil {
		if closeErr := l.errorFile.Close(); closeErr != nil && err == nil {
			err = closeErr
		}
	}
	return err
}

// Error 返回 Error 级别日志记录器（全局实例）。
//
// 用于记录错误信息，调用链式方法添加字段后需调用 Msg() 输出。
//
// 返回值：
//   - *zerolog.Event: 错误级别日志事件，用于链式调用
func Error() *zerolog.Event {
	return log.Error()
}

// Info 返回 Info 级别日志记录器（全局实例）。
//
// 用于记录一般信息日志，调用链式方法添加字段后需调用 Msg() 输出。
//
// 返回值：
//   - *zerolog.Event: 信息级别日志事件，用于链式调用
func Info() *zerolog.Event {
	return log.Info()
}

// Warn 返回 Warn 级别日志记录器（全局实例）。
//
// 用于记录警告信息，调用链式方法添加字段后需调用 Msg() 输出。
//
// 返回值：
//   - *zerolog.Event: 警告级别日志事件，用于链式调用
func Warn() *zerolog.Event {
	return log.Warn()
}

// Debug 返回 Debug 级别日志记录器（全局实例）。
//
// 用于记录调试信息，调用链式方法添加字段后需调用 Msg() 输出。
// 仅在日志级别设置为 debug 时才会实际输出。
//
// 返回值：
//   - *zerolog.Event: 调试级别日志事件，用于链式调用
func Debug() *zerolog.Event {
	return log.Debug()
}

// parseLevel 解析日志级别。
//
// 将字符串级别转换为 zerolog.Level。
// 支持的级别：debug, info, warn, error（不区分大小写）。
// 未知级别默认返回 info。
//
// 参数：
//   - level: 日志级别字符串
//
// 返回值：
//   - zerolog.Level: 解析后的日志级别
func parseLevel(level string) zerolog.Level {
	switch strings.ToLower(level) {
	case "debug":
		return zerolog.DebugLevel
	case "info":
		return zerolog.InfoLevel
	case "warn":
		return zerolog.WarnLevel
	case "error":
		return zerolog.ErrorLevel
	default:
		return zerolog.InfoLevel
	}
}

// NewAppLogger 创建应用日志管理器。
//
// 根据配置创建 AppLogger 实例，用于统一管理应用生命周期日志
// （启动、停止、信号处理等）。默认使用 text 格式输出到错误日志路径。
//
// 参数：
//   - cfg: 日志配置，包含输出路径、级别、格式等设置
//
// 返回值：
//   - *AppLogger: 初始化的应用日志记录器实例
func NewAppLogger(cfg *config.LoggingConfig) *AppLogger {
	if cfg == nil {
		cfg = &config.LoggingConfig{}
	}

	format := cfg.Format
	if format == "" {
		format = "text" // 默认纯文本
	}

	writer := getOutput(cfg.Error.Path)

	var errorLog zerolog.Logger
	if format == "text" {
		// text 格式：使用 ConsoleWriter（无颜色）
		errorLog = zerolog.New(zerolog.ConsoleWriter{Out: writer, TimeFormat: time.RFC3339, NoColor: true}).Level(parseLevel(cfg.Error.Level)).With().Timestamp().Logger()
	} else {
		// json 格式
		errorLog = zerolog.New(writer).Level(parseLevel(cfg.Error.Level)).With().Timestamp().Logger()
	}

	return &AppLogger{
		format:   format,
		errorLog: errorLog,
		writer:   writer,
	}
}

// LogStartup 记录启动消息。
func (l *AppLogger) LogStartup(msg string, fields map[string]string) {
	event := l.errorLog.Info()
	for k, v := range fields {
		event.Str(k, v)
	}
	event.Msg(msg)
}

// LogShutdown 记录停止消息。
func (l *AppLogger) LogShutdown(msg string) {
	l.errorLog.Info().Msg(msg)
}

// LogSignal 记录信号处理消息。
func (l *AppLogger) LogSignal(sig string, action string) {
	l.errorLog.Info().Str("signal", sig).Str("action", action).Msg("收到信号")
}

// Info 返回 Info 级别日志记录器。
func (l *AppLogger) Info() *zerolog.Event {
	return l.errorLog.Info()
}

// Error 返回 Error 级别日志记录器。
func (l *AppLogger) Error() *zerolog.Event {
	return l.errorLog.Error()
}
