package npc

import (
	"sync"
	"time"

	"eq2emu/internal/common"
	"eq2emu/internal/entity"
	"eq2emu/internal/spawn"
)

// NPCSpell represents a spell configuration for NPCs
type NPCSpell struct {
	ListID             int32 // Spell list identifier
	SpellID            int32 // Spell ID from master spell list
	Tier               int8  // Spell tier
	CastOnSpawn        bool  // Cast when NPC spawns
	CastOnInitialAggro bool  // Cast when first entering combat
	RequiredHPRatio    int8  // HP ratio requirement for casting (-100 to 100)
	mutex              sync.RWMutex
}

// NewNPCSpell creates a new NPCSpell
func NewNPCSpell() *NPCSpell {
	return &NPCSpell{
		ListID:             0,
		SpellID:            0,
		Tier:               1,
		CastOnSpawn:        false,
		CastOnInitialAggro: false,
		RequiredHPRatio:    0,
	}
}

// Copy creates a deep copy of the NPCSpell
func (ns *NPCSpell) Copy() *NPCSpell {
	ns.mutex.RLock()
	defer ns.mutex.RUnlock()

	return &NPCSpell{
		ListID:             ns.ListID,
		SpellID:            ns.SpellID,
		Tier:               ns.Tier,
		CastOnSpawn:        ns.CastOnSpawn,
		CastOnInitialAggro: ns.CastOnInitialAggro,
		RequiredHPRatio:    ns.RequiredHPRatio,
	}
}

// Getters
func (ns *NPCSpell) GetListID() int32 {
	ns.mutex.RLock()
	defer ns.mutex.RUnlock()
	return ns.ListID
}

func (ns *NPCSpell) GetSpellID() int32 {
	ns.mutex.RLock()
	defer ns.mutex.RUnlock()
	return ns.SpellID
}

func (ns *NPCSpell) GetTier() int8 {
	ns.mutex.RLock()
	defer ns.mutex.RUnlock()
	return ns.Tier
}

func (ns *NPCSpell) GetCastOnSpawn() bool {
	ns.mutex.RLock()
	defer ns.mutex.RUnlock()
	return ns.CastOnSpawn
}

func (ns *NPCSpell) GetCastOnInitialAggro() bool {
	ns.mutex.RLock()
	defer ns.mutex.RUnlock()
	return ns.CastOnInitialAggro
}

func (ns *NPCSpell) GetRequiredHPRatio() int8 {
	ns.mutex.RLock()
	defer ns.mutex.RUnlock()
	return ns.RequiredHPRatio
}

// Setters
func (ns *NPCSpell) SetListID(id int32) {
	ns.mutex.Lock()
	defer ns.mutex.Unlock()
	ns.ListID = id
}

func (ns *NPCSpell) SetSpellID(id int32) {
	ns.mutex.Lock()
	defer ns.mutex.Unlock()
	ns.SpellID = id
}

func (ns *NPCSpell) SetTier(tier int8) {
	ns.mutex.Lock()
	defer ns.mutex.Unlock()
	ns.Tier = tier
}

func (ns *NPCSpell) SetCastOnSpawn(cast bool) {
	ns.mutex.Lock()
	defer ns.mutex.Unlock()
	ns.CastOnSpawn = cast
}

func (ns *NPCSpell) SetCastOnInitialAggro(cast bool) {
	ns.mutex.Lock()
	defer ns.mutex.Unlock()
	ns.CastOnInitialAggro = cast
}

func (ns *NPCSpell) SetRequiredHPRatio(ratio int8) {
	ns.mutex.Lock()
	defer ns.mutex.Unlock()
	ns.RequiredHPRatio = ratio
}

// SkillBonus represents a skill bonus from spells
type SkillBonus struct {
	SpellID int32                      // Spell providing the bonus
	Skills  map[int32]*SkillBonusValue // Map of skill ID to bonus value
	mutex   sync.RWMutex
}

// SkillBonusValue represents the actual bonus value for a skill
type SkillBonusValue struct {
	SkillID int32   // Skill receiving the bonus
	Value   float32 // Bonus amount
}

// NewSkillBonus creates a new SkillBonus
func NewSkillBonus(spellID int32) *SkillBonus {
	return &SkillBonus{
		SpellID: spellID,
		Skills:  make(map[int32]*SkillBonusValue),
	}
}

// AddSkill adds a skill bonus
func (sb *SkillBonus) AddSkill(skillID int32, value float32) {
	sb.mutex.Lock()
	defer sb.mutex.Unlock()

	sb.Skills[skillID] = &SkillBonusValue{
		SkillID: skillID,
		Value:   value,
	}
}

// RemoveSkill removes a skill bonus
func (sb *SkillBonus) RemoveSkill(skillID int32) bool {
	sb.mutex.Lock()
	defer sb.mutex.Unlock()

	if _, exists := sb.Skills[skillID]; exists {
		delete(sb.Skills, skillID)
		return true
	}
	return false
}

// GetSkills returns a copy of all skill bonuses
func (sb *SkillBonus) GetSkills() map[int32]*SkillBonusValue {
	sb.mutex.RLock()
	defer sb.mutex.RUnlock()

	result := make(map[int32]*SkillBonusValue)
	for id, bonus := range sb.Skills {
		result[id] = &SkillBonusValue{
			SkillID: bonus.SkillID,
			Value:   bonus.Value,
		}
	}
	return result
}

// MovementLocation represents a movement destination for runback
type MovementLocation struct {
	X                float32 // X coordinate
	Y                float32 // Y coordinate
	Z                float32 // Z coordinate
	GridID           int32   // Grid location ID
	Stage            int32   // Movement stage
	ResetHPOnRunback bool    // Whether to reset HP when reaching location
	UseNavPath       bool    // Whether to use navigation pathfinding
	Mapped           bool    // Whether location is mapped
}

// NewMovementLocation creates a new MovementLocation
func NewMovementLocation(x, y, z float32, gridID int32) *MovementLocation {
	return &MovementLocation{
		X:                x,
		Y:                y,
		Z:                z,
		GridID:           gridID,
		Stage:            0,
		ResetHPOnRunback: false,
		UseNavPath:       false,
		Mapped:           false,
	}
}

// Copy creates a deep copy of the MovementLocation
func (ml *MovementLocation) Copy() *MovementLocation {
	return &MovementLocation{
		X:                ml.X,
		Y:                ml.Y,
		Z:                ml.Z,
		GridID:           ml.GridID,
		Stage:            ml.Stage,
		ResetHPOnRunback: ml.ResetHPOnRunback,
		UseNavPath:       ml.UseNavPath,
		Mapped:           ml.Mapped,
	}
}

// NPC represents a non-player character extending Entity
type NPC struct {
	*entity.Entity // Embedded entity for combat capabilities

	// Core NPC properties
	appearanceID   int32 // Appearance ID for client display
	npcID          int32 // NPC database ID
	aiStrategy     int8  // AI strategy (balanced/offensive/defensive)
	attackType     int8  // Attack type preference
	castPercentage int8  // Percentage chance to cast spells
	maxPetLevel    int8  // Maximum pet level

	// Combat and movement
	aggroRadius        float32           // Aggro detection radius
	baseAggroRadius    float32           // Base aggro radius (for resets)
	runback            *MovementLocation // Runback location when leaving combat
	runningBack        bool              // Currently running back to spawn point
	runbackHeadingDir1 int16             // Original heading direction 1
	runbackHeadingDir2 int16             // Original heading direction 2
	pauseTimer         *Timer            // Movement pause timer

	// Spell and skill management
	primarySpellList     int32                 // Primary spell list ID
	secondarySpellList   int32                 // Secondary spell list ID
	primarySkillList     int32                 // Primary skill list ID
	secondarySkillList   int32                 // Secondary skill list ID
	equipmentListID      int32                 // Equipment list ID
	skills               map[string]*Skill     // NPC skills by name
	spells               []*NPCSpell           // Available spells
	castOnSpells         map[int8][]*NPCSpell  // Spells to cast by trigger type
	skillBonuses         map[int32]*SkillBonus // Skill bonuses from spells
	hasSpells            bool                  // Whether NPC has any spells
	castOnAggroCompleted bool                  // Whether cast-on-aggro spells are done

	// Brain/AI system (placeholder for now)
	brain Brain // AI brain for decision making

	// Shard system (for cross-server functionality)
	shardID               int32 // Shard identifier
	shardCharID           int32 // Character ID on shard
	shardCreatedTimestamp int64 // Timestamp when created on shard

	// Thread safety
	mutex      sync.RWMutex // Main NPC mutex
	brainMutex sync.RWMutex // Brain-specific mutex

	// Atomic flags for thread-safe state management
	callRunback bool // Flag to trigger runback
}

// Timer represents a simple timer for NPC operations
type Timer struct {
	duration  time.Duration
	startTime time.Time
	enabled   bool
	mutex     sync.RWMutex
}

// NewTimer creates a new timer
func NewTimer() *Timer {
	return &Timer{
		enabled: false,
	}
}

// Start starts the timer with the given duration
func (t *Timer) Start(durationMS int32, reset bool) {
	t.mutex.Lock()
	defer t.mutex.Unlock()

	if reset || !t.enabled {
		t.duration = time.Duration(durationMS) * time.Millisecond
		t.startTime = time.Now()
		t.enabled = true
	}
}

// Check checks if the timer has expired
func (t *Timer) Check() bool {
	t.mutex.RLock()
	defer t.mutex.RUnlock()

	if !t.enabled {
		return false
	}

	return time.Since(t.startTime) >= t.duration
}

// Enabled returns whether the timer is currently enabled
func (t *Timer) Enabled() bool {
	t.mutex.RLock()
	defer t.mutex.RUnlock()
	return t.enabled
}

// Disable disables the timer
func (t *Timer) Disable() {
	t.mutex.Lock()
	defer t.mutex.Unlock()
	t.enabled = false
}

// Skill represents an NPC skill (simplified from C++ version)
type Skill struct {
	SkillID    int32  // Skill identifier
	Name       string // Skill name
	CurrentVal int16  // Current skill value
	MaxVal     int16  // Maximum skill value
	mutex      sync.RWMutex
}

// NewSkill creates a new skill
func NewSkill(id int32, name string, current, max int16) *Skill {
	return &Skill{
		SkillID:    id,
		Name:       name,
		CurrentVal: current,
		MaxVal:     max,
	}
}

// GetCurrentVal returns the current skill value
func (s *Skill) GetCurrentVal() int16 {
	s.mutex.RLock()
	defer s.mutex.RUnlock()
	return s.CurrentVal
}

// SetCurrentVal sets the current skill value
func (s *Skill) SetCurrentVal(val int16) {
	s.mutex.Lock()
	defer s.mutex.Unlock()
	s.CurrentVal = val
}

// IncreaseSkill increases the skill value (with random chance)
func (s *Skill) IncreaseSkill() bool {
	s.mutex.Lock()
	defer s.mutex.Unlock()

	if s.CurrentVal < s.MaxVal {
		s.CurrentVal++
		return true
	}
	return false
}

// Brain interface represents the AI brain system (placeholder)
type Brain interface {
	Think() error
	GetBody() *NPC
	SetBody(*NPC)
	IsActive() bool
	SetActive(bool)
}

// DefaultBrain provides a simple brain implementation
type DefaultBrain struct {
	npc    *NPC
	active bool
	mutex  sync.RWMutex
}

// NewDefaultBrain creates a new default brain
func NewDefaultBrain(npc *NPC) *DefaultBrain {
	return &DefaultBrain{
		npc:    npc,
		active: true,
	}
}

// Think processes AI logic (placeholder implementation)
func (b *DefaultBrain) Think() error {
	// TODO: Implement AI thinking logic
	return nil
}

// GetBody returns the NPC this brain controls
func (b *DefaultBrain) GetBody() *NPC {
	b.mutex.RLock()
	defer b.mutex.RUnlock()
	return b.npc
}

// SetBody sets the NPC this brain controls
func (b *DefaultBrain) SetBody(npc *NPC) {
	b.mutex.Lock()
	defer b.mutex.Unlock()
	b.npc = npc
}

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

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

// NPCStatistics contains NPC system statistics
type NPCStatistics struct {
	TotalNPCs          int            `json:"total_npcs"`
	NPCsInCombat       int            `json:"npcs_in_combat"`
	NPCsWithSpells     int            `json:"npcs_with_spells"`
	NPCsWithSkills     int            `json:"npcs_with_skills"`
	AIStrategyCounts   map[string]int `json:"ai_strategy_counts"`
	SpellCastCount     int64          `json:"spell_cast_count"`
	SkillUsageCount    int64          `json:"skill_usage_count"`
	RunbackCount       int64          `json:"runback_count"`
	AverageAggroRadius float32        `json:"average_aggro_radius"`
}
