// 任务中心配置组合测试工具
// 功能：
// 1. 生成所有有效的任务配置组合到 MySQL 数据库
// 2. 模拟用户任务进度
// 3. 验证任务功能是否正常

package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"log"
	"os"
	"time"

	"bindbox-game/configs"
	"bindbox-game/internal/repository/mysql"
	tcmodel "bindbox-game/internal/repository/mysql/task_center"

	"gorm.io/datatypes"
)

// ================================
// 常量定义
// ================================

const (
	// 任务指标
	MetricFirstOrder  = "first_order"
	MetricOrderCount  = "order_count"
	MetricOrderAmount = "order_amount"
	MetricInviteCount = "invite_count"

	// 操作符
	OperatorGTE = ">="
	OperatorEQ  = "="

	// 时间窗口
	WindowDaily    = "daily"
	WindowWeekly   = "weekly"
	WindowMonthly  = "monthly"
	WindowLifetime = "lifetime"

	// 奖励类型
	RewardTypePoints     = "points"
	RewardTypeCoupon     = "coupon"
	RewardTypeItemCard   = "item_card"
	RewardTypeTitle      = "title"
	RewardTypeGameTicket = "game_ticket"
)

// TaskCombination 表示一种任务配置组合
type TaskCombination struct {
	Name       string
	Metric     string
	Operator   string
	Threshold  int64
	Window     string
	RewardType string
}

// TestResult 测试结果
type TestResult struct {
	Name    string
	Passed  bool
	Message string
}

// ================================
// 配置组合生成器
// ================================

// GenerateAllCombinations 生成所有有效的任务配置组合
func GenerateAllCombinations() []TaskCombination {
	metrics := []struct {
		name      string
		operators []string
		threshold int64
	}{
		{MetricFirstOrder, []string{OperatorEQ}, 1},
		{MetricOrderCount, []string{OperatorGTE, OperatorEQ}, 3},
		{MetricOrderAmount, []string{OperatorGTE, OperatorEQ}, 10000},
		{MetricInviteCount, []string{OperatorGTE, OperatorEQ}, 2},
	}
	windows := []string{WindowDaily, WindowWeekly, WindowMonthly, WindowLifetime}
	rewards := []string{RewardTypePoints, RewardTypeCoupon, RewardTypeItemCard, RewardTypeTitle, RewardTypeGameTicket}

	var combinations []TaskCombination
	idx := 0
	for _, m := range metrics {
		for _, op := range m.operators {
			for _, w := range windows {
				for _, r := range rewards {
					idx++
					combinations = append(combinations, TaskCombination{
						Name:       fmt.Sprintf("测试任务%03d_%s_%s_%s", idx, m.name, w, r),
						Metric:     m.name,
						Operator:   op,
						Threshold:  m.threshold,
						Window:     w,
						RewardType: r,
					})
				}
			}
		}
	}
	return combinations
}

// generateRewardPayload 根据奖励类型生成对应的 JSON payload
func generateRewardPayload(rewardType string) string {
	switch rewardType {
	case RewardTypePoints:
		return `{"points": 100}`
	case RewardTypeCoupon:
		return `{"coupon_id": 1, "quantity": 1}`
	case RewardTypeItemCard:
		return `{"card_id": 1, "quantity": 1}`
	case RewardTypeTitle:
		return `{"title_id": 1}`
	case RewardTypeGameTicket:
		return `{"game_code": "minesweeper", "amount": 5}`
	default:
		return `{}`
	}
}

// ================================
// 数据库操作
// ================================

// SeedAllCombinations 将所有配置组合写入数据库
func SeedAllCombinations(repo mysql.Repo, dryRun bool) error {
	db := repo.GetDbW()
	combos := GenerateAllCombinations()

	fmt.Printf("准备生成 %d 个任务配置组合\n", len(combos))
	if dryRun {
		fmt.Println("【试运行模式】不会实际写入数据库")
		for i, c := range combos {
			fmt.Printf("  %3d. %s (指标=%s, 操作符=%s, 窗口=%s, 奖励=%s)\n",
				i+1, c.Name, c.Metric, c.Operator, c.Window, c.RewardType)
		}
		return nil
	}

	// 开始事务
	tx := db.Begin()
	defer func() {
		if r := recover(); r != nil {
			tx.Rollback()
		}
	}()

	// 清理旧的测试数据
	if err := tx.Where("name LIKE ?", "测试任务%").Delete(&tcmodel.Task{}).Error; err != nil {
		tx.Rollback()
		return fmt.Errorf("清理旧任务失败: %v", err)
	}
	fmt.Println("已清理旧的测试任务数据")

	created := 0
	for _, combo := range combos {
		// 检查是否已存在
		var exists tcmodel.Task
		if err := tx.Where("name = ?", combo.Name).First(&exists).Error; err == nil {
			fmt.Printf("  跳过: %s (已存在)\n", combo.Name)
			continue
		}

		// 插入任务
		task := &tcmodel.Task{
			Name:        combo.Name,
			Description: fmt.Sprintf("测试 %s + %s + %s + %s", combo.Metric, combo.Operator, combo.Window, combo.RewardType),
			Status:      1,
			Visibility:  1,
		}
		if err := tx.Create(task).Error; err != nil {
			tx.Rollback()
			return fmt.Errorf("插入任务失败: %v", err)
		}

		// 插入档位
		tier := &tcmodel.TaskTier{
			TaskID:    task.ID,
			Metric:    combo.Metric,
			Operator:  combo.Operator,
			Threshold: combo.Threshold,
			Window:    combo.Window,
			Priority:  0,
		}
		if err := tx.Create(tier).Error; err != nil {
			tx.Rollback()
			return fmt.Errorf("插入档位失败: %v", err)
		}

		// 插入奖励
		payload := generateRewardPayload(combo.RewardType)
		reward := &tcmodel.TaskReward{
			TaskID:        task.ID,
			TierID:        tier.ID,
			RewardType:    combo.RewardType,
			RewardPayload: datatypes.JSON(payload),
			Quantity:      10,
		}
		if err := tx.Create(reward).Error; err != nil {
			tx.Rollback()
			return fmt.Errorf("插入奖励失败: %v", err)
		}

		created++
		if created%10 == 0 {
			fmt.Printf("  已创建 %d 个任务...\n", created)
		}
	}

	if err := tx.Commit().Error; err != nil {
		return fmt.Errorf("提交事务失败: %v", err)
	}

	fmt.Printf("✅ 成功创建 %d 个任务配置组合\n", created)
	return nil
}

// ================================
// 模拟用户任务
// ================================

// SimulateUserTask 模拟用户完成任务
func SimulateUserTask(repo mysql.Repo, userID int64, taskID int64) error {
	db := repo.GetDbW()

	// 查询任务和档位
	var task tcmodel.Task
	if err := db.Where("id = ?", taskID).First(&task).Error; err != nil {
		return fmt.Errorf("任务不存在: %v", err)
	}

	var tier tcmodel.TaskTier
	if err := db.Where("task_id = ?", taskID).First(&tier).Error; err != nil {
		return fmt.Errorf("档位不存在: %v", err)
	}

	fmt.Printf("模拟任务: %s (指标=%s, 阈值=%d)\n", task.Name, tier.Metric, tier.Threshold)

	// 创建或更新用户进度
	progress := &tcmodel.UserTaskProgress{
		UserID:       userID,
		TaskID:       taskID,
		ClaimedTiers: datatypes.JSON("[]"),
	}

	// 根据指标类型设置进度
	switch tier.Metric {
	case MetricFirstOrder:
		progress.FirstOrder = 1
		progress.OrderCount = 1
		progress.OrderAmount = 10000
	case MetricOrderCount:
		progress.OrderCount = tier.Threshold
	case MetricOrderAmount:
		progress.OrderAmount = tier.Threshold
		progress.OrderCount = 1
	case MetricInviteCount:
		progress.InviteCount = tier.Threshold
	}

	// Upsert
	if err := db.Where("user_id = ? AND task_id = ?", userID, taskID).
		Assign(progress).
		FirstOrCreate(progress).Error; err != nil {
		return fmt.Errorf("创建进度失败: %v", err)
	}

	fmt.Printf("✅ 用户 %d 的任务进度已更新: order_count=%d, order_amount=%d, invite_count=%d, first_order=%d\n",
		userID, progress.OrderCount, progress.OrderAmount, progress.InviteCount, progress.FirstOrder)

	return nil
}

// ================================
// 验证功能
// ================================

// VerifyAllConfigs 验证所有配置是否正确
func VerifyAllConfigs(repo mysql.Repo) []TestResult {
	db := repo.GetDbR()
	var results []TestResult

	// 1. 检查任务数量
	var taskCount int64
	var sampleTasks []tcmodel.Task
	db.Model(&tcmodel.Task{}).Where("name LIKE ?", "测试任务%").Count(&taskCount)
	db.Model(&tcmodel.Task{}).Where("name LIKE ?", "测试任务%").Limit(5).Find(&sampleTasks)

	var sampleMsg string
	for _, t := range sampleTasks {
		sampleMsg += fmt.Sprintf("[%d:%s] ", t.ID, t.Name)
	}

	results = append(results, TestResult{
		Name:    "任务数量检查",
		Passed:  taskCount > 0,
		Message: fmt.Sprintf("找到 %d 个测试任务. 样本: %s", taskCount, sampleMsg),
	})

	// 2. 检查每种指标的覆盖
	metrics := []string{MetricFirstOrder, MetricOrderCount, MetricOrderAmount, MetricInviteCount}
	for _, m := range metrics {
		var count int64
		db.Model(&tcmodel.TaskTier{}).Where("metric = ?", m).Count(&count)
		results = append(results, TestResult{
			Name:    fmt.Sprintf("指标覆盖: %s", m),
			Passed:  count > 0,
			Message: fmt.Sprintf("找到 %d 个档位使用此指标", count),
		})
	}

	// 3. 检查每种时间窗口的覆盖
	windows := []string{WindowDaily, WindowWeekly, WindowMonthly, WindowLifetime}
	for _, w := range windows {
		var count int64
		db.Model(&tcmodel.TaskTier{}).Where("window = ?", w).Count(&count)
		results = append(results, TestResult{
			Name:    fmt.Sprintf("时间窗口覆盖: %s", w),
			Passed:  count > 0,
			Message: fmt.Sprintf("找到 %d 个档位使用此时间窗口", count),
		})
	}

	// 4. 检查每种奖励类型的覆盖
	rewards := []string{RewardTypePoints, RewardTypeCoupon, RewardTypeItemCard, RewardTypeTitle, RewardTypeGameTicket}
	for _, r := range rewards {
		var count int64
		db.Model(&tcmodel.TaskReward{}).Where("reward_type = ?", r).Count(&count)
		results = append(results, TestResult{
			Name:    fmt.Sprintf("奖励类型覆盖: %s", r),
			Passed:  count > 0,
			Message: fmt.Sprintf("找到 %d 个奖励使用此类型", count),
		})
	}

	// 5. 检查奖励 payload 格式
	var rewardList []tcmodel.TaskReward
	db.Limit(20).Find(&rewardList)
	for _, r := range rewardList {
		var data map[string]interface{}
		err := json.Unmarshal([]byte(r.RewardPayload), &data)
		passed := err == nil
		msg := "JSON 格式正确"
		if err != nil {
			msg = fmt.Sprintf("JSON 解析失败: %v", err)
		}
		results = append(results, TestResult{
			Name:    fmt.Sprintf("奖励Payload格式: ID=%d, Type=%s", r.ID, r.RewardType),
			Passed:  passed,
			Message: msg,
		})
	}

	return results
}

// PrintResults 打印测试结果
func PrintResults(results []TestResult) {
	passed := 0
	failed := 0

	fmt.Println("\n========== 测试结果 ==========")
	for _, r := range results {
		status := "✅ PASS"
		if !r.Passed {
			status = "❌ FAIL"
			failed++
		} else {
			passed++
		}
		fmt.Printf("%s | %s | %s\n", status, r.Name, r.Message)
	}
	fmt.Println("==============================")
	fmt.Printf("总计: %d 通过, %d 失败\n", passed, failed)
}

// ================================
// 主程序
// ================================

func main() {
	// 命令行参数
	action := flag.String("action", "help", "操作类型: seed/simulate/verify/integration/invite-test/help")
	dryRun := flag.Bool("dry-run", false, "试运行模式，不实际写入数据库")
	userID := flag.Int64("user", 8888, "用户ID (用于 simulate 或 integration)")
	taskID := flag.Int64("task", 0, "任务ID")
	flag.Parse()

	// 显示帮助
	if *action == "help" {
		fmt.Println(`
任务中心配置组合测试工具

用法:
  go run main.go -action=<操作>

操作类型:
  seed        - 生成所有配置组合到数据库
  simulate    - 简单模拟用户进度 (仅修改进度表)
  integration - 真实集成测试 (触发 OnOrderPaid, 验证全流程)
  invite-test - 邀请全链路测试 (模拟邀请、下单、双端奖励发放)
  verify      - 验证配置是否正确

参数:
  -dry-run  - 试运行模式，不实际写入数据库
  -user     - 用户ID (默认: 8888)
  -task     - 任务ID

示例:
  # 邀请全链路测试
  go run main.go -action=invite-test
`)
		return
	}

	// 初始化数据库连接
	repo, err := mysql.New()
	if err != nil {
		log.Fatalf("连接数据库失败: %v", err)
	}

	cfg := configs.Get()
	fmt.Printf("已连接到数据库: %s\n", cfg.MySQL.Write.Name)
	fmt.Printf("时间: %s\n", time.Now().Format("2006-01-02 15:04:05"))

	// 执行操作
	switch *action {
	case "seed":
		if err := SeedAllCombinations(repo, *dryRun); err != nil {
			log.Printf("生成配置失败: %v", err)
			os.Exit(1)
		}

	case "simulate":
		if *taskID == 0 {
			fmt.Println("请指定任务ID: -task=<ID>")
			os.Exit(1)
		}
		if err := SimulateUserTask(repo, *userID, *taskID); err != nil {
			log.Printf("模拟失败: %v", err)
			os.Exit(1)
		}

	case "integration":
		// 确保用户存在
		if err := ensureUserExists(repo, *userID, "测试用户"); err != nil {
			log.Printf("预检用户失败: %v", err)
			os.Exit(1)
		}
		if err := IntegrationTest(repo); err != nil {
			log.Printf("集成测试失败: %v", err)
			os.Exit(1)
		}

	case "invite-test":
		if err := InviteAndTaskIntegrationTest(repo); err != nil {
			log.Printf("邀请测试失败: %v", err)
			os.Exit(1)
		}

	case "verify":
		results := VerifyAllConfigs(repo)
		PrintResults(results)

	default:
		fmt.Printf("未知操作: %s\n", *action)
		os.Exit(1)
	}
}
