package public

import (
	"fmt"
	"net/http"
	"time"

	"bindbox-game/internal/pkg/core"
	"bindbox-game/internal/pkg/logger"
	"bindbox-game/internal/repository/mysql"
	"bindbox-game/internal/repository/mysql/model"
	douyinsvc "bindbox-game/internal/service/douyin"
	gamesvc "bindbox-game/internal/service/game"
	livestreamsvc "bindbox-game/internal/service/livestream"

	"go.uber.org/zap"
	"gorm.io/gorm"
)

type handler struct {
	logger     logger.CustomLogger
	repo       mysql.Repo
	livestream livestreamsvc.Service
	douyin     douyinsvc.Service
}

// New 创建公开接口处理器
func New(l logger.CustomLogger, repo mysql.Repo, douyin douyinsvc.Service) *handler {
	ticketSvc := gamesvc.NewTicketService(l, repo)
	return &handler{
		logger:     l,
		repo:       repo,
		livestream: livestreamsvc.New(l, repo, ticketSvc),
		douyin:     douyin,
	}
}

// ========== 直播间公开接口 ==========

type publicActivityResponse struct {
	ID           int64                 `json:"id"`
	Name         string                `json:"name"`
	StreamerName string                `json:"streamer_name"`
	Status       int32                 `json:"status"`
	StartTime    string                `json:"start_time,omitempty"`
	EndTime      string                `json:"end_time,omitempty"`
	Prizes       []publicPrizeResponse `json:"prizes"`
}

type publicPrizeResponse struct {
	ID          int64  `json:"id"`
	Name        string `json:"name"`
	Image       string `json:"image"`
	Level       int32  `json:"level"`
	Remaining   int32  `json:"remaining"`
	Probability string `json:"probability"`
	Weight      int32  `json:"weight"`
}

// GetLivestreamByAccessCode 根据访问码获取直播间活动详情
// @Summary 获取直播间活动详情(公开)
// @Description 根据访问码获取直播间活动和奖品信息，无需登录
// @Tags 公开接口.直播间
// @Accept json
// @Produce json
// @Param access_code path string true "访问码"
// @Success 200 {object} publicActivityResponse
// @Failure 404 {object} code.Failure
// @Router /api/public/livestream/{access_code} [get]
func (h *handler) GetLivestreamByAccessCode() core.HandlerFunc {
	return func(ctx core.Context) {
		accessCode := ctx.Param("access_code")
		if accessCode == "" {
			ctx.AbortWithError(core.Error(http.StatusBadRequest, 10001, "访问码不能为空"))
			return
		}

		activity, err := h.livestream.GetActivityByAccessCode(ctx.RequestContext(), accessCode)
		if err != nil {
			ctx.AbortWithError(core.Error(http.StatusNotFound, 10002, "活动不存在或已结束"))
			return
		}

		prizes, _ := h.livestream.ListPrizes(ctx.RequestContext(), activity.ID)

		res := &publicActivityResponse{
			ID:           activity.ID,
			Name:         activity.Name,
			StreamerName: activity.StreamerName,
			Status:       activity.Status,
			Prizes:       make([]publicPrizeResponse, len(prizes)),
		}

		if !activity.StartTime.IsZero() {
			res.StartTime = activity.StartTime.Format("2006-01-02 15:04:05")
		}
		if !activity.EndTime.IsZero() {
			res.EndTime = activity.EndTime.Format("2006-01-02 15:04:05")
		}

		// 计算总权重 (仅统计有库存的)
		var totalWeight int64
		for _, p := range prizes {
			if p.Remaining != 0 {
				totalWeight += int64(p.Weight)
			}
		}

		for i, p := range prizes {
			probStr := "0%"
			if p.Remaining != 0 && totalWeight > 0 {
				prob := (float64(p.Weight) / float64(totalWeight)) * 100
				probStr = fmt.Sprintf("%.2f%%", prob)
			}

			res.Prizes[i] = publicPrizeResponse{
				ID:          p.ID,
				Name:        p.Name,
				Image:       p.Image,
				Level:       p.Level,
				Remaining:   p.Remaining,
				Probability: probStr,
				Weight:      p.Weight,
			}
		}

		ctx.Payload(res)
	}
}

type publicDrawLogResponse struct {
	PrizeName    string `json:"prize_name"`
	Level        int32  `json:"level"`
	DouyinUserID string `json:"douyin_user_id"`
	CreatedAt    string `json:"created_at"`
}

type listPublicDrawLogsResponse struct {
	List  []publicDrawLogResponse `json:"list"`
	Total int64                   `json:"total"`
}

// GetLivestreamWinners 获取中奖记录(公开)
// @Summary 获取直播间中奖记录(公开)
// @Description 根据访问码获取直播间中奖历史，无需登录
// @Tags 公开接口.直播间
// @Accept json
// @Produce json
// @Param access_code path string true "访问码"
// @Param page query int false "页码" default(1)
// @Param page_size query int false "每页数量" default(20)
// @Success 200 {object} listPublicDrawLogsResponse
// @Failure 404 {object} code.Failure
// @Router /api/public/livestream/{access_code}/winners [get]
func (h *handler) GetLivestreamWinners() core.HandlerFunc {
	return func(ctx core.Context) {
		accessCode := ctx.Param("access_code")
		if accessCode == "" {
			ctx.AbortWithError(core.Error(http.StatusBadRequest, 10001, "访问码不能为空"))
			return
		}

		activity, err := h.livestream.GetActivityByAccessCode(ctx.RequestContext(), accessCode)
		if err != nil {
			ctx.AbortWithError(core.Error(http.StatusNotFound, 10002, "活动不存在"))
			return
		}

		page := 1      // Default page 1
		pageSize := 20 // Default pageSize 20

		var startTime, endTime *time.Time

		params := ctx.RequestInputParams()
		if stStr := params.Get("start_time"); stStr != "" {
			if t, err := time.Parse(time.RFC3339, stStr); err == nil {
				startTime = &t
			}
		}

		if etStr := params.Get("end_time"); etStr != "" {
			if t, err := time.Parse(time.RFC3339, etStr); err == nil {
				endTime = &t
			}
		}

		logs, total, err := h.livestream.ListDrawLogs(ctx.RequestContext(), activity.ID, page, pageSize, startTime, endTime)
		if err != nil {
			ctx.AbortWithError(core.Error(http.StatusInternalServerError, 10003, err.Error()))
			return
		}

		res := &listPublicDrawLogsResponse{
			List:  make([]publicDrawLogResponse, len(logs)),
			Total: total,
		}

		for i, log := range logs {
			// 隐藏部分抖音ID
			maskedID := log.DouyinUserID
			if len(maskedID) > 4 {
				maskedID = maskedID[:2] + "****" + maskedID[len(maskedID)-2:]
			}

			res.List[i] = publicDrawLogResponse{
				PrizeName:    log.PrizeName,
				Level:        log.Level,
				DouyinUserID: maskedID,
				CreatedAt:    log.CreatedAt.Format("2006-01-02 15:04:05"),
			}
		}

		ctx.Payload(res)
	}
}

// ========== 直播间抽奖接口 ==========

type drawRequest struct {
	DouyinOrderID string `json:"shop_order_id" binding:"required"` // 店铺订单号
	DouyinUserID  string `json:"douyin_user_id"`                   // 可选，兼容旧逻辑
}

type drawReceipt struct {
	SeedVersion int32  `json:"seed_version"`
	Timestamp   int64  `json:"timestamp"`
	Nonce       int64  `json:"nonce"`
	Signature   string `json:"signature"`
	Algorithm   string `json:"algorithm"`
}

type drawResponse struct {
	PrizeID      int64        `json:"prize_id"`
	PrizeName    string       `json:"prize_name"`
	PrizeImage   string       `json:"prize_image"`
	Level        int32        `json:"level"`
	SeedHash     string       `json:"seed_hash"`
	UserNickname string       `json:"user_nickname"`
	Receipt      *drawReceipt `json:"receipt,omitempty"`
}

// DrawLivestream 执行直播间抽奖
// @Summary 执行直播间抽奖(公开)
// @Description 根据访问码执行抽奖，需提供抖音用户ID
// @Tags 公开接口.直播间
// @Accept json
// @Produce json
// @Param access_code path string true "访问码"
// @Param body body drawRequest true "抽奖参数"
// @Success 200 {object} drawResponse
// @Failure 400 {object} code.Failure
// @Failure 404 {object} code.Failure
// @Router /api/public/livestream/{access_code}/draw [post]
func (h *handler) DrawLivestream() core.HandlerFunc {
	return func(ctx core.Context) {
		accessCode := ctx.Param("access_code")
		if accessCode == "" {
			ctx.AbortWithError(core.Error(http.StatusBadRequest, 10001, "访问码不能为空"))
			return
		}

		var req drawRequest
		if err := ctx.ShouldBindJSON(&req); err != nil {
			ctx.AbortWithError(core.Error(http.StatusBadRequest, 10002, "参数格式错误"))
			return
		}

		activity, err := h.livestream.GetActivityByAccessCode(ctx.RequestContext(), accessCode)
		if err != nil {
			ctx.AbortWithError(core.Error(http.StatusNotFound, 10003, "活动不存在或已结束"))
			return
		}

		if activity.Status != 1 {
			ctx.AbortWithError(core.Error(http.StatusBadRequest, 10004, "活动未开始"))
			return
		}

		// 1. [核心重构] 根据店铺订单号查出本地记录并核销
		var order model.DouyinOrders
		db := h.repo.GetDbW().WithContext(ctx.RequestContext())

		err = db.Where("shop_order_id = ?", req.DouyinOrderID).First(&order).Error
		if err != nil {
			ctx.AbortWithError(core.Error(http.StatusNotFound, 10005, "订单不存在"))
			return
		}

		if order.RewardGranted >= int32(order.ProductCount) {
			ctx.AbortWithError(core.Error(http.StatusBadRequest, 10006, "该订单已完成抽奖，请勿重复操作"))
			return
		}

		// 执行抽奖
		result, err := h.livestream.Draw(ctx.RequestContext(), livestreamsvc.DrawInput{
			ActivityID:    activity.ID,
			DouyinOrderID: order.ID,
			ShopOrderID:   order.ShopOrderID,
			DouyinUserID:  order.DouyinUserID,
			UserNickname:  order.UserNickname,
		})
		if err != nil {
			// 检查是否为黑名单错误
			if err.Error() == "该用户已被列入黑名单，无法开奖" {
				ctx.AbortWithError(core.Error(http.StatusForbidden, 10008, err.Error()))
				return
			}
			ctx.AbortWithError(core.Error(http.StatusBadRequest, 10007, err.Error()))
			return
		}

		// 标记订单已核销 (增加已发放计数)
		// 使用 GORM 表达式更新，确保并发安全
		// update douyin_orders set reward_granted = reward_granted + 1, updated_at = now() where id = ?
		if err := db.Model(&order).Update("reward_granted", gorm.Expr("reward_granted + 1")).Error; err != nil {
			h.logger.Error("[Draw] 更新订单发放状态失败", zap.String("order_id", order.ShopOrderID), zap.Error(err))
			// 注意：这里虽然更新失败，但已执行抽奖，可能会导致用户少一次抽奖机会（计数没加），但为了防止超发，宁可少发。
			// 理想情况是放在事务中，但 livestream.Draw 内部可能有独立事务。
		}

		res := &drawResponse{
			PrizeID:      result.Prize.ID,
			PrizeName:    result.Prize.Name,
			PrizeImage:   result.Prize.Image,
			Level:        result.Prize.Level,
			SeedHash:     result.SeedHash,
			UserNickname: order.UserNickname,
		}

		// 填充凭证信息
		if result.Receipt != nil {
			res.Receipt = &drawReceipt{
				SeedVersion: result.Receipt.SeedVersion,
				Timestamp:   result.Receipt.Timestamp,
				Nonce:       result.Receipt.Nonce,
				Signature:   result.Receipt.Signature,
				Algorithm:   result.Receipt.Algorithm,
			}
		}

		ctx.Payload(res)
	}
}

// SyncLivestreamOrders 触发全店订单同步并尝试发奖
func (h *handler) SyncLivestreamOrders() core.HandlerFunc {
	return func(ctx core.Context) {
		accessCode := ctx.Param("access_code")
		if accessCode == "" {
			ctx.AbortWithError(core.Error(http.StatusBadRequest, 10001, "访问码不能为空"))
			return
		}

		// 调用服务执行全量扫描 (基于时间更新，覆盖最近1小时变化)
		result, err := h.douyin.SyncAllOrders(ctx.RequestContext(), 1*time.Hour)
		if err != nil {
			ctx.AbortWithError(core.Error(http.StatusInternalServerError, 10004, err.Error()))
			return
		}

		ctx.Payload(map[string]any{
			"message":       "同步完成",
			"total_fetched": result.TotalFetched,
			"new_orders":    result.NewOrders,
			"matched_users": result.MatchedUsers,
		})
	}
}

// GetLivestreamPendingOrders 获取当前活动的待抽奖订单（严格模式：防止窜台）
func (h *handler) GetLivestreamPendingOrders() core.HandlerFunc {
	return func(ctx core.Context) {
		accessCode := ctx.Param("access_code")

		if accessCode == "" {
			ctx.AbortWithError(core.Error(http.StatusBadRequest, 10001, "访问码不能为空"))
			return
		}

		// ✅ 新增：获取活动信息，获取绑定的产品ID
		activity, err := h.livestream.GetActivityByAccessCode(ctx.RequestContext(), accessCode)
		if err != nil {
			ctx.AbortWithError(core.Error(http.StatusNotFound, 10002, "活动不存在或已结束"))
			return
		}

		// ✅ 严格模式：如果活动未绑定产品ID，返回空列表，防止"窜台"
		if activity.DouyinProductID == "" {
			h.logger.Warn("[GetPendingOrders] 活动未绑定产品ID，返回空列表（防止窜台）",
				zap.String("access_code", accessCode),
				zap.Int64("activity_id", activity.ID))

			// 返回空列表
			type OrderWithBlacklist struct {
				model.DouyinOrders
				IsBlacklisted bool `json:"is_blacklisted"`
			}
			ctx.Payload([]OrderWithBlacklist{})
			return
		}

		// [核心优化] 自动同步：每次拉取待抽奖列表前，静默执行一次快速全局扫描 (最近 10 分钟)
		_, _ = h.douyin.SyncAllOrders(ctx.RequestContext(), 10*time.Minute)

		// ✅ 修改：添加产品ID过滤条件（核心修复，防止不同活动订单窜台）
		var pendingOrders []model.DouyinOrders
		db := h.repo.GetDbR().WithContext(ctx.RequestContext())

		err = db.Where("order_status = 2 AND reward_granted < product_count AND douyin_product_id = ?",
			activity.DouyinProductID).
			Find(&pendingOrders).Error

		if err != nil {
			ctx.AbortWithError(core.Error(http.StatusInternalServerError, 10003, err.Error()))
			return
		}

		// 查询黑名单用户
		blacklistMap := make(map[string]bool)
		if len(pendingOrders) > 0 {
			var douyinUserIDs []string
			for _, order := range pendingOrders {
				if order.DouyinUserID != "" {
					douyinUserIDs = append(douyinUserIDs, order.DouyinUserID)
				}
			}
			if len(douyinUserIDs) > 0 {
				var blacklistUsers []model.DouyinBlacklist
				db.Table("douyin_blacklist").
					Where("douyin_user_id IN ? AND status = 1", douyinUserIDs).
					Find(&blacklistUsers)
				for _, bl := range blacklistUsers {
					blacklistMap[bl.DouyinUserID] = true
				}
			}
		}

		// 构造响应，包含黑名单状态
		type OrderWithBlacklist struct {
			model.DouyinOrders
			IsBlacklisted bool `json:"is_blacklisted"`
		}

		result := make([]OrderWithBlacklist, len(pendingOrders))
		for i, order := range pendingOrders {
			result[i] = OrderWithBlacklist{
				DouyinOrders:  order,
				IsBlacklisted: blacklistMap[order.DouyinUserID],
			}
		}

		ctx.Payload(result)
	}
}
