package douyin

import (
	"bindbox-game/internal/pkg/logger"
	"bindbox-game/internal/repository/mysql"
	"bindbox-game/internal/repository/mysql/model"
	"bindbox-game/internal/service/game"
	"bindbox-game/internal/service/sysconfig"
	"context"
	"fmt"
	"strconv"
	"time"

	"go.uber.org/zap"

	"bindbox-game/internal/service/user"
)

// StartDouyinOrderSync 启动抖店订单定时同步任务
func StartDouyinOrderSync(l logger.CustomLogger, repo mysql.Repo, syscfg sysconfig.Service, ticketSvc game.TicketService, userSvc user.Service, titleSvc TitleAssigner) {
	svc := New(l, repo, syscfg, ticketSvc, userSvc, titleSvc)

	go func() {
		// 初始等待30秒让服务完全启动
		time.Sleep(30 * time.Second)

		firstRun := true
		for {
			ctx := context.Background()

			// 获取同步间隔配置
			intervalMinutes := 5 // 默认5分钟
			if c, err := syscfg.GetByKey(ctx, ConfigKeyDouyinInterval); err == nil && c != nil {
				if v, e := strconv.Atoi(c.ConfigValue); e == nil && v > 0 {
					intervalMinutes = v
				}
			}

			// 检查是否配置了 Cookie
			cookieCfg, err := syscfg.GetByKey(ctx, ConfigKeyDouyinCookie)
			if err != nil || cookieCfg == nil || cookieCfg.ConfigValue == "" {
				l.Debug("[抖店定时同步] Cookie 未配置，跳过本次同步")
				time.Sleep(time.Duration(intervalMinutes) * time.Minute)
				continue
			}

			l.Info("[抖店定时同步] 开始同步", zap.Int("interval_minutes", intervalMinutes))

			// ========== 优先：按用户同步 (Only valid users) ==========
			// “优先遍历：代码先查 users 表中所有已绑定抖音的用户。 然后根据抖音id 去请求抖音的订单接口拿数据”
			result, err := svc.FetchAndSyncOrders(ctx)
			if err != nil {
				l.Error("[抖店定时同步] 用户订单同步失败", zap.Error(err))
			} else {
				l.Info("[抖店定时同步] 用户订单同步成功",
					zap.Int("total_fetched", result.TotalFetched),
					zap.Int("new_orders", result.NewOrders),
					zap.Int("matched_users", result.MatchedUsers),
				)
			}

			// ========== 自动补发扫雷游戏资格 (针对刚才同步到的订单) ==========
			// [修复] 禁用自动补发逻辑，防止占用直播间抽奖配额
			// if err := svc.GrantMinesweeperQualifications(ctx); err != nil {
			// 	l.Error("[定时补发] 补发扫雷资格失败", zap.Error(err))
			// }

			// ========== 自动发放直播间奖品 ==========
			if err := svc.GrantLivestreamPrizes(ctx); err != nil {
				l.Error("[定时发放] 发放直播奖品失败", zap.Error(err))
			}

			// ========== 核心：批量同步最近所有订单变更 (基于更新时间，不分状态) ==========
			// 首次运行同步最近 48 小时以修复潜在的历史遗漏，之后同步最近 1 小时
			syncDuration := 1 * time.Hour
			if firstRun {
				syncDuration = 48 * time.Hour
			}
			if res, err := svc.SyncAllOrders(ctx, syncDuration); err != nil {
				l.Error("[定时同步] 全量同步失败", zap.Error(err))
			} else {
				l.Info("[定时同步] 全量同步完成", zap.String("info", res.DebugInfo))
			}
			firstRun = false

			// ========== 新增：同步退款状态 ==========
			if err := svc.SyncRefundStatus(ctx); err != nil {
				l.Error("[定时同步] 同步退款状态失败", zap.Error(err))
			}

			// 等待下次同步
			time.Sleep(time.Duration(intervalMinutes) * time.Minute)
		}
	}()

	l.Info("[抖店定时同步] 定时任务已启动")
}

// GrantMinesweeperQualifications 自动补发扫雷资格
// 逻辑：遍历已绑定抖音的用户 -> 查找其未归属的订单 -> 关联订单 -> 补发资格
func (s *service) GrantMinesweeperQualifications(ctx context.Context) error {
	db := s.repo.GetDbW().WithContext(ctx)

	// 1. 查找所有已绑定抖音的用户
	var users []model.Users
	if err := s.repo.GetDbR().Where("douyin_user_id != '' AND douyin_user_id IS NOT NULL").Find(&users).Error; err != nil {
		return err
	}

	for _, u := range users {
		// 1.1 检查是否在黑名单中
		var blacklistCount int64
		if err := s.repo.GetDbR().Table("douyin_blacklist").Where("douyin_user_id = ?", u.DouyinUserID).Count(&blacklistCount).Error; err == nil && blacklistCount > 0 {
			continue
		}

		// 2. 查找该抖音ID下未关联(local_user_id=0 or empty)的订单
		var orders []model.DouyinOrders
		if err := db.Where("douyin_user_id = ? AND (local_user_id = '' OR local_user_id = '0')", u.DouyinUserID).Find(&orders).Error; err != nil {
			continue
		}

		for _, order := range orders {
			// 3. 关联订单到用户
			if err := db.Model(&order).Update("local_user_id", strconv.FormatInt(u.ID, 10)).Error; err != nil {
				s.logger.Error("[自动补发] 关联订单失败", zap.String("order_id", order.ShopOrderID), zap.Error(err))
				continue
			}

			// 4. 如果是已支付待发货的订单(2)，且未发奖，则补发
			if order.OrderStatus == 2 && order.RewardGranted == 0 {
				orderID := order.ID
				s.logger.Info("[自动补发] 开始补发扫雷资格", zap.Int64("user_id", u.ID), zap.String("shop_order_id", order.ShopOrderID))

				// 调用发奖服务
				count := int64(1)
				if order.ProductCount > 0 {
					count = int64(order.ProductCount)
				}
				s.logger.Info("[自动补发] 发放数量", zap.Int64("count", count))

				if err := s.ticketSvc.GrantTicket(ctx, u.ID, "minesweeper", int(count), "douyin_order", orderID, "定时任务补发"); err == nil {
					db.Model(&order).Update("reward_granted", int32(count))
					s.logger.Info("[自动补发] 补发成功", zap.String("shop_order_id", order.ShopOrderID))
				} else {
					s.logger.Error("[自动补发] 补发失败", zap.Error(err))
				}
			}
		}
	}
	return nil
}

// GrantLivestreamPrizes 自动发放直播间奖品
// 逻辑：扫描 livestream_draw_logs 中 is_granted=0 的记录 -> 找到对应 ProductID -> 发放商品
func (s *service) GrantLivestreamPrizes(ctx context.Context) error {
	db := s.repo.GetDbW().WithContext(ctx)

	// 1. 查找未发放的记录
	var logs []model.LivestreamDrawLogs
	if err := db.Where("is_granted = 0").Find(&logs).Error; err != nil {
		return err
	}

	for _, log := range logs {
		// 必须要有对应的本地用户ID
		if log.LocalUserID == 0 {
			// 尝试从 douyin_orders 补全 user_id
			var order model.DouyinOrders
			if err := s.repo.GetDbR().Where("shop_order_id = ?", log.ShopOrderID).First(&order).Error; err == nil {
				if uid, _ := strconv.ParseInt(order.LocalUserID, 10, 64); uid > 0 {
					log.LocalUserID = uid
					db.Model(&log).Update("local_user_id", uid)
				}
			}
		}

		if log.LocalUserID == 0 {
			continue // 还没关联到用户，跳过
		}

		// 2. 查奖品关联的 ProductID
		var prize model.LivestreamPrizes
		if err := s.repo.GetDbR().Where("id = ?", log.PrizeID).First(&prize).Error; err != nil {
			s.logger.Error("[自动发放] 奖品不存在", zap.Int64("prize_id", log.PrizeID))
			continue
		}

		if prize.ProductID == 0 {
			s.logger.Warn("[自动发放] 奖品未关联商品ID，跳过", zap.Int64("prize_id", log.PrizeID), zap.String("name", prize.Name))
			continue
		}

		// 3. 发放商品 (使用 GrantReward 创建新订单发放)
		sourceType := int32(5) // 5 代表直播间
		req := user.GrantRewardRequest{
			ProductID:  prize.ProductID,
			Quantity:   1,
			SourceType: &sourceType,
			Remark:     fmt.Sprintf("直播间抽奖: %s (关联抖店订单: %s)", log.PrizeName, log.ShopOrderID),
		}

		s.logger.Info("[自动发放] 开始发放直播商品",
			zap.Int64("user_id", log.LocalUserID),
			zap.Int64("product_id", prize.ProductID),
			zap.String("prize", log.PrizeName),
		)

		res, err := s.userSvc.GrantReward(ctx, log.LocalUserID, req)
		if err != nil {
			s.logger.Error("[自动发放] 发放失败", zap.Error(err))
			// 如果发放失败是库存原因等，可能需要告警。暂时不重试，等下个周期。
		} else {
			// 4. 更新发放状态
			db.Model(&log).Update("is_granted", 1)
			s.logger.Info("[自动发放] 发放成功", zap.Int64("log_id", log.ID))

			// 5. 自动虚拟发货 (本地状态更新)
			// 直播间奖品通常为虚拟发货，直接标记为已消费/已发货
			if res != nil && res.OrderID > 0 {
				updates := map[string]interface{}{
					"is_consumed": 1,
					"updated_at":  time.Now(),
				}
				if err := s.repo.GetDbW().WithContext(ctx).Model(&model.Orders{}).Where("id = ?", res.OrderID).Updates(updates).Error; err != nil {
					s.logger.Error("[自动发放] 更新订单状态失败", zap.Int64("order_id", res.OrderID), zap.Error(err))
				}

				// 更新发货记录
				shippingUpdates := map[string]interface{}{
					"status":     2, // 已发货
					"shipped_at": time.Now(),
					"updated_at": time.Now(),
				}
				if err := s.repo.GetDbW().WithContext(ctx).Model(&model.ShippingRecords{}).Where("order_id = ?", res.OrderID).Updates(shippingUpdates).Error; err != nil {
					s.logger.Error("[自动发放] 更新发货记录失败", zap.Int64("order_id", res.OrderID), zap.Error(err))
				} else {
					s.logger.Info("[自动发放] 虚拟发货完成(本地)", zap.Int64("order_id", res.OrderID))
				}
			}
		}
	}
	return nil
}

// SyncRefundStatus 同步退款状态
// 逻辑：检查 douyin_orders 的状态变更，如果订单已退款，则标记对应的 livestream_draw_logs
func (s *service) SyncRefundStatus(ctx context.Context) error {
	db := s.repo.GetDbW().WithContext(ctx)

	// 1. 查找所有关联直播抽奖但尚未标记退款的记录
	var logs []model.LivestreamDrawLogs
	if err := db.Where("is_refunded = 0 AND shop_order_id != ''").Find(&logs).Error; err != nil {
		return err
	}

	refundedCount := 0
	for _, log := range logs {
		// 2. 查找对应的抖店订单
		var order model.DouyinOrders
		if err := s.repo.GetDbR().Where("shop_order_id = ?", log.ShopOrderID).First(&order).Error; err != nil {
			continue // 找不到订单，跳过
		}

		// 3. 检查订单状态：抖店状态 4=已关闭 (包含退款/取消等关闭情况)
		// 状态说明: 3=已发货, 4=已关闭, 5=已完成
		if order.OrderStatus == 4 {
			db.Model(&log).Update("is_refunded", 1)
			refundedCount++
			s.logger.Info("[退款同步] 标记退款记录",
				zap.Int64("draw_log_id", log.ID),
				zap.String("shop_order_id", log.ShopOrderID),
				zap.Int32("order_status", order.OrderStatus),
			)

			// 4. 如果用户已关联，回收资产
			if log.LocalUserID > 0 {
				s.reclaimLivestreamAssets(ctx, &log)
			}
		}
	}

	if refundedCount > 0 {
		s.logger.Info("[退款同步] 本次同步完成", zap.Int("refunded_count", refundedCount))
	}
	return nil
}

// reclaimLivestreamAssets 回收直播间发放的资产
// 逻辑：查找该用户通过此抽奖获得的 user_inventory，作废或扣除积分
func (s *service) reclaimLivestreamAssets(ctx context.Context, log *model.LivestreamDrawLogs) {
	db := s.repo.GetDbW().WithContext(ctx)

	// 1. 查找关联的 user_inventory 记录
	// 直播间奖品是通过 GrantReward 发放的，会创建一个新的本地订单
	// 我们需要通过 remark 或其他方式找到关联的 inventory
	// 由于 GrantReward 会在 remark 中记录 shop_order_id，我们通过这个来查找

	var inventories []model.UserInventory
	// 查找用户持有的、来自直播间的资产（通过 remark 包含 shop_order_id 来关联）
	searchPattern := "%" + log.ShopOrderID + "%"
	if err := db.Where("user_id = ? AND status IN (1, 3) AND remark LIKE ?", log.LocalUserID, searchPattern).Find(&inventories).Error; err != nil {
		s.logger.Error("[资产回收] 查询资产失败", zap.Error(err), zap.Int64("user_id", log.LocalUserID))
		return
	}

	if len(inventories) == 0 {
		// 尝试通过 prize_id 和 product_id 关联查找
		var prize model.LivestreamPrizes
		if err := s.repo.GetDbR().Where("id = ?", log.PrizeID).First(&prize).Error; err == nil && prize.ProductID > 0 {
			// 查找该用户最近获得的该商品的资产（时间相近）
			db.Where("user_id = ? AND product_id = ? AND status IN (1, 3) AND created_at >= ?",
				log.LocalUserID, prize.ProductID, log.CreatedAt.Add(-time.Hour)).
				Order("created_at DESC").Limit(1).Find(&inventories)
		}
	}

	if len(inventories) == 0 {
		s.logger.Warn("[资产回收] 未找到可回收资产",
			zap.Int64("user_id", log.LocalUserID),
			zap.String("shop_order_id", log.ShopOrderID),
		)
		return
	}

	// 2. 回收资产
	for _, inv := range inventories {
		if inv.Status == 1 {
			// 状态1（持有）：作废
			db.Model(&inv).Updates(map[string]any{
				"status": 2,
				"remark": inv.Remark + "|refund_reclaimed",
			})
			s.logger.Info("[资产回收] 作废持有资产",
				zap.Int64("inventory_id", inv.ID),
				zap.Int64("user_id", inv.UserID),
			)
		} else if inv.Status == 3 {
			// 状态3（已兑换/发货）：扣除积分
			// 查找商品价格作为积分扣除依据
			var product model.Products
			if err := s.repo.GetDbR().Where("id = ?", inv.ProductID).First(&product).Error; err == nil {
				pointsToDeduct := product.Price / 100 // 分转换为积分（假设 1积分=1分钱）
				if pointsToDeduct > 0 {
					_, consumed, err := s.userSvc.ConsumePointsForRefund(ctx, inv.UserID, pointsToDeduct, "user_inventory", fmt.Sprintf("%d", inv.ID), "直播退款回收已兑换资产")
					if err != nil {
						s.logger.Error("[资产回收] 扣除积分失败", zap.Error(err), zap.Int64("user_id", inv.UserID))
					}
					if consumed < pointsToDeduct {
						// 积分不足，标记用户
						s.logger.Warn("[资产回收] 用户积分不足",
							zap.Int64("user_id", inv.UserID),
							zap.Int64("needed", pointsToDeduct),
							zap.Int64("consumed", consumed),
						)
						// 可选：加入黑名单
						// db.Exec("UPDATE users SET status = 3 WHERE id = ?", inv.UserID)
					}
				}
			}
			// 作废记录
			db.Model(&inv).Updates(map[string]any{
				"status": 2,
				"remark": inv.Remark + "|refund_reclaimed_points_deducted",
			})
			s.logger.Info("[资产回收] 扣除积分并作废",
				zap.Int64("inventory_id", inv.ID),
				zap.Int64("user_id", inv.UserID),
			)
		}
	}

	// 3. 恢复奖品库存
	db.Exec("UPDATE livestream_prizes SET remaining = remaining + 1 WHERE id = ? AND remaining >= 0", log.PrizeID)
	s.logger.Info("[资产回收] 恢复奖品库存", zap.Int64("prize_id", log.PrizeID))
}
