package ai

import (
	"sync"
	"time"
)

// HateEntry represents a single hate entry in the hate list
type HateEntry struct {
	EntityID    int32 // ID of the hated entity
	HateValue   int32 // Amount of hate (must be >= 1)
	LastUpdated int64 // Timestamp of last hate update
}

// NewHateEntry creates a new hate entry
func NewHateEntry(entityID, hateValue int32) *HateEntry {
	if hateValue < MinHateValue {
		hateValue = MinHateValue
	}

	return &HateEntry{
		EntityID:    entityID,
		HateValue:   hateValue,
		LastUpdated: time.Now().UnixMilli(),
	}
}

// HateList manages the hate list for an NPC brain
type HateList struct {
	entries map[int32]*HateEntry // Map of entity ID to hate entry
	mutex   sync.RWMutex         // Thread safety
}

// NewHateList creates a new hate list
func NewHateList() *HateList {
	return &HateList{
		entries: make(map[int32]*HateEntry),
	}
}

// AddHate adds or updates hate for an entity
func (hl *HateList) AddHate(entityID, hateValue int32) {
	hl.mutex.Lock()
	defer hl.mutex.Unlock()

	if len(hl.entries) >= MaxHateListSize {
		// Remove oldest entry if at capacity
		hl.removeOldestEntry()
	}

	if entry, exists := hl.entries[entityID]; exists {
		// Update existing entry
		entry.HateValue += hateValue
		if entry.HateValue < MinHateValue {
			entry.HateValue = MinHateValue
		}
		entry.LastUpdated = time.Now().UnixMilli()
	} else {
		// Create new entry
		if hateValue < MinHateValue {
			hateValue = MinHateValue
		}
		hl.entries[entityID] = NewHateEntry(entityID, hateValue)
	}
}

// GetHate returns the hate value for an entity
func (hl *HateList) GetHate(entityID int32) int32 {
	hl.mutex.RLock()
	defer hl.mutex.RUnlock()

	if entry, exists := hl.entries[entityID]; exists {
		return entry.HateValue
	}
	return 0
}

// RemoveHate removes hate for a specific entity
func (hl *HateList) RemoveHate(entityID int32) {
	hl.mutex.Lock()
	defer hl.mutex.Unlock()

	delete(hl.entries, entityID)
}

// Clear removes all hate entries
func (hl *HateList) Clear() {
	hl.mutex.Lock()
	defer hl.mutex.Unlock()

	hl.entries = make(map[int32]*HateEntry)
}

// GetMostHated returns the entity ID with the highest hate
func (hl *HateList) GetMostHated() int32 {
	hl.mutex.RLock()
	defer hl.mutex.RUnlock()

	var mostHated int32 = 0
	var highestHate int32 = 0

	for entityID, entry := range hl.entries {
		if entry.HateValue > highestHate {
			highestHate = entry.HateValue
			mostHated = entityID
		}
	}

	return mostHated
}

// GetHatePercentage returns the percentage of hate for an entity
func (hl *HateList) GetHatePercentage(entityID int32) int8 {
	hl.mutex.RLock()
	defer hl.mutex.RUnlock()

	entry, exists := hl.entries[entityID]
	if !exists || entry.HateValue <= 0 {
		return 0
	}

	// Calculate total hate
	var totalHate int32 = 0
	for _, e := range hl.entries {
		totalHate += e.HateValue
	}

	if totalHate <= 0 {
		return 0
	}

	percentage := float32(entry.HateValue) / float32(totalHate) * 100.0
	return int8(percentage)
}

// GetAllEntries returns a copy of all hate entries
func (hl *HateList) GetAllEntries() map[int32]*HateEntry {
	hl.mutex.RLock()
	defer hl.mutex.RUnlock()

	result := make(map[int32]*HateEntry)
	for id, entry := range hl.entries {
		result[id] = &HateEntry{
			EntityID:    entry.EntityID,
			HateValue:   entry.HateValue,
			LastUpdated: entry.LastUpdated,
		}
	}
	return result
}

// Size returns the number of entries in the hate list
func (hl *HateList) Size() int {
	hl.mutex.RLock()
	defer hl.mutex.RUnlock()

	return len(hl.entries)
}

// removeOldestEntry removes the oldest hate entry (internal use only)
func (hl *HateList) removeOldestEntry() {
	if len(hl.entries) == 0 {
		return
	}

	var oldestID int32
	var oldestTime int64 = time.Now().UnixMilli()

	for id, entry := range hl.entries {
		if entry.LastUpdated < oldestTime {
			oldestTime = entry.LastUpdated
			oldestID = id
		}
	}

	if oldestID != 0 {
		delete(hl.entries, oldestID)
	}
}

// EncounterEntry represents a single encounter participant
type EncounterEntry struct {
	EntityID    int32 // ID of the entity
	CharacterID int32 // Character ID for players (0 for NPCs)
	AddedTime   int64 // When entity was added to encounter
	IsPlayer    bool  // Whether this is a player entity
	IsBot       bool  // Whether this is a bot entity
}

// NewEncounterEntry creates a new encounter entry
func NewEncounterEntry(entityID, characterID int32, isPlayer, isBot bool) *EncounterEntry {
	return &EncounterEntry{
		EntityID:    entityID,
		CharacterID: characterID,
		AddedTime:   time.Now().UnixMilli(),
		IsPlayer:    isPlayer,
		IsBot:       isBot,
	}
}

// EncounterList manages the encounter list for an NPC brain
type EncounterList struct {
	entries           map[int32]*EncounterEntry // Map of entity ID to encounter entry
	playerEntries     map[int32]int32           // Map of character ID to entity ID
	playerInEncounter bool                      // Whether any player is in encounter
	mutex             sync.RWMutex              // Thread safety
}

// NewEncounterList creates a new encounter list
func NewEncounterList() *EncounterList {
	return &EncounterList{
		entries:           make(map[int32]*EncounterEntry),
		playerEntries:     make(map[int32]int32),
		playerInEncounter: false,
	}
}

// AddEntity adds an entity to the encounter list
func (el *EncounterList) AddEntity(entityID, characterID int32, isPlayer, isBot bool) bool {
	el.mutex.Lock()
	defer el.mutex.Unlock()

	if len(el.entries) >= MaxEncounterSize {
		return false
	}

	// Check if already in encounter
	if _, exists := el.entries[entityID]; exists {
		return false
	}

	// Add entry
	entry := NewEncounterEntry(entityID, characterID, isPlayer, isBot)
	el.entries[entityID] = entry

	// Track player entries separately
	if isPlayer && characterID > 0 {
		el.playerEntries[characterID] = entityID
		el.playerInEncounter = true
	}

	return true
}

// RemoveEntity removes an entity from the encounter list
func (el *EncounterList) RemoveEntity(entityID int32) {
	el.mutex.Lock()
	defer el.mutex.Unlock()

	if entry, exists := el.entries[entityID]; exists {
		// Remove from player entries if it's a player
		if entry.IsPlayer && entry.CharacterID > 0 {
			delete(el.playerEntries, entry.CharacterID)
		}

		// Remove main entry
		delete(el.entries, entityID)

		// Update player in encounter flag
		el.updatePlayerInEncounter()
	}
}

// Clear removes all entities from the encounter list
func (el *EncounterList) Clear() {
	el.mutex.Lock()
	defer el.mutex.Unlock()

	el.entries = make(map[int32]*EncounterEntry)
	el.playerEntries = make(map[int32]int32)
	el.playerInEncounter = false
}

// IsEntityInEncounter checks if an entity is in the encounter
func (el *EncounterList) IsEntityInEncounter(entityID int32) bool {
	el.mutex.RLock()
	defer el.mutex.RUnlock()

	_, exists := el.entries[entityID]
	return exists
}

// IsPlayerInEncounter checks if a player (by character ID) is in the encounter
func (el *EncounterList) IsPlayerInEncounter(characterID int32) bool {
	el.mutex.RLock()
	defer el.mutex.RUnlock()

	_, exists := el.playerEntries[characterID]
	return exists
}

// HasPlayerInEncounter returns whether any player is in the encounter
func (el *EncounterList) HasPlayerInEncounter() bool {
	el.mutex.RLock()
	defer el.mutex.RUnlock()

	return el.playerInEncounter
}

// Size returns the number of entities in the encounter
func (el *EncounterList) Size() int {
	el.mutex.RLock()
	defer el.mutex.RUnlock()

	return len(el.entries)
}

// CountPlayerBots returns the number of players and bots in encounter
func (el *EncounterList) CountPlayerBots() int {
	el.mutex.RLock()
	defer el.mutex.RUnlock()

	count := 0
	for _, entry := range el.entries {
		if entry.IsPlayer || entry.IsBot {
			count++
		}
	}
	return count
}

// GetAllEntityIDs returns a copy of all entity IDs in the encounter
func (el *EncounterList) GetAllEntityIDs() []int32 {
	el.mutex.RLock()
	defer el.mutex.RUnlock()

	result := make([]int32, 0, len(el.entries))
	for entityID := range el.entries {
		result = append(result, entityID)
	}
	return result
}

// GetAllEntries returns a copy of all encounter entries
func (el *EncounterList) GetAllEntries() map[int32]*EncounterEntry {
	el.mutex.RLock()
	defer el.mutex.RUnlock()

	result := make(map[int32]*EncounterEntry)
	for id, entry := range el.entries {
		result[id] = &EncounterEntry{
			EntityID:    entry.EntityID,
			CharacterID: entry.CharacterID,
			AddedTime:   entry.AddedTime,
			IsPlayer:    entry.IsPlayer,
			IsBot:       entry.IsBot,
		}
	}
	return result
}

// updatePlayerInEncounter updates the player in encounter flag (internal use only)
func (el *EncounterList) updatePlayerInEncounter() {
	el.playerInEncounter = len(el.playerEntries) > 0
}

// BrainState represents the current state of a brain
type BrainState struct {
	State         int32 // Current AI state
	LastThink     int64 // Timestamp of last think cycle
	ThinkTick     int32 // Time between think cycles in milliseconds
	SpellRecovery int64 // Timestamp when spell recovery completes
	IsActive      bool  // Whether the brain is active
	DebugLevel    int8  // Debug output level
	mutex         sync.RWMutex
}

// NewBrainState creates a new brain state
func NewBrainState() *BrainState {
	return &BrainState{
		State:         AIStateIdle,
		LastThink:     time.Now().UnixMilli(),
		ThinkTick:     DefaultThinkTick,
		SpellRecovery: 0,
		IsActive:      true,
		DebugLevel:    DebugLevelNone,
	}
}

// GetState returns the current AI state
func (bs *BrainState) GetState() int32 {
	bs.mutex.RLock()
	defer bs.mutex.RUnlock()
	return bs.State
}

// SetState sets the current AI state
func (bs *BrainState) SetState(state int32) {
	bs.mutex.Lock()
	defer bs.mutex.Unlock()
	bs.State = state
}

// GetLastThink returns the timestamp of the last think cycle
func (bs *BrainState) GetLastThink() int64 {
	bs.mutex.RLock()
	defer bs.mutex.RUnlock()
	return bs.LastThink
}

// SetLastThink sets the timestamp of the last think cycle
func (bs *BrainState) SetLastThink(timestamp int64) {
	bs.mutex.Lock()
	defer bs.mutex.Unlock()
	bs.LastThink = timestamp
}

// GetThinkTick returns the think tick interval
func (bs *BrainState) GetThinkTick() int32 {
	bs.mutex.RLock()
	defer bs.mutex.RUnlock()
	return bs.ThinkTick
}

// SetThinkTick sets the think tick interval
func (bs *BrainState) SetThinkTick(tick int32) {
	bs.mutex.Lock()
	defer bs.mutex.Unlock()

	if tick < 1 {
		tick = 1
	} else if tick > MaxThinkTick {
		tick = MaxThinkTick
	}

	bs.ThinkTick = tick
}

// GetSpellRecovery returns the spell recovery timestamp
func (bs *BrainState) GetSpellRecovery() int64 {
	bs.mutex.RLock()
	defer bs.mutex.RUnlock()
	return bs.SpellRecovery
}

// SetSpellRecovery sets the spell recovery timestamp
func (bs *BrainState) SetSpellRecovery(timestamp int64) {
	bs.mutex.Lock()
	defer bs.mutex.Unlock()
	bs.SpellRecovery = timestamp
}

// HasRecovered checks if the brain has recovered from spell casting
func (bs *BrainState) HasRecovered() bool {
	bs.mutex.RLock()
	defer bs.mutex.RUnlock()

	currentTime := time.Now().UnixMilli()
	return bs.SpellRecovery <= currentTime
}

// IsActive returns whether the brain is active
func (bs *BrainState) IsActive() bool {
	bs.mutex.RLock()
	defer bs.mutex.RUnlock()
	return bs.IsActive
}

// SetActive sets the brain's active state
func (bs *BrainState) SetActive(active bool) {
	bs.mutex.Lock()
	defer bs.mutex.Unlock()
	bs.IsActive = active
}

// GetDebugLevel returns the debug level
func (bs *BrainState) GetDebugLevel() int8 {
	bs.mutex.RLock()
	defer bs.mutex.RUnlock()
	return bs.DebugLevel
}

// SetDebugLevel sets the debug level
func (bs *BrainState) SetDebugLevel(level int8) {
	bs.mutex.Lock()
	defer bs.mutex.Unlock()
	bs.DebugLevel = level
}

// BrainStatistics contains brain performance statistics
type BrainStatistics struct {
	ThinkCycles      int64   `json:"think_cycles"`
	SpellsCast       int64   `json:"spells_cast"`
	MeleeAttacks     int64   `json:"melee_attacks"`
	HateEvents       int64   `json:"hate_events"`
	EncounterEvents  int64   `json:"encounter_events"`
	AverageThinkTime float64 `json:"average_think_time_ms"`
	LastThinkTime    int64   `json:"last_think_time"`
	TotalActiveTime  int64   `json:"total_active_time_ms"`
}

// NewBrainStatistics creates new brain statistics
func NewBrainStatistics() *BrainStatistics {
	return &BrainStatistics{
		ThinkCycles:      0,
		SpellsCast:       0,
		MeleeAttacks:     0,
		HateEvents:       0,
		EncounterEvents:  0,
		AverageThinkTime: 0.0,
		LastThinkTime:    time.Now().UnixMilli(),
		TotalActiveTime:  0,
	}
}
