package middleware

import (
	"bytes"
	"encoding/json"
	"log/slog"
	"strings"

	"github.com/gin-gonic/gin"
)

// ErrorAudit emits a structured Error-level log for unexpected HTTP failures.
// The logging core is the single persistence path for sys_error, matching the
// reference behavior and avoiding duplicate rows for HTTP failures.
func ErrorAudit(logger *slog.Logger) gin.HandlerFunc {
	return func(c *gin.Context) {
		c.Next()
		privateErrors := strings.TrimSpace(c.Errors.ByType(gin.ErrorTypePrivate).String())
		auditPersistFailed, _ := c.Get(ctxOperationAuditPersistFailedKey)
		// sysError writes must never audit themselves. Log-viewer failures are
		// already recorded by the handler with the underlying filesystem error;
		// emitting again from the response envelope would duplicate both the
		// classified error file and the sys_error row.
		if auditPersistFailed != true && (strings.Contains(c.Request.URL.Path, "/sysError/") || strings.Contains(c.Request.URL.Path, "/logViewer/") || c.Writer.Status() >= 300 && c.Writer.Status() < 500) {
			return
		}
		var response Response
		var body []byte
		if value, ok := c.Get(ctxRespBufferKey); ok {
			if buffer, valid := value.(*bytes.Buffer); valid {
				body = buffer.Bytes()
			}
		}
		if json.Unmarshal(body, &response) != nil && privateErrors == "" {
			return
		}
		if response.Code == CodeSuccess && privateErrors == "" {
			return
		}
		if privateErrors == "" && expectedClientFailure(response.Msg) {
			return
		}
		errorMessage := response.Msg
		if privateErrors != "" {
			errorMessage = privateErrors
		}
		requestID, _ := c.Get("request_id")
		if logger != nil {
			logger.ErrorContext(c.Request.Context(), "请求处理失败", "mod", failureLogModule(c.Request.URL.Path), "path", c.Request.URL.Path, "method", c.Request.Method, "status", c.Writer.Status(), "error", errorMessage, "request_id", stringValue(requestID), "trace_id", stringValueFromContext(c, "trace_id"))
		}
	}
}

func failureLogModule(path string) string {
	for _, marker := range []string{"/fileUploadAndDownload/", "/mediaUpload/", "/attachmentCategory/"} {
		if strings.Contains(path, marker) {
			return "upload"
		}
	}
	if strings.Contains(path, "/timedTask/") {
		return "timedTask"
	}
	if strings.Contains(path, "/logViewer/") {
		return "log-viewer"
	}
	return "biz"
}

func expectedClientFailure(message string) bool {
	for _, value := range []string{"参数错误", "请输入用户名和密码", "验证码错误", "用户名不存在或者密码错误", "用户被禁止登录", "账号已锁定", "权限不足", "密码已过期", "未登录", "token", "令牌失效"} {
		if strings.Contains(message, value) {
			return true
		}
	}
	return false
}
