package model

import (
	"math"
	"math/rand"

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

// IlvlFactor returns L(ilvl) = pow(mult, max(0, ilvl-1)) per spec section 6.4.
func IlvlFactor(ilvl int) float64 {
	d := ilvl - 1
	if d < 0 {
		d = 0
	}
	mult := tuning.Get().IlvlPerLevelMultiplier
	if mult <= 0 {
		mult = tuning.DefaultValues().IlvlPerLevelMultiplier
	}
	return math.Pow(mult, float64(d))
}

// RarityMultiplier returns M(rarity) per spec section 6.4.2.
func RarityMultiplier(rarity Rarity) float64 {
	switch rarity {
	case RarityCommon:
		return safeMultiplier(tuning.Get().RarityMultiplierCommon, tuning.DefaultValues().RarityMultiplierCommon)
	case RarityUncommon:
		return safeMultiplier(tuning.Get().RarityMultiplierUncommon, tuning.DefaultValues().RarityMultiplierUncommon)
	case RarityRare:
		return safeMultiplier(tuning.Get().RarityMultiplierRare, tuning.DefaultValues().RarityMultiplierRare)
	case RarityEpic:
		return safeMultiplier(tuning.Get().RarityMultiplierEpic, tuning.DefaultValues().RarityMultiplierEpic)
	case RarityLegendary:
		return safeMultiplier(tuning.Get().RarityMultiplierLegendary, tuning.DefaultValues().RarityMultiplierLegendary)
	default:
		return 1.00
	}
}

func safeMultiplier(value float64, fallback float64) float64 {
	if value > 0 {
		return value
	}
	return fallback
}

// ScalePrimary computes primaryOut = round(basePrimary * L(ilvl) * M(rarity)).
func ScalePrimary(basePrimary int, ilvl int, rarity Rarity) int {
	return int(math.Round(float64(basePrimary) * IlvlFactor(ilvl) * RarityMultiplier(rarity)))
}

// RollIlvl generates an item level from the monster level per spec section 6.4.5.
// Base enemies: delta in {-1, 0, +1} uniform.
// Elite enemies: delta in {0, +1, +2} with weights 0.4/0.4/0.2.
func RollIlvl(monsterLevel int, isElite bool) int {
	var delta int
	if isElite {
		r := rand.Float64()
		cfg := tuning.Get()
		switch {
		case r < cfg.RollIlvlEliteBaseChance:
			delta = 0
		case r < cfg.RollIlvlEliteBaseChance+cfg.RollIlvlElitePlusOneChance:
			delta = 1
		default:
			delta = 2
		}
	} else {
		delta = rand.Intn(3) - 1 // -1, 0, or +1
	}
	ilvl := monsterLevel + delta
	if ilvl < 1 {
		ilvl = 1
	}
	return ilvl
}
