package player

import (
	"sort"

	"eq2emu/internal/spells"
)

// AddSpellBookEntry adds a spell to the player's spell book
func (p *Player) AddSpellBookEntry(spellID int32, tier int8, slot int32, spellType int32, timer int32, saveNeeded bool) {
	p.spellsBookMutex.Lock()
	defer p.spellsBookMutex.Unlock()

	// Check if spell already exists
	for _, entry := range p.spells {
		if entry.SpellID == spellID && entry.Tier == tier {
			// Update existing entry
			entry.Slot = slot
			entry.Type = spellType
			entry.Timer = timer
			entry.SaveNeeded = saveNeeded
			return
		}
	}

	// Create new entry
	entry := &SpellBookEntry{
		SpellID:    spellID,
		Tier:       tier,
		Slot:       slot,
		Type:       spellType,
		Timer:      timer,
		SaveNeeded: saveNeeded,
		Player:     p,
		Visible:    true,
		InUse:      false,
	}

	p.spells = append(p.spells, entry)
}

// GetSpellBookSpell returns a spell book entry by spell ID
func (p *Player) GetSpellBookSpell(spellID int32) *SpellBookEntry {
	p.spellsBookMutex.RLock()
	defer p.spellsBookMutex.RUnlock()

	for _, entry := range p.spells {
		if entry.SpellID == spellID {
			return entry
		}
	}
	return nil
}

// GetSpellsSaveNeeded returns spells that need saving to database
func (p *Player) GetSpellsSaveNeeded() []*SpellBookEntry {
	p.spellsBookMutex.RLock()
	defer p.spellsBookMutex.RUnlock()

	var needSave []*SpellBookEntry
	for _, entry := range p.spells {
		if entry.SaveNeeded {
			needSave = append(needSave, entry)
		}
	}
	return needSave
}

// GetFreeSpellBookSlot returns the next free spell book slot for a type
func (p *Player) GetFreeSpellBookSlot(spellType int32) int32 {
	p.spellsBookMutex.RLock()
	defer p.spellsBookMutex.RUnlock()

	// Find highest slot for this type
	var maxSlot int32 = -1
	for _, entry := range p.spells {
		if entry.Type == spellType && entry.Slot > maxSlot {
			maxSlot = entry.Slot
		}
	}

	return maxSlot + 1
}

// GetSpellBookSpellIDBySkill returns spell IDs for a given skill
func (p *Player) GetSpellBookSpellIDBySkill(skillID int32) []int32 {
	p.spellsBookMutex.RLock()
	defer p.spellsBookMutex.RUnlock()

	var spellIDs []int32
	for _, entry := range p.spells {
		// TODO: Check if spell matches skill
		// spell := master_spell_list.GetSpell(entry.SpellID)
		// if spell != nil && spell.GetSkillID() == skillID {
		//     spellIDs = append(spellIDs, entry.SpellID)
		// }
	}
	return spellIDs
}

// HasSpell checks if player has a spell
func (p *Player) HasSpell(spellID int32, tier int8, includeHigherTiers bool, includePossibleScribe bool) bool {
	p.spellsBookMutex.RLock()
	defer p.spellsBookMutex.RUnlock()

	for _, entry := range p.spells {
		if entry.SpellID == spellID {
			if tier == 255 || entry.Tier == tier {
				return true
			}
			if includeHigherTiers && entry.Tier > tier {
				return true
			}
		}
	}

	if includePossibleScribe {
		// TODO: Check if player can scribe this spell
	}

	return false
}

// GetSpellTier returns the tier of a spell the player has
func (p *Player) GetSpellTier(spellID int32) int8 {
	p.spellsBookMutex.RLock()
	defer p.spellsBookMutex.RUnlock()

	var highestTier int8 = 0
	for _, entry := range p.spells {
		if entry.SpellID == spellID && entry.Tier > highestTier {
			highestTier = entry.Tier
		}
	}
	return highestTier
}

// GetSpellSlot returns the slot of a spell
func (p *Player) GetSpellSlot(spellID int32) int8 {
	entry := p.GetSpellBookSpell(spellID)
	if entry != nil {
		return int8(entry.Slot)
	}
	return -1
}

// SetSpellStatus sets the status of a spell
func (p *Player) SetSpellStatus(spell *spells.Spell, status int8) {
	if spell == nil {
		return
	}

	entry := p.GetSpellBookSpell(spell.GetSpellID())
	if entry != nil {
		p.AddSpellStatus(entry, int16(status), true, 0)
	}
}

// RemoveSpellStatus removes a status from a spell
func (p *Player) RemoveSpellStatus(spell *spells.Spell, status int8) {
	if spell == nil {
		return
	}

	entry := p.GetSpellBookSpell(spell.GetSpellID())
	if entry != nil {
		p.RemoveSpellStatusEntry(entry, int16(status), true, 0)
	}
}

// AddSpellStatus adds a status to a spell entry
func (p *Player) AddSpellStatus(spell *SpellBookEntry, value int16, modifyRecast bool, recast int16) {
	if spell == nil {
		return
	}

	p.spellsBookMutex.Lock()
	defer p.spellsBookMutex.Unlock()

	spell.Status |= int8(value)
	if modifyRecast {
		spell.Recast = recast
		spell.RecastAvailable = 0 // TODO: Calculate actual time
	}
}

// RemoveSpellStatusEntry removes a status from a spell entry
func (p *Player) RemoveSpellStatusEntry(spell *SpellBookEntry, value int16, modifyRecast bool, recast int16) {
	if spell == nil {
		return
	}

	p.spellsBookMutex.Lock()
	defer p.spellsBookMutex.Unlock()

	spell.Status &= ^int8(value)
	if modifyRecast {
		spell.Recast = recast
		spell.RecastAvailable = 0
	}
}

// RemoveSpellBookEntry removes a spell from the spell book
func (p *Player) RemoveSpellBookEntry(spellID int32, removePassivesFromList bool) {
	p.spellsBookMutex.Lock()
	defer p.spellsBookMutex.Unlock()

	for i, entry := range p.spells {
		if entry.SpellID == spellID {
			// Remove from slice
			p.spells = append(p.spells[:i], p.spells[i+1:]...)

			if removePassivesFromList {
				// TODO: Remove from passive list
				p.RemovePassive(spellID, entry.Tier, true)
			}
			break
		}
	}
}

// DeleteSpellBook deletes spells from the spell book based on type
func (p *Player) DeleteSpellBook(typeSelection int8) {
	p.spellsBookMutex.Lock()
	defer p.spellsBookMutex.Unlock()

	var keep []*SpellBookEntry
	for _, entry := range p.spells {
		deleteIt := false

		// Check type flags
		if typeSelection&DELETE_TRADESKILLS != 0 {
			// TODO: Check if tradeskill spell
		}
		if typeSelection&DELETE_SPELLS != 0 {
			// TODO: Check if spell
		}
		if typeSelection&DELETE_COMBAT_ART != 0 {
			// TODO: Check if combat art
		}
		if typeSelection&DELETE_ABILITY != 0 {
			// TODO: Check if ability
		}
		if typeSelection&DELETE_NOT_SHOWN != 0 && !entry.Visible {
			deleteIt = true
		}

		if !deleteIt {
			keep = append(keep, entry)
		}
	}

	p.spells = keep
}

// ResortSpellBook resorts the spell book
func (p *Player) ResortSpellBook(sortBy, order, pattern, maxlvlOnly, bookType int32) {
	p.spellsBookMutex.Lock()
	defer p.spellsBookMutex.Unlock()

	// Filter spells based on criteria
	var filtered []*SpellBookEntry
	for _, entry := range p.spells {
		// TODO: Apply filters based on pattern, maxlvlOnly, bookType
		filtered = append(filtered, entry)
	}

	// Sort based on sortBy and order
	switch sortBy {
	case 0: // By name
		if order == 0 {
			sort.Slice(filtered, func(i, j int) bool {
				return SortSpellEntryByName(filtered[i], filtered[j])
			})
		} else {
			sort.Slice(filtered, func(i, j int) bool {
				return SortSpellEntryByNameReverse(filtered[i], filtered[j])
			})
		}
	case 1: // By level
		if order == 0 {
			sort.Slice(filtered, func(i, j int) bool {
				return SortSpellEntryByLevel(filtered[i], filtered[j])
			})
		} else {
			sort.Slice(filtered, func(i, j int) bool {
				return SortSpellEntryByLevelReverse(filtered[i], filtered[j])
			})
		}
	case 2: // By category
		if order == 0 {
			sort.Slice(filtered, func(i, j int) bool {
				return SortSpellEntryByCategory(filtered[i], filtered[j])
			})
		} else {
			sort.Slice(filtered, func(i, j int) bool {
				return SortSpellEntryByCategoryReverse(filtered[i], filtered[j])
			})
		}
	}

	// Reassign slots
	for i, entry := range filtered {
		entry.Slot = int32(i)
	}
}

// Spell sorting functions
func SortSpellEntryByName(s1, s2 *SpellBookEntry) bool {
	// TODO: Get spell names and compare
	return s1.SpellID < s2.SpellID
}

func SortSpellEntryByNameReverse(s1, s2 *SpellBookEntry) bool {
	return !SortSpellEntryByName(s1, s2)
}

func SortSpellEntryByLevel(s1, s2 *SpellBookEntry) bool {
	// TODO: Get spell levels and compare
	return s1.Tier < s2.Tier
}

func SortSpellEntryByLevelReverse(s1, s2 *SpellBookEntry) bool {
	return !SortSpellEntryByLevel(s1, s2)
}

func SortSpellEntryByCategory(s1, s2 *SpellBookEntry) bool {
	// TODO: Get spell categories and compare
	return s1.Type < s2.Type
}

func SortSpellEntryByCategoryReverse(s1, s2 *SpellBookEntry) bool {
	return !SortSpellEntryByCategory(s1, s2)
}

// LockAllSpells locks all non-tradeskill spells
func (p *Player) LockAllSpells() {
	p.spellsBookMutex.Lock()
	defer p.spellsBookMutex.Unlock()

	p.allSpellsLocked = true

	for _, entry := range p.spells {
		// TODO: Check if not tradeskill spell
		entry.Status |= SPELL_STATUS_LOCK
	}
}

// UnlockAllSpells unlocks all non-tradeskill spells
func (p *Player) UnlockAllSpells(modifyRecast bool, exception *spells.Spell) {
	p.spellsBookMutex.Lock()
	defer p.spellsBookMutex.Unlock()

	p.allSpellsLocked = false

	exceptionID := int32(0)
	if exception != nil {
		exceptionID = exception.GetSpellID()
	}

	for _, entry := range p.spells {
		if entry.SpellID != exceptionID {
			// TODO: Check if not tradeskill spell
			entry.Status &= ^SPELL_STATUS_LOCK
			if modifyRecast {
				entry.RecastAvailable = 0
			}
		}
	}
}

// LockSpell locks a spell and all linked spells
func (p *Player) LockSpell(spell *spells.Spell, recast int16) {
	if spell == nil {
		return
	}

	// Lock the main spell
	entry := p.GetSpellBookSpell(spell.GetSpellID())
	if entry != nil {
		p.AddSpellStatus(entry, SPELL_STATUS_LOCK, true, recast)
	}

	// TODO: Lock all spells with shared timer
}

// UnlockSpell unlocks a spell and all linked spells
func (p *Player) UnlockSpell(spell *spells.Spell) {
	if spell == nil {
		return
	}

	p.UnlockSpellByID(spell.GetSpellID(), spell.GetSpellData().LinkedTimerID)
}

// UnlockSpellByID unlocks a spell by ID
func (p *Player) UnlockSpellByID(spellID, linkedTimerID int32) {
	// Unlock the main spell
	entry := p.GetSpellBookSpell(spellID)
	if entry != nil {
		p.RemoveSpellStatusEntry(entry, SPELL_STATUS_LOCK, true, 0)
	}

	// TODO: Unlock all spells with shared timer
	if linkedTimerID > 0 {
		// Get all spells with this timer and unlock them
	}
}

// LockTSSpells locks tradeskill spells and unlocks combat spells
func (p *Player) LockTSSpells() {
	p.spellsBookMutex.Lock()
	defer p.spellsBookMutex.Unlock()

	for _, entry := range p.spells {
		// TODO: Check if tradeskill spell
		// if spell.IsTradeskill() {
		//     entry.Status |= SPELL_STATUS_LOCK
		// } else {
		//     entry.Status &= ^SPELL_STATUS_LOCK
		// }
	}
}

// UnlockTSSpells unlocks tradeskill spells and locks combat spells
func (p *Player) UnlockTSSpells() {
	p.spellsBookMutex.Lock()
	defer p.spellsBookMutex.Unlock()

	for _, entry := range p.spells {
		// TODO: Check if tradeskill spell
		// if spell.IsTradeskill() {
		//     entry.Status &= ^SPELL_STATUS_LOCK
		// } else {
		//     entry.Status |= SPELL_STATUS_LOCK
		// }
	}
}

// QueueSpell queues a spell for casting
func (p *Player) QueueSpell(spell *spells.Spell) {
	if spell == nil {
		return
	}

	entry := p.GetSpellBookSpell(spell.GetSpellID())
	if entry != nil {
		p.AddSpellStatus(entry, SPELL_STATUS_QUEUE, false, 0)
	}
}

// UnQueueSpell removes a spell from the queue
func (p *Player) UnQueueSpell(spell *spells.Spell) {
	if spell == nil {
		return
	}

	entry := p.GetSpellBookSpell(spell.GetSpellID())
	if entry != nil {
		p.RemoveSpellStatusEntry(entry, SPELL_STATUS_QUEUE, false, 0)
	}
}

// GetSpellBookSpellsByTimer returns all spells with a given timer
func (p *Player) GetSpellBookSpellsByTimer(spell *spells.Spell, timerID int32) []*spells.Spell {
	var timerSpells []*spells.Spell

	p.spellsBookMutex.RLock()
	defer p.spellsBookMutex.RUnlock()

	// TODO: Find all spells with matching timer
	// for _, entry := range p.spells {
	//     spell := master_spell_list.GetSpell(entry.SpellID)
	//     if spell != nil && spell.GetTimerID() == timerID {
	//         timerSpells = append(timerSpells, spell)
	//     }
	// }

	return timerSpells
}

// AddPassiveSpell adds a passive spell
func (p *Player) AddPassiveSpell(id int32, tier int8) {
	for _, spellID := range p.passiveSpells {
		if spellID == id {
			return // Already have it
		}
	}
	p.passiveSpells = append(p.passiveSpells, id)
}

// RemovePassive removes a passive spell
func (p *Player) RemovePassive(id int32, tier int8, removeFromList bool) {
	// TODO: Remove passive effects

	if removeFromList {
		for i, spellID := range p.passiveSpells {
			if spellID == id {
				p.passiveSpells = append(p.passiveSpells[:i], p.passiveSpells[i+1:]...)
				break
			}
		}
	}
}

// ApplyPassiveSpells applies all passive spells
func (p *Player) ApplyPassiveSpells() {
	// TODO: Cast all passive spells
	for _, spellID := range p.passiveSpells {
		// Get spell and cast it
	}
}

// RemoveAllPassives removes all passive spell effects
func (p *Player) RemoveAllPassives() {
	// TODO: Remove all passive effects
	p.passiveSpells = nil
}

// GetSpellSlotMappingCount returns the number of spell slots
func (p *Player) GetSpellSlotMappingCount() int16 {
	p.spellsBookMutex.RLock()
	defer p.spellsBookMutex.RUnlock()

	return int16(len(p.spells))
}

// GetSpellPacketCount returns the spell packet count
func (p *Player) GetSpellPacketCount() int16 {
	return p.spellCount
}

// AddMaintainedSpell adds a maintained spell effect
func (p *Player) AddMaintainedSpell(luaSpell *spells.LuaSpell) {
	// TODO: Add to maintained effects
}

// RemoveMaintainedSpell removes a maintained spell effect
func (p *Player) RemoveMaintainedSpell(luaSpell *spells.LuaSpell) {
	// TODO: Remove from maintained effects
}

// AddSpellEffect adds a spell effect
func (p *Player) AddSpellEffect(luaSpell *spells.LuaSpell, overrideExpireTime int32) {
	// TODO: Add spell effect
}

// RemoveSpellEffect removes a spell effect
func (p *Player) RemoveSpellEffect(luaSpell *spells.LuaSpell) {
	// TODO: Remove spell effect
}

// GetFreeMaintainedSpellSlot returns a free maintained spell slot
func (p *Player) GetFreeMaintainedSpellSlot() *spells.MaintainedEffects {
	// TODO: Find free slot in maintained effects
	return nil
}

// GetMaintainedSpell returns a maintained spell by ID
func (p *Player) GetMaintainedSpell(id int32, onCharLoad bool) *spells.MaintainedEffects {
	// TODO: Find maintained spell
	return nil
}

// GetMaintainedSpellBySlot returns a maintained spell by slot
func (p *Player) GetMaintainedSpellBySlot(slot int8) *spells.MaintainedEffects {
	// TODO: Find maintained spell by slot
	return nil
}

// GetMaintainedSpells returns all maintained spells
func (p *Player) GetMaintainedSpells() *spells.MaintainedEffects {
	// TODO: Return maintained effects array
	return nil
}

// GetFreeSpellEffectSlot returns a free spell effect slot
func (p *Player) GetFreeSpellEffectSlot() *spells.SpellEffects {
	// TODO: Find free slot in spell effects
	return nil
}

// GetSpellEffects returns all spell effects
func (p *Player) GetSpellEffects() *spells.SpellEffects {
	// TODO: Return spell effects array
	return nil
}

// SaveSpellEffects saves spell effects to database
func (p *Player) SaveSpellEffects() {
	if p.stopSaveSpellEffects {
		return
	}
	// TODO: Save spell effects to database
}

// GetTierUp returns the next tier for a given tier
func (p *Player) GetTierUp(tier int16) int16 {
	switch tier {
	case 0:
		return 1
	case 1:
		return 2
	case 2:
		return 3
	case 3:
		return 4
	case 4:
		return 5
	case 5:
		return 6
	case 6:
		return 7
	case 7:
		return 8
	case 8:
		return 9
	case 9:
		return 10
	default:
		return tier + 1
	}
}
