package npc

// Database interface for NPC persistence
type Database interface {
	LoadAllNPCs() ([]*NPC, error)
	SaveNPC(npc *NPC) error
	DeleteNPC(npcID int32) error
	LoadNPCSpells(npcID int32) ([]*NPCSpell, error)
	SaveNPCSpells(npcID int32, spells []*NPCSpell) error
	LoadNPCSkills(npcID int32) (map[string]*Skill, error)
	SaveNPCSkills(npcID int32, skills map[string]*Skill) error
}

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

// Client interface for NPC-related client operations
type Client interface {
	GetPlayer() Player
	GetVersion() int16
	SendNPCUpdate(npcData []byte) error
	SendCombatUpdate(combatData []byte) error
	SendSpellCast(spellData []byte) error
}

// Player interface for NPC-related player operations
type Player interface {
	GetCharacterID() int32
	GetName() string
	GetLevel() int8
	GetZoneID() int32
	GetX() float32
	GetY() float32
	GetZ() float32
	IsInCombat() bool
	GetTarget() *NPC
	SendMessage(message string)
}

// Zone interface for NPC zone operations
type Zone interface {
	GetZoneID() int32
	GetNPCs() []*NPC
	AddNPC(npc *NPC) error
	RemoveNPC(npcID int32) error
	GetPlayersInRange(x, y, z, radius float32) []Player
	ProcessEntityCommand(command string, client Client, target *NPC) error
	CallSpawnScript(npc *NPC, scriptType string, args ...interface{}) error
}

// SpellManager interface for spell system integration
type SpellManager interface {
	GetSpell(spellID int32, tier int8) Spell
	CastSpell(caster *NPC, target interface{}, spell Spell) error
	GetSpellEffect(entity interface{}, spellID int32) SpellEffect
	ProcessSpell(spell Spell, caster *NPC, target interface{}) error
}

// Spell interface for spell data
type Spell interface {
	GetSpellID() int32
	GetName() string
	GetTier() int8
	GetRange() float32
	GetMinRange() float32
	GetPowerRequired() int32
	IsFriendlySpell() bool
	IsToggleSpell() bool
	GetCastTime() int32
	GetRecastTime() int32
}

// SpellEffect interface for active spell effects
type SpellEffect interface {
	GetSpellID() int32
	GetTier() int8
	GetDuration() int32
	GetRemainingTime() int32
	IsExpired() bool
}

// SkillManager interface for skill system integration
type SkillManager interface {
	GetSkill(skillID int32) MasterSkill
	GetSkillByName(name string) MasterSkill
	ApplySkillBonus(entity interface{}, skillID int32, bonus float32) error
	RemoveSkillBonus(entity interface{}, skillID int32, bonus float32) error
}

// MasterSkill interface for skill definitions
type MasterSkill interface {
	GetSkillID() int32
	GetName() string
	GetDescription() string
	GetMaxValue() int16
}

// AppearanceManager interface for appearance system integration
type AppearanceManager interface {
	GetAppearance(appearanceID int32) Appearance
	GetAppearancesByName(name string) []Appearance
	RandomizeAppearance(npc *NPC, flags int32) error
}

// Appearance interface for appearance data
type Appearance interface {
	GetAppearanceID() int32
	GetName() string
	GetModelType() int16
	GetRace() int16
	GetGender() int8
}

// MovementManager interface for movement system integration
type MovementManager interface {
	StartMovement(npc *NPC, x, y, z float32) error
	StopMovement(npc *NPC) error
	SetSpeed(npc *NPC, speed float32) error
	NavigateToLocation(npc *NPC, x, y, z float32) error
	IsMoving(npc *NPC) bool
}

// CombatManager interface for combat system integration
type CombatManager interface {
	StartCombat(npc *NPC, target interface{}) error
	EndCombat(npc *NPC) error
	ProcessCombatRound(npc *NPC) error
	CalculateDamage(attacker *NPC, target interface{}) int32
	ApplyDamage(target interface{}, damage int32) error
}

// NPCAware interface for entities that can interact with NPCs
type NPCAware interface {
	GetNPC() *NPC
	IsNPC() bool
	HandleNPCInteraction(npc *NPC, interactionType string) error
	ReceiveNPCCommand(npc *NPC, command string) error
}

// EntityAdapter provides NPC functionality for entities
type EntityAdapter struct {
	npc    *NPC
	logger Logger
}

// NewEntityAdapter creates a new entity adapter
func NewEntityAdapter(npc *NPC, logger Logger) *EntityAdapter {
	return &EntityAdapter{
		npc:    npc,
		logger: logger,
	}
}

// GetNPC returns the associated NPC
func (ea *EntityAdapter) GetNPC() *NPC {
	return ea.npc
}

// IsNPC always returns true for entity adapters
func (ea *EntityAdapter) IsNPC() bool {
	return true
}

// HandleNPCInteraction processes interactions with other NPCs
func (ea *EntityAdapter) HandleNPCInteraction(otherNPC *NPC, interactionType string) error {
	if ea.npc == nil || otherNPC == nil {
		return fmt.Errorf("invalid NPC for interaction")
	}

	// Handle different interaction types
	switch interactionType {
	case "aggro":
		return ea.handleAggroInteraction(otherNPC)
	case "assist":
		return ea.handleAssistInteraction(otherNPC)
	case "trade":
		return ea.handleTradeInteraction(otherNPC)
	default:
		if ea.logger != nil {
			ea.logger.LogWarning("Unknown NPC interaction type: %s", interactionType)
		}
		return fmt.Errorf("unknown interaction type: %s", interactionType)
	}
}

// ReceiveNPCCommand processes commands from other NPCs
func (ea *EntityAdapter) ReceiveNPCCommand(otherNPC *NPC, command string) error {
	if ea.npc == nil || otherNPC == nil {
		return fmt.Errorf("invalid NPC for command")
	}

	// Process the command
	switch command {
	case "follow":
		return ea.handleFollowCommand(otherNPC)
	case "attack":
		return ea.handleAttackCommand(otherNPC)
	case "retreat":
		return ea.handleRetreatCommand(otherNPC)
	default:
		if ea.logger != nil {
			ea.logger.LogWarning("Unknown NPC command: %s", command)
		}
		return fmt.Errorf("unknown command: %s", command)
	}
}

// handleAggroInteraction processes aggro interactions
func (ea *EntityAdapter) handleAggroInteraction(otherNPC *NPC) error {
	// TODO: Implement aggro logic between NPCs
	if ea.logger != nil {
		ea.logger.LogDebug("NPC %d received aggro from NPC %d",
			ea.npc.GetNPCID(), otherNPC.GetNPCID())
	}
	return nil
}

// handleAssistInteraction processes assist interactions
func (ea *EntityAdapter) handleAssistInteraction(otherNPC *NPC) error {
	// TODO: Implement assist logic between NPCs
	if ea.logger != nil {
		ea.logger.LogDebug("NPC %d received assist request from NPC %d",
			ea.npc.GetNPCID(), otherNPC.GetNPCID())
	}
	return nil
}

// handleTradeInteraction processes trade interactions
func (ea *EntityAdapter) handleTradeInteraction(otherNPC *NPC) error {
	// TODO: Implement trade logic between NPCs
	if ea.logger != nil {
		ea.logger.LogDebug("NPC %d received trade request from NPC %d",
			ea.npc.GetNPCID(), otherNPC.GetNPCID())
	}
	return nil
}

// handleFollowCommand processes follow commands
func (ea *EntityAdapter) handleFollowCommand(otherNPC *NPC) error {
	// TODO: Implement follow logic
	if ea.logger != nil {
		ea.logger.LogDebug("NPC %d received follow command from NPC %d",
			ea.npc.GetNPCID(), otherNPC.GetNPCID())
	}
	return nil
}

// handleAttackCommand processes attack commands
func (ea *EntityAdapter) handleAttackCommand(otherNPC *NPC) error {
	// TODO: Implement attack logic
	if ea.logger != nil {
		ea.logger.LogDebug("NPC %d received attack command from NPC %d",
			ea.npc.GetNPCID(), otherNPC.GetNPCID())
	}
	return nil
}

// handleRetreatCommand processes retreat commands
func (ea *EntityAdapter) handleRetreatCommand(otherNPC *NPC) error {
	// TODO: Implement retreat logic
	if ea.logger != nil {
		ea.logger.LogDebug("NPC %d received retreat command from NPC %d",
			ea.npc.GetNPCID(), otherNPC.GetNPCID())
	}
	return nil
}

// SpellCasterAdapter provides spell casting functionality for NPCs
type SpellCasterAdapter struct {
	npc          *NPC
	spellManager SpellManager
	logger       Logger
}

// NewSpellCasterAdapter creates a new spell caster adapter
func NewSpellCasterAdapter(npc *NPC, spellManager SpellManager, logger Logger) *SpellCasterAdapter {
	return &SpellCasterAdapter{
		npc:          npc,
		spellManager: spellManager,
		logger:       logger,
	}
}

// GetNextSpell selects the next spell to cast based on AI strategy
func (sca *SpellCasterAdapter) GetNextSpell(target interface{}, distance float32) Spell {
	if sca.npc == nil || sca.spellManager == nil {
		return nil
	}

	// Check cast-on-aggro spells first
	if !sca.npc.castOnAggroCompleted {
		spell := sca.getNextCastOnAggroSpell(target)
		if spell != nil {
			return spell
		}
		sca.npc.castOnAggroCompleted = true
	}

	// Get spells based on AI strategy
	strategy := sca.npc.GetAIStrategy()
	return sca.getNextSpellByStrategy(target, distance, strategy)
}

// GetNextBuffSpell selects the next buff spell to cast
func (sca *SpellCasterAdapter) GetNextBuffSpell(target interface{}) Spell {
	if sca.npc == nil || sca.spellManager == nil {
		return nil
	}

	// Check cast-on-spawn spells first
	castOnSpells := sca.npc.castOnSpells[CastOnSpawn]
	for _, npcSpell := range castOnSpells {
		spell := sca.spellManager.GetSpell(npcSpell.GetSpellID(), npcSpell.GetTier())
		if spell != nil {
			// Check if target already has this effect
			if effect := sca.spellManager.GetSpellEffect(target, spell.GetSpellID()); effect != nil {
				if effect.GetTier() < spell.GetTier() {
					return spell // Upgrade existing effect
				}
			} else {
				return spell // New effect
			}
		}
	}

	// Check regular spells for buffs
	for _, npcSpell := range sca.npc.spells {
		spell := sca.spellManager.GetSpell(npcSpell.GetSpellID(), npcSpell.GetTier())
		if spell != nil && spell.IsFriendlySpell() && spell.IsToggleSpell() {
			// Check if target already has this effect
			if effect := sca.spellManager.GetSpellEffect(target, spell.GetSpellID()); effect != nil {
				if effect.GetTier() < spell.GetTier() {
					return spell // Upgrade existing effect
				}
			} else {
				return spell // New effect
			}
		}
	}

	return nil
}

// CastSpell attempts to cast a spell
func (sca *SpellCasterAdapter) CastSpell(target interface{}, spell Spell) error {
	if sca.npc == nil || sca.spellManager == nil || spell == nil {
		return fmt.Errorf("invalid parameters for spell casting")
	}

	// Check casting conditions
	if err := sca.checkCastingConditions(spell); err != nil {
		return fmt.Errorf("casting conditions not met: %w", err)
	}

	// Cast the spell
	if err := sca.spellManager.CastSpell(sca.npc, target, spell); err != nil {
		return fmt.Errorf("failed to cast spell: %w", err)
	}

	if sca.logger != nil {
		sca.logger.LogDebug("NPC %d cast spell %s (%d)",
			sca.npc.GetNPCID(), spell.GetName(), spell.GetSpellID())
	}

	return nil
}

// getNextCastOnAggroSpell selects cast-on-aggro spells
func (sca *SpellCasterAdapter) getNextCastOnAggroSpell(target interface{}) Spell {
	castOnSpells := sca.npc.castOnSpells[CastOnAggro]
	for _, npcSpell := range castOnSpells {
		spell := sca.spellManager.GetSpell(npcSpell.GetSpellID(), npcSpell.GetTier())
		if spell != nil {
			// Check if target doesn't already have this effect
			if effect := sca.spellManager.GetSpellEffect(target, spell.GetSpellID()); effect == nil {
				return spell
			}
		}
	}
	return nil
}

// getNextSpellByStrategy selects spells based on AI strategy
func (sca *SpellCasterAdapter) getNextSpellByStrategy(target interface{}, distance float32, strategy int8) Spell {
	// TODO: Implement more sophisticated spell selection based on strategy

	for _, npcSpell := range sca.npc.spells {
		// Check HP ratio requirements
		if npcSpell.GetRequiredHPRatio() != 0 {
			// TODO: Implement HP ratio checking
		}

		spell := sca.spellManager.GetSpell(npcSpell.GetSpellID(), npcSpell.GetTier())
		if spell == nil {
			continue
		}

		// Check strategy compatibility
		if strategy == AIStrategyOffensive && spell.IsFriendlySpell() {
			continue
		}
		if strategy == AIStrategyDefensive && !spell.IsFriendlySpell() {
			continue
		}

		// Check range and power requirements
		if distance <= spell.GetRange() && distance >= spell.GetMinRange() {
			// TODO: Check power requirements
			return spell
		}
	}

	return nil
}

// checkCastingConditions validates spell casting conditions
func (sca *SpellCasterAdapter) checkCastingConditions(spell Spell) error {
	if sca.npc.Entity == nil {
		return fmt.Errorf("NPC entity is nil")
	}

	// TODO: Implement power checking, cooldown checking, etc.

	return nil
}

// CombatAdapter provides combat functionality for NPCs
type CombatAdapter struct {
	npc           *NPC
	combatManager CombatManager
	logger        Logger
}

// NewCombatAdapter creates a new combat adapter
func NewCombatAdapter(npc *NPC, combatManager CombatManager, logger Logger) *CombatAdapter {
	return &CombatAdapter{
		npc:           npc,
		combatManager: combatManager,
		logger:        logger,
	}
}

// EnterCombat handles entering combat state
func (ca *CombatAdapter) EnterCombat(target interface{}) error {
	if ca.npc == nil {
		return fmt.Errorf("NPC is nil")
	}

	// Start combat through combat manager
	if ca.combatManager != nil {
		if err := ca.combatManager.StartCombat(ca.npc, target); err != nil {
			return fmt.Errorf("failed to start combat: %w", err)
		}
	}

	// Update NPC state
	ca.npc.InCombat(true)

	if ca.logger != nil {
		ca.logger.LogDebug("NPC %d entered combat", ca.npc.GetNPCID())
	}

	return nil
}

// ExitCombat handles exiting combat state
func (ca *CombatAdapter) ExitCombat() error {
	if ca.npc == nil {
		return fmt.Errorf("NPC is nil")
	}

	// End combat through combat manager
	if ca.combatManager != nil {
		if err := ca.combatManager.EndCombat(ca.npc); err != nil {
			return fmt.Errorf("failed to end combat: %w", err)
		}
	}

	// Update NPC state
	ca.npc.InCombat(false)

	if ca.logger != nil {
		ca.logger.LogDebug("NPC %d exited combat", ca.npc.GetNPCID())
	}

	return nil
}

// ProcessCombat handles combat processing
func (ca *CombatAdapter) ProcessCombat() error {
	if ca.npc == nil {
		return fmt.Errorf("NPC is nil")
	}

	if ca.combatManager != nil {
		return ca.combatManager.ProcessCombatRound(ca.npc)
	}

	return nil
}

// MovementAdapter provides movement functionality for NPCs
type MovementAdapter struct {
	npc             *NPC
	movementManager MovementManager
	logger          Logger
}

// NewMovementAdapter creates a new movement adapter
func NewMovementAdapter(npc *NPC, movementManager MovementManager, logger Logger) *MovementAdapter {
	return &MovementAdapter{
		npc:             npc,
		movementManager: movementManager,
		logger:          logger,
	}
}

// MoveToLocation moves the NPC to a specific location
func (ma *MovementAdapter) MoveToLocation(x, y, z float32) error {
	if ma.npc == nil {
		return fmt.Errorf("NPC is nil")
	}

	if ma.movementManager != nil {
		return ma.movementManager.NavigateToLocation(ma.npc, x, y, z)
	}

	return fmt.Errorf("movement manager not available")
}

// StopMovement stops the NPC's movement
func (ma *MovementAdapter) StopMovement() error {
	if ma.npc == nil {
		return fmt.Errorf("NPC is nil")
	}

	if ma.movementManager != nil {
		return ma.movementManager.StopMovement(ma.npc)
	}

	return fmt.Errorf("movement manager not available")
}

// IsMoving checks if the NPC is currently moving
func (ma *MovementAdapter) IsMoving() bool {
	if ma.npc == nil || ma.movementManager == nil {
		return false
	}

	return ma.movementManager.IsMoving(ma.npc)
}

// RunbackToSpawn moves the NPC back to its spawn location
func (ma *MovementAdapter) RunbackToSpawn() error {
	if ma.npc == nil {
		return fmt.Errorf("NPC is nil")
	}

	runbackLocation := ma.npc.GetRunbackLocation()
	if runbackLocation == nil {
		return fmt.Errorf("no runback location set")
	}

	return ma.MoveToLocation(runbackLocation.X, runbackLocation.Y, runbackLocation.Z)
}
