package game

import (
	"math/rand"
	"sort"
	"time"

	"github.com/denisovdennis/autohero/internal/model"
)

// BalanceEnemyMode selects how the opponent is chosen for balance Monte Carlo.
type BalanceEnemyMode int

const (
	// BalanceEnemyWolfOnly uses a single scaled Forest Wolf (canonical curve check).
	BalanceEnemyWolfOnly BalanceEnemyMode = iota
	// BalanceEnemyMixedSpawn matches PickEnemyForLevelWithRNG (weighted random template in band).
	BalanceEnemyMixedSpawn
)

// BalanceMonteCarloResult aggregates outcomes from RunBalanceMonteCarlo.
type BalanceMonteCarloResult struct {
	Iterations int
	Wins       int
	WinRate    float64
	MedianDur  time.Duration
	P90Dur     time.Duration
	MeanDur    time.Duration
}

// RunBalanceMonteCarlo runs N independent fights at hero level against scaled enemies.
// Per-iteration RNG is derived from seed so results are reproducible.
// Global math/rand is re-seeded per fight for damage/crit/dodge rolls (same as legacy combat).
func RunBalanceMonteCarlo(level int, iterations int, seed int64, gearProfile ReferenceGearProfile, enemyMode BalanceEnemyMode) BalanceMonteCarloResult {
	if iterations <= 0 {
		return BalanceMonteCarloResult{}
	}
	var wins int
	durations := make([]time.Duration, 0, iterations)
	var sumDur time.Duration

	for i := 0; i < iterations; i++ {
		var gearRng *rand.Rand
		if gearProfile == ReferenceGearRolled {
			gearRng = rand.New(rand.NewSource(seed + int64(i)*1_000_003))
		}
		baseHero := NewReferenceHeroForBalance(level, gearProfile, gearRng)
		hero := CloneHeroForCombatSim(baseHero)

		// Combat RNG (damage rolls, dodge, crit, debuff procs).
		rand.Seed(seed + int64(i)*9_999_983)

		var enemy model.Enemy
		switch enemyMode {
		case BalanceEnemyWolfOnly:
			enemy = firstEnemyForBalance(level)
		case BalanceEnemyMixedSpawn:
			pickRNG := rand.New(rand.NewSource(seed + int64(i)*2_000_001))
			enemy = PickEnemyForLevelWithRNG(level, pickRNG, hero)
		default:
			enemy = firstEnemyForBalance(level)
		}

		survived, elapsed := ResolveCombatToEndWithDuration(hero, &enemy, CombatSimDeterministicStart, CombatSimOptions{
			TickRate: 100 * time.Millisecond,
			MaxSteps: CombatSimMaxStepsLong,
		})
		if survived {
			wins++
		}
		durations = append(durations, elapsed)
		sumDur += elapsed
	}

	sort.Slice(durations, func(i, j int) bool { return durations[i] < durations[j] })
	median := durations[len(durations)/2]
	p90idx := int(0.9 * float64(len(durations)-1))
	if p90idx < 0 {
		p90idx = 0
	}
	p90 := durations[p90idx]

	return BalanceMonteCarloResult{
		Iterations: iterations,
		Wins:       wins,
		WinRate:    float64(wins) / float64(iterations),
		MedianDur:  median,
		P90Dur:     p90,
		MeanDur:    time.Duration(int64(sumDur) / int64(iterations)),
	}
}

func firstEnemyForBalance(level int) model.Enemy {
	var best model.Enemy
	bestSet := false
	for _, t := range model.EnemyTemplates {
		if !bestSet {
			best = t
			bestSet = true
			continue
		}
		if t.BaseLevel > 0 && (best.BaseLevel == 0 || t.BaseLevel < best.BaseLevel) {
			best = t
		}
	}
	if !bestSet {
		return model.Enemy{}
	}
	return ScaleEnemyTemplate(best, level)
}
