package middleware

import (
	"time"

	"github.com/Wei-Shaw/sub2api/internal/pkg/ctxkey"
	"github.com/Wei-Shaw/sub2api/internal/pkg/logger"
	"github.com/gin-gonic/gin"
	"go.uber.org/zap"
)

// Logger 请求日志中间件
func Logger() gin.HandlerFunc {
	return func(c *gin.Context) {
		// 开始时间
		startTime := time.Now()

		// 请求路径
		path := c.Request.URL.Path

		// 处理请求
		c.Next()

		// 跳过健康检查等高频探针路径的日志
		if path == "/health" || path == "/setup/status" {
			return
		}

		endTime := time.Now()
		latency := endTime.Sub(startTime)

		method := c.Request.Method
		statusCode := c.Writer.Status()
		clientIP := c.ClientIP()
		protocol := c.Request.Proto
		accountID, hasAccountID := c.Request.Context().Value(ctxkey.AccountID).(int64)
		platform, _ := c.Request.Context().Value(ctxkey.Platform).(string)
		model, _ := c.Request.Context().Value(ctxkey.Model).(string)

		fields := []zap.Field{
			zap.String("component", "http.access"),
			zap.Int("status_code", statusCode),
			zap.Int64("latency_ms", latency.Milliseconds()),
			zap.String("client_ip", clientIP),
			zap.String("protocol", protocol),
			zap.String("method", method),
			zap.String("path", path),
		}
		if hasAccountID && accountID > 0 {
			fields = append(fields, zap.Int64("account_id", accountID))
		}
		if platform != "" {
			fields = append(fields, zap.String("platform", platform))
		}
		if model != "" {
			fields = append(fields, zap.String("model", model))
		}

		l := logger.FromContext(c.Request.Context()).With(fields...)
		l.Info("http request completed", zap.Time("completed_at", endTime))
		// 当全局日志级别高于 info（如 warn/error）时，access info 不会进入 zap core，
		// 这里补写一次 sink，保证 ops 系统日志仍可索引关键访问轨迹。
		if !logger.L().Core().Enabled(logger.LevelInfo) {
			sinkFields := map[string]any{
				"component":    "http.access",
				"status_code":  statusCode,
				"latency_ms":   latency.Milliseconds(),
				"client_ip":    clientIP,
				"protocol":     protocol,
				"method":       method,
				"path":         path,
				"completed_at": endTime,
			}
			if requestID, ok := c.Request.Context().Value(ctxkey.RequestID).(string); ok && requestID != "" {
				sinkFields["request_id"] = requestID
			}
			if clientRequestID, ok := c.Request.Context().Value(ctxkey.ClientRequestID).(string); ok && clientRequestID != "" {
				sinkFields["client_request_id"] = clientRequestID
			}
			if hasAccountID && accountID > 0 {
				sinkFields["account_id"] = accountID
			}
			if platform != "" {
				sinkFields["platform"] = platform
			}
			if model != "" {
				sinkFields["model"] = model
			}
			logger.WriteSinkEvent("info", "http.access", "http request completed", sinkFields)
		}

		if len(c.Errors) > 0 {
			l.Warn("http request contains gin errors", zap.String("errors", c.Errors.String()))
		}
	}
}
