package ai

import (
	"fmt"
	"time"
)

// Logger interface for AI logging
type Logger interface {
	LogInfo(message string, args ...interface{})
	LogError(message string, args ...interface{})
	LogDebug(message string, args ...interface{})
	LogWarning(message string, args ...interface{})
}

// NPC interface defines the required NPC functionality for AI
type NPC interface {
	// Basic NPC information
	GetID() int32
	GetName() string
	GetHP() int32
	GetTotalHP() int32
	SetHP(int32)
	IsAlive() bool

	// Combat state
	GetInCombat() bool
	InCombat(bool)
	GetTarget() Entity
	SetTarget(Entity)

	// Pet functionality
	IsPet() bool
	GetOwner() Entity

	// Movement and positioning
	GetX() float32
	GetY() float32
	GetZ() float32
	GetDistance(Entity) float32
	FaceTarget(Entity, bool)
	IsFollowing() bool
	SetFollowing(bool)
	GetFollowTarget() Spawn
	SetFollowTarget(Spawn, float32)
	CalculateRunningLocation(bool)
	ClearRunningLocations()

	// Runback functionality
	IsRunningBack() bool
	GetRunbackLocation() *MovementLocation
	GetRunbackDistance() float32
	Runback(float32)
	ShouldCallRunback() bool
	SetCallRunback(bool)

	// Status effects
	IsMezzedOrStunned() bool
	IsCasting() bool
	IsDazed() bool
	IsFeared() bool
	IsStifled() bool
	InWater() bool
	IsWaterCreature() bool
	IsFlyingCreature() bool

	// Combat mechanics
	AttackAllowed(Entity) bool
	PrimaryWeaponReady() bool
	SecondaryWeaponReady() bool
	SetPrimaryLastAttackTime(int64)
	SetSecondaryLastAttackTime(int64)
	MeleeAttack(Entity, float32, bool)

	// Spell casting
	GetCastPercentage() int8
	GetNextSpell(Entity, float32) Spell
	GetNextBuffSpell(Spawn) Spell
	SetCastOnAggroCompleted(bool)
	CheckLoS(Entity) bool

	// Movement pausing
	IsPauseMovementTimerActive() bool

	// Encounter state
	SetEncounterState(int8)

	// Scripts
	GetSpawnScript() string

	// Utility
	KillSpawn(NPC)
}

// Entity interface for combat entities
type Entity interface {
	Spawn
	GetID() int32
	GetName() string
	GetHP() int32
	GetTotalHP() int32
	IsPlayer() bool
	IsBot() bool
	IsPet() bool
	GetOwner() Entity
	InWater() bool
}

// Spawn interface for basic spawn functionality
type Spawn interface {
	GetID() int32
	GetName() string
	GetX() float32
	GetY() float32
	GetZ() float32
}

// Spell interface for spell data
type Spell interface {
	GetSpellID() int32
	GetName() string
	IsFriendlySpell() bool
	GetCastTime() int32
	GetRecoveryTime() int32
	GetRange() float32
	GetMinRange() float32
}

// MovementLocation represents a location for movement/runback
type MovementLocation struct {
	X      float32
	Y      float32
	Z      float32
	GridID int32
	Stage  int32
}

// LuaInterface defines Lua script integration
type LuaInterface interface {
	RunSpawnScript(script, function string, npc NPC, target Entity) error
}

// Zone interface for zone-related AI operations
type Zone interface {
	GetSpawnByID(int32) Spawn
	ProcessSpell(spell Spell, caster NPC, target Spawn) error
	CallSpawnScript(npc NPC, scriptType string, args ...interface{}) error
}

// AIManager provides high-level management of the AI system
type AIManager struct {
	brains       map[int32]Brain // Map of NPC ID to brain
	activeCount  int64           // Number of active brains
	totalThinks  int64           // Total think cycles processed
	logger       Logger          // Logger for AI events
	luaInterface LuaInterface    // Lua script interface
}

// NewAIManager creates a new AI manager
func NewAIManager(logger Logger, luaInterface LuaInterface) *AIManager {
	return &AIManager{
		brains:       make(map[int32]Brain),
		activeCount:  0,
		totalThinks:  0,
		logger:       logger,
		luaInterface: luaInterface,
	}
}

// AddBrain adds a brain for an NPC
func (am *AIManager) AddBrain(npcID int32, brain Brain) error {
	if brain == nil {
		return fmt.Errorf("brain cannot be nil")
	}

	if _, exists := am.brains[npcID]; exists {
		return fmt.Errorf("brain already exists for NPC %d", npcID)
	}

	am.brains[npcID] = brain
	if brain.IsActive() {
		am.activeCount++
	}

	if am.logger != nil {
		am.logger.LogDebug("Added brain for NPC %d (type: %d)", npcID, brain.GetBrainType())
	}

	return nil
}

// RemoveBrain removes a brain for an NPC
func (am *AIManager) RemoveBrain(npcID int32) {
	if brain, exists := am.brains[npcID]; exists {
		if brain.IsActive() {
			am.activeCount--
		}
		delete(am.brains, npcID)

		if am.logger != nil {
			am.logger.LogDebug("Removed brain for NPC %d", npcID)
		}
	}
}

// GetBrain retrieves a brain for an NPC
func (am *AIManager) GetBrain(npcID int32) Brain {
	return am.brains[npcID]
}

// CreateBrainForNPC creates and adds the appropriate brain for an NPC
func (am *AIManager) CreateBrainForNPC(npc NPC, brainType int8, options ...interface{}) error {
	if npc == nil {
		return fmt.Errorf("NPC cannot be nil")
	}

	npcID := npc.GetID()

	// Create brain based on type
	var brain Brain
	switch brainType {
	case BrainTypeCombatPet:
		brain = NewCombatPetBrain(npc, am.logger)

	case BrainTypeNonCombatPet:
		brain = NewNonCombatPetBrain(npc, am.logger)

	case BrainTypeBlank:
		brain = NewBlankBrain(npc, am.logger)

	case BrainTypeLua:
		brain = NewLuaBrain(npc, am.logger, am.luaInterface)

	case BrainTypeDumbFire:
		if len(options) >= 2 {
			if target, ok := options[0].(Entity); ok {
				if expireTime, ok := options[1].(int32); ok {
					brain = NewDumbFirePetBrain(npc, target, expireTime, am.logger)
				}
			}
		}
		if brain == nil {
			return fmt.Errorf("invalid options for dumbfire brain")
		}

	default:
		brain = NewBaseBrain(npc, am.logger)
	}

	return am.AddBrain(npcID, brain)
}

// ProcessAllBrains runs think cycles for all active brains
func (am *AIManager) ProcessAllBrains() {
	currentTime := currentTimeMillis()

	for npcID, brain := range am.brains {
		if !brain.IsActive() {
			continue
		}

		// Check if it's time to think
		lastThink := brain.GetLastThink()
		thinkTick := brain.GetThinkTick()

		if currentTime-lastThink >= int64(thinkTick) {
			if err := brain.Think(); err != nil {
				if am.logger != nil {
					am.logger.LogError("Brain think error for NPC %d: %v", npcID, err)
				}
			}
			am.totalThinks++
		}
	}
}

// SetBrainActive sets the active state of a brain
func (am *AIManager) SetBrainActive(npcID int32, active bool) {
	if brain := am.brains[npcID]; brain != nil {
		wasActive := brain.IsActive()
		brain.SetActive(active)

		// Update active count
		if wasActive && !active {
			am.activeCount--
		} else if !wasActive && active {
			am.activeCount++
		}
	}
}

// GetActiveCount returns the number of active brains
func (am *AIManager) GetActiveCount() int64 {
	return am.activeCount
}

// GetTotalThinks returns the total number of think cycles processed
func (am *AIManager) GetTotalThinks() int64 {
	return am.totalThinks
}

// GetBrainCount returns the total number of brains
func (am *AIManager) GetBrainCount() int {
	return len(am.brains)
}

// GetBrainsByType returns all brains of a specific type
func (am *AIManager) GetBrainsByType(brainType int8) []Brain {
	var result []Brain
	for _, brain := range am.brains {
		if brain.GetBrainType() == brainType {
			result = append(result, brain)
		}
	}
	return result
}

// ClearAllBrains removes all brains
func (am *AIManager) ClearAllBrains() {
	am.brains = make(map[int32]Brain)
	am.activeCount = 0

	if am.logger != nil {
		am.logger.LogInfo("Cleared all AI brains")
	}
}

// GetStatistics returns overall AI system statistics
func (am *AIManager) GetStatistics() *AIStatistics {
	return &AIStatistics{
		TotalBrains:  len(am.brains),
		ActiveBrains: int(am.activeCount),
		TotalThinks:  am.totalThinks,
		BrainsByType: am.getBrainCountsByType(),
	}
}

// getBrainCountsByType returns counts of brains by type
func (am *AIManager) getBrainCountsByType() map[string]int {
	counts := make(map[string]int)

	for _, brain := range am.brains {
		typeName := getBrainTypeName(brain.GetBrainType())
		counts[typeName]++
	}

	return counts
}

// AIStatistics contains AI system statistics
type AIStatistics struct {
	TotalBrains  int            `json:"total_brains"`
	ActiveBrains int            `json:"active_brains"`
	TotalThinks  int64          `json:"total_thinks"`
	BrainsByType map[string]int `json:"brains_by_type"`
}

// AIBrainAdapter provides NPC functionality for brains
type AIBrainAdapter struct {
	npc    NPC
	logger Logger
}

// NewAIBrainAdapter creates a new brain adapter
func NewAIBrainAdapter(npc NPC, logger Logger) *AIBrainAdapter {
	return &AIBrainAdapter{
		npc:    npc,
		logger: logger,
	}
}

// GetNPC returns the adapted NPC
func (aba *AIBrainAdapter) GetNPC() NPC {
	return aba.npc
}

// ProcessAI processes AI for the NPC using its brain
func (aba *AIBrainAdapter) ProcessAI(brain Brain) error {
	if brain == nil {
		return fmt.Errorf("brain is nil")
	}

	if !brain.IsActive() {
		return nil
	}

	return brain.Think()
}

// SetupDefaultBrain sets up a default brain for the NPC
func (aba *AIBrainAdapter) SetupDefaultBrain() Brain {
	return NewBaseBrain(aba.npc, aba.logger)
}

// SetupPetBrain sets up a pet brain based on pet type
func (aba *AIBrainAdapter) SetupPetBrain(combatPet bool) Brain {
	if combatPet {
		return NewCombatPetBrain(aba.npc, aba.logger)
	}
	return NewNonCombatPetBrain(aba.npc, aba.logger)
}

// Utility functions

// getBrainTypeName returns the string name for a brain type
func getBrainTypeName(brainType int8) string {
	switch brainType {
	case BrainTypeDefault:
		return "default"
	case BrainTypeCombatPet:
		return "combat_pet"
	case BrainTypeNonCombatPet:
		return "non_combat_pet"
	case BrainTypeBlank:
		return "blank"
	case BrainTypeLua:
		return "lua"
	case BrainTypeDumbFire:
		return "dumbfire"
	default:
		return "unknown"
	}
}

// currentTimeMillis returns current time in milliseconds
func currentTimeMillis() int64 {
	return time.Now().UnixMilli()
}

// HateListDebugger provides debugging functionality for hate lists
type HateListDebugger struct {
	logger Logger
}

// NewHateListDebugger creates a new hate list debugger
func NewHateListDebugger(logger Logger) *HateListDebugger {
	return &HateListDebugger{
		logger: logger,
	}
}

// PrintHateList prints a formatted hate list
func (hld *HateListDebugger) PrintHateList(npcName string, hateList map[int32]*HateEntry) {
	if hld.logger == nil {
		return
	}

	hld.logger.LogInfo("%s's Hate List", npcName)
	hld.logger.LogInfo("-------------------")

	if len(hateList) == 0 {
		hld.logger.LogInfo("(empty)")
	} else {
		for entityID, entry := range hateList {
			hld.logger.LogInfo("Entity %d: %d hate", entityID, entry.HateValue)
		}
	}

	hld.logger.LogInfo("-------------------")
}

// PrintEncounterList prints a formatted encounter list
func (hld *HateListDebugger) PrintEncounterList(npcName string, encounterList map[int32]*EncounterEntry) {
	if hld.logger == nil {
		return
	}

	hld.logger.LogInfo("%s's Encounter List", npcName)
	hld.logger.LogInfo("-------------------")

	if len(encounterList) == 0 {
		hld.logger.LogInfo("(empty)")
	} else {
		for entityID, entry := range encounterList {
			entryType := "NPC"
			if entry.IsPlayer {
				entryType = "Player"
			} else if entry.IsBot {
				entryType = "Bot"
			}
			hld.logger.LogInfo("Entity %d (%s)", entityID, entryType)
		}
	}

	hld.logger.LogInfo("-------------------")
}
