package middleware

import (
	"bytes"
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"time"

	"github.com/LoveLosita/smartflow/backend/dao"
	"github.com/LoveLosita/smartflow/backend/respond"
	"github.com/gin-gonic/gin"
)

type IdempotencyValue struct {
	Status int    `json:"status"` // HTTP 状态码
	Body   string `json:"body"`   // JSON 响应体
}

type responseRecorder struct {
	gin.ResponseWriter
	body *bytes.Buffer
}

func (r *responseRecorder) Write(b []byte) (int, error) {
	r.body.Write(b)                  // 录制到缓冲区
	return r.ResponseWriter.Write(b) // 正常发送给前端
}

func IdempotencyMiddleware(cache *dao.CacheDAO) gin.HandlerFunc {
	return func(c *gin.Context) {
		// 1. 获取 IKey
		ikey := c.GetHeader("X-Idempotency-Key")
		if ikey == "" {
			c.JSON(http.StatusBadRequest, respond.MissingIdempotencyKey) // 400 错误，缺少 IKey
			c.Abort()
			return
		}

		userID := c.GetInt("user_id") // 假设 JWT 已存入
		redisKey := fmt.Sprintf("idempotency:%d:%s", userID, ikey)

		// 2. 查 Redis 缓存
		cachedData, err := cache.GetRecord(c, redisKey)
		if err != nil { // 💡 Fail-Open：Redis 挂了也别卡住用户，记个日志继续走
			log.Printf("[Idempotency] Redis Get error: %v", err)
		} else if cachedData != "" {
			// 命中缓存，直接回放录像
			var val IdempotencyValue
			json.Unmarshal([]byte(cachedData), &val)
			c.Data(val.Status, "application/json", []byte(val.Body))
			c.Abort()
			return
		}

		// 3. 分布式锁：防止微秒级的并发碰撞 (SetNX)
		// 锁 10 秒，防止请求卡死导致 key 永久锁定
		lockKey := redisKey + ":lock"
		success, err := cache.AcquireLock(c, lockKey, 10*time.Second)
		if err != nil { // 如果加锁报错，为了保险我们依然放行，让底层的数据库唯一索引去兜底
			log.Printf("[Idempotency] Redis Lock error: %v", err)
		} else if !success {
			c.JSON(http.StatusConflict, respond.RequestIsProcessing)
			c.Abort()
			return
		}
		// 💡 只有在加锁成功时才需要 defer 删锁
		if err == nil && success {
			defer cache.ReleaseLock(c, lockKey)
		}

		// 4. 装饰 ResponseWriter 开始录制
		recorder := &responseRecorder{
			ResponseWriter: c.Writer,
			body:           bytes.NewBufferString(""),
		}
		c.Writer = recorder

		// 5. 执行后续 Handler (你的 Service 逻辑)
		c.Next()

		// 6. 录制完成，存入 Redis (缓存 24 小时)
		// 只有状态码 < 500 时才存入 Redis，这样如果是服务器临时抽风，用户重试依然有机会成功
		if c.Writer.Status() < 500 {
			respVal := IdempotencyValue{
				Status: c.Writer.Status(),
				Body:   recorder.body.String(),
			}
			data, _ := json.Marshal(respVal)
			if err := cache.SaveRecord(c, redisKey, string(data), 24*time.Hour); err != nil {
				log.Printf("[Idempotency] Redis Save error: %v", err)
			}
		}
	}
}
