package mysql

import (
	"context"
	"testing"
	"time"

	"hyapp/internal/testutil/mysqlschema"
	"hyapp/pkg/appcode"
	domain "hyapp/services/lucky-gift-service/internal/domain/luckygift"
)

func dailyStatsTestRule(poolID string, strategy string, targetRTPPPM int64) domain.RuleConfig {
	return domain.RuleConfig{
		PoolID:                   poolID,
		Enabled:                  true,
		StrategyVersion:          strategy,
		TargetRTPPPM:             targetRTPPPM,
		PoolRatePPM:              980_000,
		SettlementWindowWager:    1_000_000,
		ControlBandPPM:           10_000,
		GiftPriceReference:       100,
		NoviceMaxEquivalentDraws: 2_000,
		NormalMaxEquivalentDraws: 20_000,
		Stages: []domain.RuleStage{
			{Stage: domain.StageNovice, Tiers: []domain.RuleTier{
				{Stage: domain.StageNovice, TierID: "novice_1x", MultiplierPPM: 1_000_000, BaseWeightPPM: 1_000_000, Enabled: true},
			}},
		},
	}
}

// 回归：Databi 日汇总的策略归因与北京日分桶——刷新循环把抽奖按 rule_version 归因到策略族，
// 分日用 +08:00（23:00 UTC 的抽奖落在次日北京日），日聚合只读奖池汇总行不重复计礼物明细行，
// 策略/区域过滤和找不到规则行时回落 fixed_v2 的行为都在此锁死。
func TestLuckyGiftDailyStatsStrategyAttribution(t *testing.T) {
	caller := mysqlschema.CallerFile(t, 1)
	schema := mysqlschema.New(t, mysqlschema.Config{
		EnvVar:         "LUCKY_GIFT_SERVICE_MYSQL_TEST_DSN",
		InitDBPath:     mysqlschema.InitDBPath(t, caller, "..", "..", "..", "deploy", "mysql", "initdb", "001_lucky_gift_service.sql"),
		DatabasePrefix: "hy_lucky_gift_day",
	})
	repo := &Repository{db: schema.DB}
	ctx := appcode.WithContext(context.Background(), "lalu")

	if _, err := repo.PublishLuckyGiftRuleConfig(ctx, dailyStatsTestRule("lucky", domain.StrategyFixedV2, 950_000), 1_700_000_000_000); err != nil {
		t.Fatalf("publish fixed_v2 rule: %v", err)
	}
	if _, err := repo.PublishLuckyGiftRuleConfig(ctx, dailyStatsTestRule("lucky", domain.StrategyDynamicV3, 960_000), 1_700_000_100_000); err != nil {
		t.Fatalf("publish dynamic_v3 rule: %v", err)
	}

	seedDraw := func(drawID string, ruleVersion, regionID int64, giftID string, spent, reward, createdAtMS int64) {
		t.Helper()
		if _, err := schema.DB.Exec(`
			INSERT INTO lucky_draw_records (
			  app_code, draw_id, command_id, user_id, device_id, room_id, anchor_id, visible_region_id, gift_id, pool_id,
			  rule_version, experience_pool, selected_tier_id, coin_spent,
			  base_reward_coins, effective_reward_coins, reward_status, rtp_window_index, gift_rtp_window_index,
			  candidate_tiers_json, pool_snapshot_json, rtp_snapshot_json,
			  paid_at_ms, created_at_ms, updated_at_ms
			) VALUES ('lalu', ?, ?, 1, 'd', 'room', 'anchor', ?, ?, 'lucky', ?, 'novice', 't', ?, ?, ?, 'granted', 1, 1, '{}', '{}', '{}', ?, ?, ?)`,
			drawID, "cmd-"+drawID, regionID, giftID, ruleVersion, spent, reward, reward, createdAtMS, createdAtMS, createdAtMS,
		); err != nil {
			t.Fatalf("seed draw %s: %v", drawID, err)
		}
	}

	// 2026-07-10 12:00 UTC = 北京日 2026-07-10；2026-07-10 23:00 UTC = 北京日 2026-07-11。
	noonUTC := time.Date(2026, 7, 10, 12, 0, 0, 0, time.UTC).UnixMilli()
	lateUTC := time.Date(2026, 7, 10, 23, 0, 0, 0, time.UTC).UnixMilli()
	seedDraw("v2-a", 1, 11, "g1", 1_000, 900, noonUTC)
	seedDraw("v3-a", 2, 11, "g1", 2_000, 1_500, noonUTC)
	seedDraw("v3-b", 2, 22, "g2", 3_000, 3_600, noonUTC)
	// 北京日跨界：UTC 当日 23 点的 v3 抽奖必须落在次日北京日。
	seedDraw("v3-c", 2, 11, "g1", 5_000, 0, lateUTC)
	// 找不到规则行的历史抽奖按 fixed_v2 归因。
	seedDraw("orphan", 99, 11, "g1", 700, 0, noonUTC)

	if _, err := repo.RefreshLuckyGiftDatabiStatsSnapshots(ctx, lateUTC+1_000, 100); err != nil {
		t.Fatalf("refresh databi stats: %v", err)
	}

	rangeStart := time.Date(2026, 7, 9, 16, 0, 0, 0, time.UTC).UnixMilli() // 北京 2026-07-10 00:00
	rangeEnd := time.Date(2026, 7, 11, 16, 0, 0, 0, time.UTC).UnixMilli()  // 北京 2026-07-12 00:00（排他）

	v3Stats, err := repo.ListLuckyGiftDailyStats(ctx, domain.DailyStatsQuery{
		AppCode: "lalu", StrategyVersion: domain.StrategyDynamicV3, StartMS: rangeStart, EndMS: rangeEnd,
	})
	if err != nil {
		t.Fatalf("list v3 daily stats: %v", err)
	}
	if len(v3Stats) != 2 {
		t.Fatalf("v3 stats days=%d, want 2 (got %+v)", len(v3Stats), v3Stats)
	}
	day0, day1 := v3Stats[0], v3Stats[1]
	if day0.StatDay != "2026-07-10" || day0.DrawCount != 2 || day0.TurnoverCoin != 5_000 || day0.PayoutCoin != 5_100 {
		t.Fatalf("v3 day0 mismatch: %+v", day0)
	}
	if day1.StatDay != "2026-07-11" || day1.DrawCount != 1 || day1.TurnoverCoin != 5_000 || day1.PayoutCoin != 0 {
		t.Fatalf("v3 day1 (北京跨日) mismatch: %+v", day1)
	}

	// fixed_v2 口径包含孤儿 rule_version 的回落归因。
	v2Stats, err := repo.ListLuckyGiftDailyStats(ctx, domain.DailyStatsQuery{
		AppCode: "lalu", StrategyVersion: domain.StrategyFixedV2, StartMS: rangeStart, EndMS: rangeEnd,
	})
	if err != nil {
		t.Fatalf("list v2 daily stats: %v", err)
	}
	if len(v2Stats) != 1 || v2Stats[0].DrawCount != 2 || v2Stats[0].TurnoverCoin != 1_700 {
		t.Fatalf("v2 stats mismatch (want orphan fallback included): %+v", v2Stats)
	}

	// 不带策略过滤时全量聚合；礼物明细行不能重复计数。
	allStats, err := repo.ListLuckyGiftDailyStats(ctx, domain.DailyStatsQuery{
		AppCode: "lalu", StartMS: rangeStart, EndMS: rangeEnd,
	})
	if err != nil {
		t.Fatalf("list all daily stats: %v", err)
	}
	if len(allStats) != 2 || allStats[0].DrawCount != 4 || allStats[0].TurnoverCoin != 6_700 {
		t.Fatalf("all stats mismatch: %+v", allStats)
	}

	// 区域过滤：只看 region 22 的 v3 抽奖。
	regionStats, err := repo.ListLuckyGiftDailyStats(ctx, domain.DailyStatsQuery{
		AppCode: "lalu", StrategyVersion: domain.StrategyDynamicV3, StartMS: rangeStart, EndMS: rangeEnd, RegionIDs: []int64{22},
	})
	if err != nil {
		t.Fatalf("list region daily stats: %v", err)
	}
	if len(regionStats) != 1 || regionStats[0].DrawCount != 1 || regionStats[0].TurnoverCoin != 3_000 {
		t.Fatalf("region stats mismatch: %+v", regionStats)
	}
}
