package game

import (
	"fmt"
	"math"
	"math/rand"
	"time"

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

// TryAutoEquipInMemory equips the item if the slot is empty or the new item improves combat
// rating by at least the runtime-configured threshold. Mutates hero.Gear. Does not touch the database.
func TryAutoEquipInMemory(hero *model.Hero, item *model.GearItem, now time.Time) bool {
	hero.EnsureGearMap()
	current := hero.Gear[item.Slot]
	if current == nil {
		hero.Gear[item.Slot] = item
		return true
	}
	oldRating := hero.CombatRatingAt(now)
	hero.Gear[item.Slot] = item
	if hero.CombatRatingAt(now) >= oldRating*tuning.Get().AutoEquipThreshold {
		return true
	}
	hero.Gear[item.Slot] = current
	return false
}

// TryEquipOrStashOffline runs TryAutoEquipInMemory; if not equipped, appends to inventory
// or invokes onDiscard with an adventure-log message when the backpack is full.
func TryEquipOrStashOffline(hero *model.Hero, item *model.GearItem, now time.Time, onDiscard func(string)) {
	hero.EnsureInventorySlice()
	if TryAutoEquipInMemory(hero, item, now) {
		return
	}
	if len(hero.Inventory) >= model.MaxInventorySlots {
		if onDiscard != nil {
			onDiscard(fmt.Sprintf("Inventory full — dropped %s (%s)", item.Name, item.Rarity))
		}
		return
	}
	hero.Inventory = append(hero.Inventory, item)
}

// AutoSellRandomInventoryShare sells a random share of inventory items.
// At least minShare (0..1) of current inventory is sold; returns sold count and gold gained.
func AutoSellRandomInventoryShare(hero *model.Hero, minShare float64, rng *rand.Rand) (int, int64) {
	if hero == nil {
		return 0, 0
	}
	hero.EnsureInventorySlice()
	n := len(hero.Inventory)
	if n == 0 {
		return 0, 0
	}
	if minShare < 0 {
		minShare = 0
	}
	if minShare > 1 {
		minShare = 1
	}

	minSell := int(math.Ceil(float64(n) * minShare))
	if minSell < 1 {
		minSell = 1
	}
	if minSell > n {
		minSell = n
	}

	var soldCount int
	if n == minSell {
		soldCount = n
	} else if rng == nil {
		soldCount = minSell + rand.Intn(n-minSell+1)
	} else {
		soldCount = minSell + rng.Intn(n-minSell+1)
	}

	perm := make([]int, n)
	for i := 0; i < n; i++ {
		perm[i] = i
	}
	if rng == nil {
		rand.Shuffle(n, func(i, j int) { perm[i], perm[j] = perm[j], perm[i] })
	} else {
		rng.Shuffle(n, func(i, j int) { perm[i], perm[j] = perm[j], perm[i] })
	}

	sold := make(map[int]struct{}, soldCount)
	for i := 0; i < soldCount; i++ {
		sold[perm[i]] = struct{}{}
	}

	kept := make([]*model.GearItem, 0, n-soldCount)
	var goldGained int64
	for i, item := range hero.Inventory {
		if _, ok := sold[i]; ok {
			if item != nil {
				goldGained += model.AutoSellPrice(item.Rarity)
			}
			continue
		}
		kept = append(kept, item)
	}
	hero.Inventory = kept
	hero.Gold += goldGained
	return soldCount, goldGained
}
