package mysql

import (
	"context"
	"runtime"
	"testing"
	"time"

	"hyapp/internal/testutil/mysqlschema"
)

func TestQueryOverviewUsesActivityLuckyGiftDayStats(t *testing.T) {
	ctx := context.Background()
	_, file, _, _ := runtime.Caller(0)
	statsSchema := mysqlschema.New(t, mysqlschema.Config{
		InitDBPath:     mysqlschema.InitDBPath(t, file, "..", "..", "..", "deploy", "mysql", "initdb", "001_statistics_service.sql"),
		DatabasePrefix: "hy_stats_query_test",
	})
	activitySchema := mysqlschema.New(t, mysqlschema.Config{
		EnvVar:         "ACTIVITY_SERVICE_MYSQL_TEST_DSN",
		InitDBPath:     mysqlschema.InitDBPath(t, file, "..", "..", "..", "..", "activity-service", "deploy", "mysql", "initdb", "001_activity_service.sql"),
		DatabasePrefix: "hy_activity_query_test",
	})
	repository, err := Open(ctx, statsSchema.DSN, activitySchema.DSN)
	if err != nil {
		t.Fatalf("open repository: %v", err)
	}
	t.Cleanup(func() { _ = repository.Close() })

	if _, err := repository.db.ExecContext(ctx, `
		INSERT INTO stat_app_day_country (
			app_code, stat_day, country_id, lucky_gift_turnover, lucky_gift_payout, updated_at_ms
		) VALUES ('lalu', '2026-06-06', 210, 100, 900, 1)`); err != nil {
		t.Fatalf("seed stale statistics overview: %v", err)
	}
	if _, err := repository.db.ExecContext(ctx, `
		INSERT INTO stat_lucky_gift_pool_day_country (
			app_code, stat_day, country_id, pool_id, turnover_coin, payout_coin, updated_at_ms
		) VALUES ('lalu', '2026-06-06', 210, 'lucky', 100, 900, 1)`); err != nil {
		t.Fatalf("seed stale statistics pool: %v", err)
	}
	if _, err := repository.activityDB.ExecContext(ctx, `
		INSERT INTO lucky_draw_pool_day_stats (
			app_code, stat_day, visible_region_id, pool_id, gift_id, draw_count, turnover_coin, payout_coin,
			base_reward_coin, room_atmosphere_reward_coin, activity_subsidy_coin, created_at_ms, updated_at_ms
		) VALUES
		  ('lalu', '2026-06-06', 210, 'super_lucky', '', 10, 2744832, 2401885, 2401885, 0, 0, 1, 1),
		  ('lalu', '2026-06-06', 210, 'lucky', '', 5, 148322, 126415, 126415, 0, 0, 1, 1)`); err != nil {
		t.Fatalf("seed activity databi stats: %v", err)
	}

	start := time.Date(2026, 6, 6, 0, 0, 0, 0, time.UTC).UnixMilli()
	overview, err := repository.QueryOverview(ctx, OverviewQuery{AppCode: "lalu", StartMS: start, EndMS: start + int64(24*time.Hour/time.Millisecond), CountryID: 210})
	if err != nil {
		t.Fatalf("query overview: %v", err)
	}
	if overview.LuckyGiftTurnover != 2_893_154 || overview.LuckyGiftPayout != 2_528_300 || overview.LuckyGiftProfit != 364_854 {
		t.Fatalf("activity lucky gift overview not applied: %+v", overview)
	}
	if len(overview.LuckyGiftPools) != 2 {
		t.Fatalf("activity lucky gift pools not applied: %+v", overview.LuckyGiftPools)
	}
	if overview.LuckyGiftPools[0].PoolID != "super_lucky" || overview.LuckyGiftPools[0].TurnoverCoin != 2_744_832 || overview.LuckyGiftPools[0].PayoutCoin != 2_401_885 {
		t.Fatalf("super_lucky pool mismatch: %+v", overview.LuckyGiftPools[0])
	}
}

func TestQueryOverviewReturnsPreviousPeriodDeltaRates(t *testing.T) {
	ctx := context.Background()
	_, file, _, _ := runtime.Caller(0)
	statsSchema := mysqlschema.New(t, mysqlschema.Config{
		InitDBPath:     mysqlschema.InitDBPath(t, file, "..", "..", "..", "deploy", "mysql", "initdb", "001_statistics_service.sql"),
		DatabasePrefix: "hy_stats_query_delta_test",
	})
	repository, err := Open(ctx, statsSchema.DSN)
	if err != nil {
		t.Fatalf("open repository: %v", err)
	}
	t.Cleanup(func() { _ = repository.Close() })

	if _, err := repository.db.ExecContext(ctx, `
		INSERT INTO stat_app_day_country (
			app_code, stat_day, country_id, active_users, paid_users, recharge_usd_minor, new_user_recharge_usd_minor,
			mifapay_recharge_usd_minor, gift_coin_spent, coin_seller_transfer_coin, lucky_gift_turnover, lucky_gift_payout,
			game_turnover, game_payout, game_refund, updated_at_ms
		) VALUES
		  ('lalu', '2026-06-05', 210, 10, 2, 100, 50, 100, 300, 80, 100, 50, 500, 100, 50, 1),
		  ('lalu', '2026-06-06', 210, 20, 5, 200, 100, 200, 600, 200, 400, 100, 1000, 400, 100, 1)`); err != nil {
		t.Fatalf("seed delta statistics: %v", err)
	}

	start := time.Date(2026, 6, 6, 0, 0, 0, 0, time.UTC).UnixMilli()
	overview, err := repository.QueryOverview(ctx, OverviewQuery{AppCode: "lalu", StartMS: start, EndMS: start + int64(24*time.Hour/time.Millisecond), CountryID: 210})
	if err != nil {
		t.Fatalf("query overview: %v", err)
	}
	if overview.RechargeDeltaRate != 1 || overview.NewUserRechargeDeltaRate != 1 || overview.ActiveUsersDeltaRate != 1 {
		t.Fatalf("top delta rates mismatch: %+v", overview)
	}
	if overview.PaidUsersDeltaRate != 1.5 || overview.ARPUDeltaRate != 0 || overview.ARPPUDeltaRate != -0.2 {
		t.Fatalf("user delta rates mismatch: %+v", overview)
	}
	if overview.GiftCoinSpentDeltaRate != 1 || overview.CoinSellerTransferDeltaRate != 1.5 || overview.LuckyGiftTurnoverDeltaRate != 3 || overview.LuckyGiftPayoutDeltaRate != 1 || overview.LuckyGiftProfitDeltaRate != 5 {
		t.Fatalf("coin delta rates mismatch: %+v", overview)
	}
	if overview.GameTurnoverDeltaRate != 1 || overview.GameProfitDeltaRate < 0.428 || overview.GameProfitDeltaRate > 0.429 {
		t.Fatalf("game delta rates mismatch: %+v", overview)
	}
}

func TestQueryOverviewReturnsCountryBreakdownAndIncludesCoinSellerRecharge(t *testing.T) {
	ctx := context.Background()
	_, file, _, _ := runtime.Caller(0)
	statsSchema := mysqlschema.New(t, mysqlschema.Config{
		InitDBPath:     mysqlschema.InitDBPath(t, file, "..", "..", "..", "deploy", "mysql", "initdb", "001_statistics_service.sql"),
		DatabasePrefix: "hy_stats_query_country_test",
	})
	repository, err := Open(ctx, statsSchema.DSN)
	if err != nil {
		t.Fatalf("open repository: %v", err)
	}
	t.Cleanup(func() { _ = repository.Close() })

	if _, err := repository.db.ExecContext(ctx, `
		INSERT INTO stat_app_day_country (
			app_code, stat_day, country_id, region_id, active_users, paid_users, recharge_usd_minor,
			coin_seller_recharge_usd_minor, google_recharge_usd_minor, updated_at_ms
		) VALUES
		  ('lalu', '2026-06-06', 86, 210, 10, 2, 150, 400, 150, 1),
		  ('lalu', '2026-06-06', 66, 210, 5, 1, 100, 0, 100, 1),
		  ('lalu', '2026-06-06', 99, 220, 3, 1, 900, 0, 900, 1)`); err != nil {
		t.Fatalf("seed country statistics: %v", err)
	}

	start := time.Date(2026, 6, 6, 0, 0, 0, 0, time.UTC).UnixMilli()
	overview, err := repository.QueryOverview(ctx, OverviewQuery{AppCode: "lalu", StartMS: start, EndMS: start + int64(24*time.Hour/time.Millisecond), RegionID: 210})
	if err != nil {
		t.Fatalf("query overview: %v", err)
	}
	if overview.RechargeUSDMinor != 650 || overview.CoinSellerRechargeUSDMinor != 400 {
		t.Fatalf("overview recharge mismatch: %+v", overview)
	}
	if len(overview.CountryBreakdown) != 2 {
		t.Fatalf("country breakdown should come from aggregate query: %+v", overview.CountryBreakdown)
	}
	if overview.CountryBreakdown[0].CountryID != 86 || overview.CountryBreakdown[0].RechargeUSDMinor != 550 || overview.CountryBreakdown[0].ARPPUUSDMinor != 275 {
		t.Fatalf("first country breakdown mismatch: %+v", overview.CountryBreakdown[0])
	}
}

func TestQuerySelfGameOverviewUsesRealUserStakeForPlatformProfitRate(t *testing.T) {
	ctx := context.Background()
	_, file, _, _ := runtime.Caller(0)
	statsSchema := mysqlschema.New(t, mysqlschema.Config{
		InitDBPath:     mysqlschema.InitDBPath(t, file, "..", "..", "..", "deploy", "mysql", "initdb", "001_statistics_service.sql"),
		DatabasePrefix: "hy_stats_self_game_profit_test",
	})
	repository, err := Open(ctx, statsSchema.DSN)
	if err != nil {
		t.Fatalf("open repository: %v", err)
	}
	t.Cleanup(func() { _ = repository.Close() })

	if _, err := repository.db.ExecContext(ctx, `
		INSERT INTO stat_self_game_match_day (
			app_code, stat_day, game_id, stake_coin, settled_matches,
			user_stake_coin, robot_stake_coin, payout_coin, refund_coin, platform_profit_coin, updated_at_ms
		) VALUES
		  ('lalu', '2026-06-06', 'dice', 100, 1, 1000, 1000, 700, 100, 200, 1),
		  ('lalu', '2026-06-06', 'dice', 500, 1, 2000, 2000, 1500, 100, 400, 1)`); err != nil {
		t.Fatalf("seed self game match stats: %v", err)
	}

	start := time.Date(2026, 6, 6, 0, 0, 0, 0, time.UTC).UnixMilli()
	overview, err := repository.QuerySelfGameOverview(ctx, SelfGameOverviewQuery{AppCode: "lalu", StartMS: start, EndMS: start + int64(24*time.Hour/time.Millisecond), GameID: "dice"})
	if err != nil {
		t.Fatalf("query self game overview: %v", err)
	}
	if overview.MatchTotals.PlatformProfitRate != 0.2 {
		t.Fatalf("platform profit rate should use real user stake only: %+v", overview.MatchTotals)
	}
	if len(overview.StakeBreakdown) != 2 || overview.StakeBreakdown[0].PlatformProfitRate != 0.2 || overview.StakeBreakdown[1].PlatformProfitRate != 0.2 {
		t.Fatalf("stake platform profit rate should use real user stake only: %+v", overview.StakeBreakdown)
	}
}

func TestQuerySelfGameOverviewKeepsParticipantWinRateInStakeBucket(t *testing.T) {
	ctx := context.Background()
	_, file, _, _ := runtime.Caller(0)
	statsSchema := mysqlschema.New(t, mysqlschema.Config{
		InitDBPath:     mysqlschema.InitDBPath(t, file, "..", "..", "..", "deploy", "mysql", "initdb", "001_statistics_service.sql"),
		DatabasePrefix: "hy_stats_self_game_participant_test",
	})
	repository, err := Open(ctx, statsSchema.DSN)
	if err != nil {
		t.Fatalf("open repository: %v", err)
	}
	t.Cleanup(func() { _ = repository.Close() })

	if _, err := repository.db.ExecContext(ctx, `
		INSERT INTO stat_self_game_participant_day (
			app_code, stat_day, game_id, stake_coin, participant_type, result, dice_point, participant_count, updated_at_ms
		) VALUES
		  ('lalu', '2026-06-06', 'dice', 100, 'user', 'win', 6, 1, 1),
		  ('lalu', '2026-06-06', 'dice', 100, 'user', 'lose', 6, 3, 1),
		  ('lalu', '2026-06-06', 'dice', 500, 'user', 'win', 6, 4, 1)`); err != nil {
		t.Fatalf("seed self game participant stats: %v", err)
	}

	start := time.Date(2026, 6, 6, 0, 0, 0, 0, time.UTC).UnixMilli()
	overview, err := repository.QuerySelfGameOverview(ctx, SelfGameOverviewQuery{AppCode: "lalu", StartMS: start, EndMS: start + int64(24*time.Hour/time.Millisecond), GameID: "dice"})
	if err != nil {
		t.Fatalf("query self game overview: %v", err)
	}
	rates := map[int64]float64{}
	for _, item := range overview.ParticipantDistribution {
		if item.Result == "win" {
			rates[item.StakeCoin] = item.WinRate
		}
	}
	if rates[100] != 0.25 || rates[500] != 1 {
		t.Fatalf("participant win rate should stay in stake bucket: %+v", overview.ParticipantDistribution)
	}
}

func TestQuerySelfGameOverviewUsesLatestPoolBalanceSnapshot(t *testing.T) {
	ctx := context.Background()
	_, file, _, _ := runtime.Caller(0)
	statsSchema := mysqlschema.New(t, mysqlschema.Config{
		InitDBPath:     mysqlschema.InitDBPath(t, file, "..", "..", "..", "deploy", "mysql", "initdb", "001_statistics_service.sql"),
		DatabasePrefix: "hy_stats_self_game_pool_test",
	})
	repository, err := Open(ctx, statsSchema.DSN)
	if err != nil {
		t.Fatalf("open repository: %v", err)
	}
	t.Cleanup(func() { _ = repository.Close() })

	if _, err := repository.db.ExecContext(ctx, `
		INSERT INTO stat_self_game_pool_day (
			app_code, stat_day, game_id, direction, reason, transaction_count, in_coin, out_coin, balance_after_coin, updated_at_ms
		) VALUES
		  ('lalu', '2026-06-05', 'dice', 'out', 'dice_settlement', 1, 0, 100, 5000, 1),
		  ('lalu', '2026-06-06', 'dice', 'out', 'dice_settlement', 1, 0, 200, 4800, 2)`); err != nil {
		t.Fatalf("seed self game pool stats: %v", err)
	}

	start := time.Date(2026, 6, 5, 0, 0, 0, 0, time.UTC).UnixMilli()
	overview, err := repository.QuerySelfGameOverview(ctx, SelfGameOverviewQuery{AppCode: "lalu", StartMS: start, EndMS: start + int64(48*time.Hour/time.Millisecond), GameID: "dice"})
	if err != nil {
		t.Fatalf("query self game overview: %v", err)
	}
	if len(overview.PoolStats) != 1 || overview.PoolStats[0].OutCoin != 300 || overview.PoolStats[0].BalanceAfterCoin != 4800 {
		t.Fatalf("pool balance should be latest snapshot, not max balance: %+v", overview.PoolStats)
	}
}
