package errors

import (
	"strconv"

	"github.com/gofiber/fiber/v2"
	"go.uber.org/zap"

	"github.com/break/junhong_cmp_fiber/pkg/constants"
)

// ErrorContext 错误发生时的请求上下文（用于日志记录）
type ErrorContext struct {
	RequestID string // 请求 ID（唯一标识）
	Method    string // HTTP 方法
	Path      string // 请求路径
	Query     string // Query 参数
	Body      string // 请求 Body（限制 50KB）
	IP        string // 客户端 IP
	UserAgent string // User-Agent
	UserID    string // 用户 ID（如果已认证）
}

const (
	// MaxBodyLogSize 请求 Body 日志记录最大字节数（50KB）
	MaxBodyLogSize = 50 * 1024
)

// FromFiberContext 从 Fiber Context 提取错误上下文
func FromFiberContext(c *fiber.Ctx) *ErrorContext {
	ctx := &ErrorContext{
		Method:    c.Method(),
		Path:      c.Path(),
		Query:     c.Request().URI().QueryArgs().String(),
		IP:        c.IP(),
		UserAgent: c.Get("User-Agent"),
	}

	// 提取 Request ID
	if rid := c.Locals(constants.ContextKeyRequestID); rid != nil {
		ctx.RequestID = rid.(string)
	}
	if ctx.RequestID == "" {
		ctx.RequestID = c.Get("X-Request-ID")
	}

	// 提取 User ID（如果已认证）
	if uid := c.Locals(constants.ContextKeyUserID); uid != nil {
		if userID, ok := uid.(uint); ok {
			ctx.UserID = strconv.FormatUint(uint64(userID), 10)
		}
	}

	// 提取请求 Body（限制 50KB）
	bodyBytes := c.Body()
	if len(bodyBytes) > 0 {
		if len(bodyBytes) > MaxBodyLogSize {
			// 超过限制时截断并添加提示
			ctx.Body = string(bodyBytes[:MaxBodyLogSize]) + " ... (truncated)"
		} else {
			ctx.Body = string(bodyBytes)
		}
	}

	return ctx
}

// ToLogFields 转换为 Zap 日志字段
func (ec *ErrorContext) ToLogFields() []zap.Field {
	fields := []zap.Field{
		zap.String("request_id", ec.RequestID),
		zap.String("method", ec.Method),
		zap.String("path", ec.Path),
		zap.String("ip", ec.IP),
	}

	// 可选字段（非空时添加）
	if ec.Query != "" {
		fields = append(fields, zap.String("query", ec.Query))
	}
	if ec.Body != "" {
		fields = append(fields, zap.String("body", ec.Body))
	}
	if ec.UserAgent != "" {
		fields = append(fields, zap.String("user_agent", ec.UserAgent))
	}
	if ec.UserID != "" {
		fields = append(fields, zap.String("user_id", ec.UserID))
	}

	return fields
}
