package app

import (
	"bindbox-game/configs"
	"bindbox-game/internal/pkg/wechat"
	"bindbox-game/internal/repository/mysql/model"
	activitysvc "bindbox-game/internal/service/activity"
	usersvc "bindbox-game/internal/service/user"
	"context"
	"fmt"
	"strings"
	"sync"
	"time"

	"go.uber.org/zap"
)

// grantRewardHelper 发放奖励辅助函数
func (h *handler) grantRewardHelper(ctx context.Context, userID, orderID int64, r *model.ActivityRewardSettings, quantity int, remark string) error {
	// 1. Grant to Order (Delegating stock check to user service)
	issue, _ := h.readDB.ActivityIssues.WithContext(ctx).Where(h.readDB.ActivityIssues.ID.Eq(r.IssueID)).First()
	var actID int64
	if issue != nil {
		actID = issue.ActivityID
	}

	rid := r.ID
	_, err := h.user.GrantRewardToOrder(ctx, userID, usersvc.GrantRewardToOrderRequest{
		OrderID:    orderID,
		ProductID:  r.ProductID,
		Quantity:   quantity,
		ActivityID: &actID,
		RewardID:   &rid,
		Remark:     remark,
	})
	if err != nil {
		h.logger.Error("Failed to grant reward to order", zap.Int64("order_id", orderID), zap.Error(err))
		return err
	}

	// 2. Update Draw Log (IsWinner = 1)
	_, err = h.writeDB.ActivityDrawLogs.WithContext(ctx).Where(
		h.writeDB.ActivityDrawLogs.OrderID.Eq(orderID),
	).Updates(&model.ActivityDrawLogs{
		IsWinner: 1,
		RewardID: r.ID,
		Level:    r.Level,
	})

	return err
}

var matchingCleanupOnce sync.Once

func (h *handler) startMatchingGameCleanup() {
	matchingCleanupOnce.Do(func() {
		go func() {
			h.logger.Info("对对碰自动开奖: 后台任务已启动")
			ticker := time.NewTicker(3 * time.Minute) // 每 3 分钟扫描一次
			defer ticker.Stop()
			for range ticker.C {
				h.autoCheckExpiredGames()
			}
		}()
	})
}

// autoCheckExpiredGames 扫描超时未结算的对对碰游戏并自动开奖
func (h *handler) autoCheckExpiredGames() {
	ctx := context.Background()

	// 1. 扫描 Redis 中所有 matching_game key
	keys, err := h.redis.Keys(ctx, activitysvc.MatchingGameKeyPrefix+"*").Result()
	if err != nil {
		h.logger.Error("对对碰自动开奖: 扫描 Redis 失败", zap.Error(err))
		return
	}

	if len(keys) == 0 {
		return
	}

	h.logger.Debug("对对碰自动开奖: 扫描到游戏会话", zap.Int("count", len(keys)))

	for _, key := range keys {
		gameID := strings.TrimPrefix(key, activitysvc.MatchingGameKeyPrefix)
		if gameID == "" {
			continue
		}

		game, err := h.activity.GetMatchingGameFromRedis(ctx, gameID)
		if err != nil {
			continue
		}

		// 2. 检查是否超过 5 分钟
		if game.CreatedAt.IsZero() || time.Since(game.CreatedAt) < 5*time.Minute {
			continue
		}

		// 3. 检查用户是否还在活跃（最近 2 分钟内有操作则跳过）
		if !game.LastActivity.IsZero() && time.Since(game.LastActivity) < 2*time.Minute {
			h.logger.Debug("对对碰自动开奖: 用户仍在活跃，跳过",
				zap.String("game_id", gameID),
				zap.Time("last_activity", game.LastActivity))
			continue
		}

		// 3. 检查订单是否已支付
		order, err := h.readDB.Orders.WithContext(ctx).Where(h.readDB.Orders.ID.Eq(game.OrderID)).First()
		if err != nil || order == nil || order.Status != 2 {
			continue
		}

		// 4. 检查是否已结算（幂等）
		existingLog, _ := h.readDB.ActivityDrawLogs.WithContext(ctx).Where(
			h.readDB.ActivityDrawLogs.OrderID.Eq(game.OrderID),
			h.readDB.ActivityDrawLogs.IsWinner.Eq(1),
		).First()
		if existingLog != nil {
			// 已结算，清理 Redis
			_ = h.redis.Del(ctx, key)
			continue
		}

		// 5. 执行自动结算
		h.logger.Info("对对碰自动开奖: 触发超时自动结算",
			zap.String("game_id", gameID),
			zap.Int64("order_id", game.OrderID),
			zap.Int64("user_id", game.UserID),
			zap.Time("created_at", game.CreatedAt),
			zap.Duration("elapsed", time.Since(game.CreatedAt)))

		h.doAutoCheck(ctx, gameID, game, order)
	}
}

// doAutoCheck 执行自动开奖结算（复用 CheckMatchingGame 核心逻辑）
func (h *handler) doAutoCheck(ctx context.Context, gameID string, game *activitysvc.MatchingGame, order *model.Orders) {
	// 1. 并发锁
	lockKey := fmt.Sprintf("lock:matching_game:check:%s", gameID)
	locked, err := h.redis.SetNX(ctx, lockKey, "1", 10*time.Second).Result()
	if err != nil || !locked {
		return
	}
	defer h.redis.Del(ctx, lockKey)

	// 2. 检查活动状态
	activity, err := h.activity.GetActivity(ctx, game.ActivityID)
	if err != nil || activity == nil || activity.Status != 1 {
		h.logger.Warn("对对碰自动开奖: 活动状态异常", zap.Int64("activity_id", game.ActivityID))
		return
	}

	// 3. 服务端模拟计算对数
	serverSimulatedPairs := game.SimulateMaxPairs()
	h.logger.Debug("对对碰自动开奖: 服务端模拟",
		zap.Int64("simulated_pairs", serverSimulatedPairs),
		zap.String("position", game.Position))

	game.TotalPairs = serverSimulatedPairs

	// 4. 发放奖励
	var rewardInfo *MatchingRewardInfo
	rewards, err := h.activity.ListIssueRewards(ctx, game.IssueID)
	if err == nil && len(rewards) > 0 {
		var candidate *model.ActivityRewardSettings
		for _, r := range rewards {
			if r.Quantity <= 0 {
				continue
			}
			if serverSimulatedPairs == r.MinScore {
				candidate = r
				break
			}
		}

		if candidate != nil {
			productName := ""
			if p, _ := h.readDB.Products.WithContext(ctx).Where(h.readDB.Products.ID.Eq(candidate.ProductID)).First(); p != nil {
				productName = p.Name
			}

			finalRemark := fmt.Sprintf("%s %s (自动开奖)", order.OrderNo, productName)

			if err := h.grantRewardHelper(ctx, game.UserID, game.OrderID, candidate, 1, finalRemark); err != nil {
				h.logger.Error("对对碰自动开奖: 发放奖励失败", zap.Int64("order_id", game.OrderID), zap.Error(err))
			} else {
				prodImage := ""
				if p, _ := h.readDB.Products.WithContext(ctx).Where(h.readDB.Products.ID.Eq(candidate.ProductID)).First(); p != nil {
					prodImage = getFirstImage(p.ImagesJSON)
				}

				rewardInfo = &MatchingRewardInfo{
					RewardID:     candidate.ID,
					Name:         productName,
					ProductName:  productName,
					ProductImage: prodImage,
					Level:        candidate.Level,
				}

				h.logger.Info("对对碰自动开奖: 奖励发放成功",
					zap.Int64("order_id", game.OrderID),
					zap.String("product_name", productName),
					zap.Int32("level", candidate.Level))
			}
		}
	}

	// 5. 虚拟发货
	rewardName := "无奖励"
	if rewardInfo != nil {
		rewardName = rewardInfo.Name
	}

	go func(orderID int64, orderNo string, userID int64, rName string) {
		bgCtx := context.Background()
		tx, _ := h.readDB.PaymentTransactions.WithContext(bgCtx).Where(h.readDB.PaymentTransactions.OrderNo.Eq(orderNo)).First()
		if tx == nil || tx.TransactionID == "" {
			return
		}

		u, _ := h.readDB.Users.WithContext(bgCtx).Where(h.readDB.Users.ID.Eq(userID)).First()
		payerOpenid := ""
		if u != nil {
			payerOpenid = u.Openid
		}

		itemsDesc := fmt.Sprintf("对对碰 %s 赏品: %s (自动开奖)", orderNo, rName)
		if len(itemsDesc) > 120 {
			itemsDesc = itemsDesc[:120]
		}

		c := configs.Get()
		if err := wechat.UploadVirtualShippingForBackground(bgCtx, &wechat.WechatConfig{AppID: c.Wechat.AppID, AppSecret: c.Wechat.AppSecret}, tx.TransactionID, orderNo, payerOpenid, itemsDesc); err != nil {
			h.logger.Error("对对碰自动开奖: 虚拟发货失败", zap.Error(err))
		} else {
			h.logger.Info("对对碰自动开奖: 虚拟发货成功", zap.String("order_no", orderNo))
		}
	}(game.OrderID, order.OrderNo, game.UserID, rewardName)

	// 6. 清理 Redis 会话
	_ = h.redis.Del(ctx, activitysvc.MatchingGameKeyPrefix+gameID)

	h.logger.Info("对对碰自动开奖: 结算完成",
		zap.String("game_id", gameID),
		zap.Int64("order_id", game.OrderID),
		zap.Int64("total_pairs", serverSimulatedPairs))
}
