// Command strategy-sim runs the product matrix against the exact pure kernel used by
// production. It performs no database or network I/O; fixed seeds make every report
// reproducible in CI, during review, and after a rule-version change.
package main

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

	domain "hyapp/services/lucky-gift-service/internal/domain/luckygift"
)

const defaultSimulationSeed int64 = 20_260_712

type simulationOptions struct {
	Seed          int64
	MonteCarloN   int
	LongRunN      int
	GroupLongRunN int
}

type simulationReport struct {
	Seed              int64            `json:"seed"`
	MonteCarloDraws   int              `json:"monte_carlo_draws"`
	LongRunDraws      int              `json:"long_run_draws"`
	GroupLongRunDraws int              `json:"group_long_run_draws"`
	Nominal           nominalEVReport  `json:"nominal_probability_table"`
	Scenarios         []scenarioResult `json:"scenarios"`
	AllPassed         bool             `json:"all_passed"`
}

type nominalEVReport struct {
	ExpectedMultiplier float64 `json:"expected_multiplier"`
	NominalRTPPercent  float64 `json:"nominal_rtp_percent"`
	TargetRTPPercent   float64 `json:"target_rtp_percent"`
	PassesTarget       bool    `json:"passes_target"`
	Conclusion         string  `json:"conclusion"`
}

type scenarioResult struct {
	Name    string `json:"name"`
	Passed  bool   `json:"passed"`
	Summary string `json:"summary"`
	Data    any    `json:"data,omitempty"`
}

func main() {
	options := simulationOptions{}
	jsonOut := ""
	flag.Int64Var(&options.Seed, "seed", defaultSimulationSeed, "deterministic simulation seed")
	flag.IntVar(&options.MonteCarloN, "monte-carlo-draws", 100_000, "draws for probability Monte Carlo")
	flag.IntVar(&options.LongRunN, "long-run-draws", 100_000, "draws for funded RTP long run")
	flag.IntVar(&options.GroupLongRunN, "group-long-run-draws", 30_000, "draws per recharge-stage group")
	flag.StringVar(&jsonOut, "json-out", "", "optional path for the same JSON report printed to stdout")
	flag.Parse()
	if options.MonteCarloN <= 0 || options.LongRunN <= 0 || options.GroupLongRunN <= 0 {
		fmt.Fprintln(os.Stderr, "all draw counts must be positive")
		os.Exit(2)
	}

	report := runSimulation(options)
	encoded, err := json.MarshalIndent(report, "", "  ")
	if err != nil {
		fmt.Fprintf(os.Stderr, "encode report: %v\n", err)
		os.Exit(1)
	}
	printHumanSummary(report)
	fmt.Println("\n--- JSON REPORT ---")
	fmt.Println(string(encoded))
	if jsonOut != "" {
		if err := os.WriteFile(jsonOut, append(encoded, '\n'), 0o644); err != nil {
			fmt.Fprintf(os.Stderr, "write JSON report: %v\n", err)
			os.Exit(1)
		}
	}
	if !report.AllPassed {
		os.Exit(1)
	}
}

func runSimulation(options simulationOptions) simulationReport {
	// The runtime default remains a safe 98% table. Only this simulator opts into
	// the supplied image's 2650% table so the report can expose why it is unsafe.
	config := imageExampleConfig(domain.DefaultLuckyGiftStrategyConfig())
	nominal := calculateNominalEV(config)
	scenarios := []scenarioResult{
		simulateImage800Redraw(config),
		simulatePWBoundaries(config),
		simulateFundSplitAndColdStart(config),
		simulateBatchCounts(config, options.Seed),
		simulateSettlementWagerWindow(),
		simulateWaterBoundaries(config),
		simulateUser24HourRTPDownweight(config, options.Seed+5),
		simulateUser24HourRTPBatchCrossing(config),
		simulateUser24HourRTPMillionDraws(config, options.Seed+6),
		simulateRechargeWindowBoundaries(config),
		simulateProbabilityMonteCarlo(config, options.Seed+10, options.MonteCarloN),
		simulateFundedRTPLongRun(config, options.Seed+20, options.LongRunN),
		simulateSettledRoundQualification(config, options.Seed+30),
		simulatePoolBlockedQualificationConsumption(config, options.Seed+40),
		simulateJackpotSet(config, options.Seed+50),
		simulateDailyJackpotLimit(config, options.Seed+60),
		simulateDualSourceOverlap(config),
		simulateSixRiskCapacities(config),
		simulateRechargeStages(config),
		simulateCombinationPriority(config, options.Seed+70),
		simulateDualJackpotSpendStrategy(config, options.Seed+75),
		simulateConcurrencyAndIdempotency(config, options.Seed+80),
		simulateLongRunGroups(config, options.Seed+90, options.GroupLongRunN),
	}
	allPassed := !nominal.PassesTarget // 2650% must be explicitly rejected, not relabelled as a 98% pass.
	for _, scenario := range scenarios {
		allPassed = allPassed && scenario.Passed
	}
	return simulationReport{
		Seed: options.Seed, MonteCarloDraws: options.MonteCarloN, LongRunDraws: options.LongRunN,
		GroupLongRunDraws: options.GroupLongRunN, Nominal: nominal, Scenarios: scenarios, AllPassed: allPassed,
	}
}

func calculateNominalEV(config domain.StrategyConfig) nominalEVReport {
	var expected float64
	for _, tier := range config.Tiers {
		if tier.Enabled {
			expected += float64(tier.BaseWeightPPM) / float64(domain.StrategyPPMScale) * float64(tier.MultiplierPPM) / float64(domain.StrategyPPMScale)
		}
	}
	return nominalEVReport{
		ExpectedMultiplier: expected, NominalRTPPercent: expected * 100, TargetRTPPercent: 98,
		PassesTarget: expected <= 0.98,
		Conclusion:   "图示固定概率表的名义EV为26.5x（2650%），不能作为98% RTP方案；只有P/W资金约束后的有资金长跑口径可验证不透支。",
	}
}

func printHumanSummary(report simulationReport) {
	fmt.Printf("幸运礼物策略模拟 seed=%d\n", report.Seed)
	fmt.Printf("名义概率表: EV=%.2fx, RTP=%.2f%%, 98%%校验=%v（预期必须为false）\n", report.Nominal.ExpectedMultiplier, report.Nominal.NominalRTPPercent, report.Nominal.PassesTarget)
	for _, scenario := range report.Scenarios {
		status := "PASS"
		if !scenario.Passed {
			status = "FAIL"
		}
		fmt.Printf("[%s] %s: %s\n", status, scenario.Name, scenario.Summary)
	}
	fmt.Printf("总结果: all_passed=%v\n", report.AllPassed)
}
