package game

import (
	"math/rand"
	"time"

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

// ReferenceGearProfile selects how ilvl/rarity are chosen for balance simulations.
type ReferenceGearProfile int

const (
	// ReferenceGearMedian uses ilvl == hero level and uncommon sword + mail (deterministic, low noise).
	ReferenceGearMedian ReferenceGearProfile = iota
	// ReferenceGearRolled uses RollIlvl(level, false) per slot with rng (matches base-monster drop spread).
	ReferenceGearRolled
	// ReferenceGearBaseline is common sword + common medium chest at ilvl == hero level (weakest fair reference).
	ReferenceGearBaseline
	// ReferenceGearMax is legendary sword + legendary medium chest at ilvl == hero level (strongest fair reference).
	ReferenceGearMax
)

// NewReferenceHeroForBalance builds a hero at the given level with sword + medium chest
// scaled per spec §6.4 (IlvlFactor, RarityMultiplier). Stats follow the same level-up path
// as gameplay (LevelUp cadences from tuning). HP is set to MaxHP for a fresh fight.
// rng is used when profile is ReferenceGearRolled; may be nil for ReferenceGearMedian.
func NewReferenceHeroForBalance(level int, profile ReferenceGearProfile, rng *rand.Rand) *model.Hero {
	if level < 1 {
		level = 1
	}
	h := &model.Hero{
		ID:           1,
		Name:         "BalanceRef",
		HP:           100,
		MaxHP:        100,
		Attack:       10,
		Defense:      5,
		Speed:        1.0,
		Strength:     1,
		Constitution: 1,
		Agility:      1,
		Luck:         1,
		State:        model.StateWalking,
		Level:        1,
		Gear:         make(map[model.EquipmentSlot]*model.GearItem),
	}
	for h.Level < level {
		h.XP = model.XPToNextLevel(h.Level)
		h.LevelUp()
	}
	h.HP = h.MaxHP

	wIlvl, aIlvl := level, level
	if profile == ReferenceGearRolled {
		if rng == nil {
			rng = rand.New(rand.NewSource(1))
		}
		wIlvl = rollIlvlForBalance(level, false, rng)
		aIlvl = rollIlvlForBalance(level, false, rng)
	}

	var wRarity, aRarity model.Rarity
	var refWeaponBase, refArmorBase int
	switch profile {
	case ReferenceGearBaseline:
		wRarity, aRarity = model.RarityCommon, model.RarityCommon
		refWeaponBase = 6  // post–gear-nerf weapon base (~7×0.85)
		refArmorBase = 3   // post–gear-nerf chest (~4×0.7 for medium)
	case ReferenceGearMax:
		wRarity, aRarity = model.RarityLegendary, model.RarityLegendary
		refWeaponBase = 6
		refArmorBase = 3
	default:
		// Median, Rolled, unknown: uncommon + mid-tier ref bases aligned with gear migration.
		wRarity, aRarity = model.RarityUncommon, model.RarityUncommon
		refWeaponBase = 10 // ~12×0.85
		refArmorBase = 8   // ~12×0.7
	}

	if profile == ReferenceGearBaseline || profile == ReferenceGearMax {
		wName, aName := "Iron Sword", "Chainmail"
		if profile == ReferenceGearMax {
			wName, aName = "Soul Reaver", "Crown of Eternity"
		}
		if wf := model.GearFamilyByName(wName); wf != nil {
			h.Gear[model.SlotMainHand] = model.NewGearItem(wf, wIlvl, wRarity)
		}
		if af := model.GearFamilyByName(aName); af != nil {
			h.Gear[model.SlotChest] = model.NewGearItem(af, aIlvl, aRarity)
		}
	}
	if h.Gear[model.SlotMainHand] == nil {
		wPrimary := model.ScalePrimary(refWeaponBase, wIlvl, wRarity)
		h.Gear[model.SlotMainHand] = &model.GearItem{
			Slot:          model.SlotMainHand,
			FormID:        "gear.form.main_hand.sword",
			Name:          "Steel Sword",
			Subtype:       "sword",
			Rarity:        wRarity,
			Ilvl:          wIlvl,
			BasePrimary:   refWeaponBase,
			PrimaryStat:   wPrimary,
			StatType:      "attack",
			SpeedModifier: 1.0,
			CritChance:    0.05,
		}
	}
	if h.Gear[model.SlotChest] == nil {
		aPrimary := model.ScalePrimary(refArmorBase, aIlvl, aRarity)
		h.Gear[model.SlotChest] = &model.GearItem{
			Slot:          model.SlotChest,
			FormID:        "gear.form.chest.medium",
			Name:          "Reinforced Mail",
			Subtype:       "medium",
			Rarity:        aRarity,
			Ilvl:          aIlvl,
			BasePrimary:   refArmorBase,
			PrimaryStat:   aPrimary,
			StatType:      "defense",
			SpeedModifier: 1.0,
		}
	}

	now := time.Now()
	h.RefreshDerivedCombatStats(now)
	return h
}

// CloneHeroForCombatSim returns a deep enough copy for ResolveCombatToEnd (gear items copied).
func CloneHeroForCombatSim(h *model.Hero) *model.Hero {
	if h == nil {
		return nil
	}
	cp := *h
	if h.Gear != nil {
		cp.Gear = make(map[model.EquipmentSlot]*model.GearItem, len(h.Gear))
		for k, v := range h.Gear {
			if v != nil {
				gv := *v
				cp.Gear[k] = &gv
			} else {
				cp.Gear[k] = nil
			}
		}
	}
	return &cp
}

// PrepareHeroForAdminCombatSim returns a clone of h for the admin combat simulator: gear copied,
// all buffs and debuffs cleared, derived stats refreshed from base+gear only, HP set to max,
// state normalized to walking (fresh duel — ignores in-combat / resting flags on the source snapshot).
// Does not persist; does not mutate h.
// combatTimelineStart should match the start time passed to ResolveCombatToEndWithDuration (e.g. CombatSimDeterministicStart); if zero, time.Now() is used.
func PrepareHeroForAdminCombatSim(h *model.Hero, combatTimelineStart time.Time) *model.Hero {
	hero := CloneHeroForCombatSim(h)
	if hero == nil {
		return nil
	}
	hero.Buffs = nil
	hero.Debuffs = nil
	hero.DebuffCatalog = nil
	now := combatTimelineStart
	if now.IsZero() {
		now = time.Now()
	}
	hero.State = model.StateWalking
	hero.RefreshDerivedCombatStats(now)
	if hero.MaxHP <= 0 {
		hero.MaxHP = 1
	}
	hero.HP = hero.MaxHP
	return hero
}

// rollIlvlForBalance mirrors model.RollIlvl but uses rng for deterministic simulations.
func rollIlvlForBalance(monsterLevel int, isElite bool, rng *rand.Rand) int {
	var delta int
	if isElite {
		r := rng.Float64()
		cfg := tuning.Get()
		switch {
		case r < cfg.RollIlvlEliteBaseChance:
			delta = 0
		case r < cfg.RollIlvlEliteBaseChance+cfg.RollIlvlElitePlusOneChance:
			delta = 1
		default:
			delta = 2
		}
	} else {
		delta = rng.Intn(3) - 1
	}
	ilvl := monsterLevel + delta
	if ilvl < 1 {
		ilvl = 1
	}
	return ilvl
}
