package player

import (
	"time"

	"eq2emu/internal/entity"
)

// WasSentSpawn checks if a spawn was already sent to the player
func (p *Player) WasSentSpawn(spawnID int32) bool {
	p.spawnMutex.Lock()
	defer p.spawnMutex.Unlock()

	if state, exists := p.spawnPacketSent[spawnID]; exists {
		return state == int8(SPAWN_STATE_SENT)
	}
	return false
}

// IsSendingSpawn checks if a spawn is currently being sent
func (p *Player) IsSendingSpawn(spawnID int32) bool {
	p.spawnMutex.Lock()
	defer p.spawnMutex.Unlock()

	if state, exists := p.spawnPacketSent[spawnID]; exists {
		return state == int8(SPAWN_STATE_SENDING)
	}
	return false
}

// IsRemovingSpawn checks if a spawn is being removed
func (p *Player) IsRemovingSpawn(spawnID int32) bool {
	p.spawnMutex.Lock()
	defer p.spawnMutex.Unlock()

	if state, exists := p.spawnPacketSent[spawnID]; exists {
		return state == int8(SPAWN_STATE_REMOVING)
	}
	return false
}

// SetSpawnSentState sets the spawn state for tracking
func (p *Player) SetSpawnSentState(spawn *entity.Spawn, state SpawnState) bool {
	if spawn == nil {
		return false
	}

	p.spawnMutex.Lock()
	defer p.spawnMutex.Unlock()

	spawnID := spawn.GetDatabaseID()
	p.spawnPacketSent[spawnID] = int8(state)

	// Handle state-specific logic
	switch state {
	case SPAWN_STATE_SENT_WAIT:
		if queueState, exists := p.spawnStateList[spawnID]; exists {
			queueState.SpawnStateTimer = time.Now().Add(500 * time.Millisecond)
		} else {
			p.spawnStateList[spawnID] = &SpawnQueueState{
				SpawnStateTimer: time.Now().Add(500 * time.Millisecond),
				IndexID:         p.GetIndexForSpawn(spawn),
			}
		}
	case SPAWN_STATE_REMOVING_SLEEP:
		if queueState, exists := p.spawnStateList[spawnID]; exists {
			queueState.SpawnStateTimer = time.Now().Add(10 * time.Second)
		} else {
			p.spawnStateList[spawnID] = &SpawnQueueState{
				SpawnStateTimer: time.Now().Add(10 * time.Second),
				IndexID:         p.GetIndexForSpawn(spawn),
			}
		}
	}

	return true
}

// CheckSpawnStateQueue checks spawn states and updates as needed
func (p *Player) CheckSpawnStateQueue() {
	p.spawnMutex.Lock()
	defer p.spawnMutex.Unlock()

	now := time.Now()
	for spawnID, queueState := range p.spawnStateList {
		if now.After(queueState.SpawnStateTimer) {
			if state, exists := p.spawnPacketSent[spawnID]; exists {
				switch SpawnState(state) {
				case SPAWN_STATE_SENT_WAIT:
					p.spawnPacketSent[spawnID] = int8(SPAWN_STATE_SENT)
					delete(p.spawnStateList, spawnID)
				case SPAWN_STATE_REMOVING_SLEEP:
					// TODO: Remove spawn from index
					p.spawnPacketSent[spawnID] = int8(SPAWN_STATE_REMOVED)
					delete(p.spawnStateList, spawnID)
				}
			}
		}
	}
}

// GetSpawnWithPlayerID returns a spawn by player-specific ID
func (p *Player) GetSpawnWithPlayerID(id int32) *entity.Spawn {
	p.indexMutex.RLock()
	defer p.indexMutex.RUnlock()

	if spawn, exists := p.playerSpawnIDMap[id]; exists {
		return spawn
	}
	return nil
}

// GetIDWithPlayerSpawn returns the player-specific ID for a spawn
func (p *Player) GetIDWithPlayerSpawn(spawn *entity.Spawn) int32 {
	if spawn == nil {
		return 0
	}

	p.indexMutex.RLock()
	defer p.indexMutex.RUnlock()

	if id, exists := p.playerSpawnReverseIDMap[spawn]; exists {
		return id
	}
	return 0
}

// GetNextSpawnIndex returns the next available spawn index
func (p *Player) GetNextSpawnIndex(spawn *entity.Spawn, setLock bool) int16 {
	if setLock {
		p.indexMutex.Lock()
		defer p.indexMutex.Unlock()
	}

	// Start from current index and find next available
	for i := p.spawnIndex + 1; i != p.spawnIndex; i++ {
		if i > 9999 { // Wrap around
			i = 1
		}
		if _, exists := p.playerSpawnIDMap[int32(i)]; !exists {
			p.spawnIndex = i
			return i
		}
	}

	// If we've looped all the way around, increment and use it anyway
	p.spawnIndex++
	if p.spawnIndex > 9999 {
		p.spawnIndex = 1
	}
	return p.spawnIndex
}

// SetSpawnMap adds a spawn to the player's spawn map
func (p *Player) SetSpawnMap(spawn *entity.Spawn) bool {
	if spawn == nil {
		return false
	}

	p.indexMutex.Lock()
	defer p.indexMutex.Unlock()

	// Check if spawn already has an ID
	if id, exists := p.playerSpawnReverseIDMap[spawn]; exists && id > 0 {
		return true
	}

	// Get next available index
	index := p.GetNextSpawnIndex(spawn, false)

	// Set bidirectional mapping
	p.playerSpawnIDMap[int32(index)] = spawn
	p.playerSpawnReverseIDMap[spawn] = int32(index)

	return true
}

// SetSpawnMapIndex sets a specific index for a spawn
func (p *Player) SetSpawnMapIndex(spawn *entity.Spawn, index int32) {
	p.indexMutex.Lock()
	defer p.indexMutex.Unlock()

	p.playerSpawnIDMap[index] = spawn
	p.playerSpawnReverseIDMap[spawn] = index
}

// SetSpawnMapAndIndex sets spawn in map and returns the index
func (p *Player) SetSpawnMapAndIndex(spawn *entity.Spawn) int16 {
	if spawn == nil {
		return 0
	}

	p.indexMutex.Lock()
	defer p.indexMutex.Unlock()

	// Check if spawn already has an ID
	if id, exists := p.playerSpawnReverseIDMap[spawn]; exists && id > 0 {
		return int16(id)
	}

	// Get next available index
	index := p.GetNextSpawnIndex(spawn, false)

	// Set bidirectional mapping
	p.playerSpawnIDMap[int32(index)] = spawn
	p.playerSpawnReverseIDMap[spawn] = int32(index)

	return index
}

// GetSpawnByIndex returns a spawn by its player-specific index
func (p *Player) GetSpawnByIndex(index int16) *entity.Spawn {
	return p.GetSpawnWithPlayerID(int32(index))
}

// GetIndexForSpawn returns the player-specific index for a spawn
func (p *Player) GetIndexForSpawn(spawn *entity.Spawn) int16 {
	return int16(p.GetIDWithPlayerSpawn(spawn))
}

// WasSpawnRemoved checks if a spawn was removed
func (p *Player) WasSpawnRemoved(spawn *entity.Spawn) bool {
	if spawn == nil {
		return false
	}

	p.spawnMutex.Lock()
	defer p.spawnMutex.Unlock()

	spawnID := spawn.GetDatabaseID()
	if state, exists := p.spawnPacketSent[spawnID]; exists {
		return state == int8(SPAWN_STATE_REMOVED)
	}
	return false
}

// ResetSpawnPackets resets spawn packet state for a spawn
func (p *Player) ResetSpawnPackets(id int32) {
	p.spawnMutex.Lock()
	defer p.spawnMutex.Unlock()

	delete(p.spawnPacketSent, id)
	delete(p.spawnStateList, id)
}

// RemoveSpawn removes a spawn from the player's view
func (p *Player) RemoveSpawn(spawn *entity.Spawn, deleteSpawn bool) {
	if spawn == nil {
		return
	}

	// Get the player index for this spawn
	index := p.GetIDWithPlayerSpawn(spawn)
	if index == 0 {
		return
	}

	// Remove from spawn maps
	p.indexMutex.Lock()
	delete(p.playerSpawnIDMap, index)
	delete(p.playerSpawnReverseIDMap, spawn)
	p.indexMutex.Unlock()

	// Remove spawn packets
	spawnID := spawn.GetDatabaseID()
	p.infoMutex.Lock()
	delete(p.spawnInfoPacketList, spawnID)
	p.infoMutex.Unlock()

	p.visMutex.Lock()
	delete(p.spawnVisPacketList, spawnID)
	p.visMutex.Unlock()

	p.posMutex.Lock()
	delete(p.spawnPosPacketList, spawnID)
	p.posMutex.Unlock()

	// Reset spawn state
	p.ResetSpawnPackets(spawnID)

	// TODO: Send despawn packet to client

	if deleteSpawn {
		// TODO: Actually delete the spawn if requested
	}
}

// ShouldSendSpawn determines if a spawn should be sent to player
func (p *Player) ShouldSendSpawn(spawn *entity.Spawn) bool {
	if spawn == nil {
		return false
	}

	// Don't send self
	if spawn == &p.Entity.Spawn {
		return false
	}

	// Check if already sent
	if p.WasSentSpawn(spawn.GetDatabaseID()) {
		return false
	}

	// Check distance
	distance := p.GetDistance(spawn)
	maxDistance := float32(200.0) // TODO: Get from rule system

	if distance > maxDistance {
		return false
	}

	// TODO: Check visibility flags, stealth, etc.

	return true
}

// SetSpawnDeleteTime sets the time when a spawn should be deleted
func (p *Player) SetSpawnDeleteTime(id int32, deleteTime int32) {
	// TODO: Implement spawn deletion timer
}

// GetSpawnDeleteTime gets the deletion time for a spawn
func (p *Player) GetSpawnDeleteTime(id int32) int32 {
	// TODO: Implement spawn deletion timer
	return 0
}

// ClearRemovalTimers clears all spawn removal timers
func (p *Player) ClearRemovalTimers() {
	// TODO: Implement spawn deletion timer clearing
}

// ResetSavedSpawns resets all saved spawn data
func (p *Player) ResetSavedSpawns() {
	p.indexMutex.Lock()
	p.playerSpawnIDMap = make(map[int32]*entity.Spawn)
	p.playerSpawnReverseIDMap = make(map[*entity.Spawn]int32)
	// Re-add self
	p.playerSpawnIDMap[1] = &p.Entity.Spawn
	p.playerSpawnReverseIDMap[&p.Entity.Spawn] = 1
	p.indexMutex.Unlock()

	p.spawnMutex.Lock()
	p.spawnPacketSent = make(map[int32]int8)
	p.spawnStateList = make(map[int32]*SpawnQueueState)
	p.spawnMutex.Unlock()

	p.infoMutex.Lock()
	p.spawnInfoPacketList = make(map[int32]string)
	p.infoMutex.Unlock()

	p.visMutex.Lock()
	p.spawnVisPacketList = make(map[int32]string)
	p.visMutex.Unlock()

	p.posMutex.Lock()
	p.spawnPosPacketList = make(map[int32]string)
	p.posMutex.Unlock()
}

// IsSpawnInRangeList checks if a spawn is in the range list
func (p *Player) IsSpawnInRangeList(spawnID int32) bool {
	p.spawnAggroRangeMutex.RLock()
	defer p.spawnAggroRangeMutex.RUnlock()

	_, exists := p.playerAggroRangeSpawns[spawnID]
	return exists
}

// SetSpawnInRangeList sets whether a spawn is in range
func (p *Player) SetSpawnInRangeList(spawnID int32, inRange bool) {
	p.spawnAggroRangeMutex.Lock()
	defer p.spawnAggroRangeMutex.Unlock()

	if inRange {
		p.playerAggroRangeSpawns[spawnID] = true
	} else {
		delete(p.playerAggroRangeSpawns, spawnID)
	}
}

// ProcessSpawnRangeUpdates processes spawn range updates
func (p *Player) ProcessSpawnRangeUpdates() {
	// TODO: Implement spawn range update processing
	// This would check all spawns in range and update visibility
}
