package spawn

import (
	"math"
	"sync"
	"sync/atomic"
	"time"

	"eq2emu/internal/common"
)

// Damage packet constants
const (
	DamagePacketTypeSiphonSpell    = 0x41
	DamagePacketTypeSiphonSpell2   = 0x49
	DamagePacketTypeMultipleDamage = 0x80
	DamagePacketTypeSimpleDamage   = 0xC0
	DamagePacketTypeSpellDamage    = 0xC1
	DamagePacketTypeSimpleCritDmg  = 0xC4
	DamagePacketTypeSpellCritDmg   = 0xC5
	DamagePacketTypeSpellDamage2   = 0xC8
	DamagePacketTypeSpellDamage3   = 0xC9
	DamagePacketTypeRangeDamage    = 0xE2
	DamagePacketTypeRangeSpellDmg  = 0xE3
	DamagePacketTypeRangeSpellDmg2 = 0xEA
)

// Damage packet results
const (
	DamagePacketResultNoDamage      = 0
	DamagePacketResultSuccessful    = 1
	DamagePacketResultMiss          = 4
	DamagePacketResultDodge         = 8
	DamagePacketResultParry         = 12
	DamagePacketResultRiposte       = 16
	DamagePacketResultBlock         = 20
	DamagePacketResultDeathBlow     = 24
	DamagePacketResultInvulnerable  = 28
	DamagePacketResultResist        = 36
	DamagePacketResultReflect       = 40
	DamagePacketResultImmune        = 44
	DamagePacketResultDeflect       = 48
	DamagePacketResultCounter       = 52
	DamagePacketResultFocus         = 56
	DamagePacketResultCounterStrike = 60
	DamagePacketResultBash          = 64
)

// Damage types
const (
	DamagePacketDamageTypeSlash   = 0
	DamagePacketDamageTypeCrush   = 1
	DamagePacketDamageTypePierce  = 2
	DamagePacketDamageTypeHeat    = 3
	DamagePacketDamageTypeCold    = 4
	DamagePacketDamageTypeMagic   = 5
	DamagePacketDamageTypeMental  = 6
	DamagePacketDamageTypeDivine  = 7
	DamagePacketDamageTypeDisease = 8
	DamagePacketDamageTypePoison  = 9
	DamagePacketDamageTypeDrown   = 10
	DamagePacketDamageTypeFalling = 11
	DamagePacketDamageTypePain    = 12
	DamagePacketDamageTypeHit     = 13
	DamagePacketDamageTypeFocus   = 14
)

// Activity status flags
const (
	ActivityStatusRoleplaying       = 1
	ActivityStatusAnonymous         = 2
	ActivityStatusLinkdead          = 4
	ActivityStatusCamping           = 8
	ActivityStatusLFG               = 16
	ActivityStatusLFW               = 32
	ActivityStatusSolid             = 64
	ActivityStatusImmunityGained    = 8192
	ActivityStatusImmunityRemaining = 16384
	ActivityStatusAFK               = 32768
)

// Position states
const (
	PosStateKneeling       = 64
	PosStateSolid          = 128
	PosStateNotargetCursor = 256
	PosStateCrouching      = 512
)

// Encounter states
const (
	EncounterStateNone        = 0
	EncounterStateAvailable   = 1
	EncounterStateBroken      = 2
	EncounterStateLocked      = 3
	EncounterStateOvermatched = 4
	EncounterStateNoReward    = 5
)

// Loot methods and modes
type GroupLootMethod int

const (
	MethodLeader          GroupLootMethod = 0
	MethodFFA             GroupLootMethod = 1
	MethodLotto           GroupLootMethod = 2
	MethodNeedBeforeGreed GroupLootMethod = 3
	MethodRoundRobin      GroupLootMethod = 4
)

type AutoLootMode int

const (
	AutoLootDisabled = 0
	AutoLootAccept   = 1
	AutoLootDecline  = 2
)

type LootTier int

const (
	ItemsAll           LootTier = 0
	ItemsTreasuredPlus LootTier = 1
	ItemsLegendaryPlus LootTier = 2
	ItemsFabledPlus    LootTier = 3
)

// SpawnProximityType defines types of spawn proximity tracking
type SpawnProximityType int

const (
	SpawnProximityDatabaseID = 0
	SpawnProximityLocationID = 1
)

// BasicInfoStruct contains basic spawn statistics
type BasicInfoStruct struct {
	CurHP                          int32
	MaxHP                          int32
	HPBase                         int32
	HPBaseInstance                 int32
	CurPower                       int32
	MaxPower                       int32
	PowerBase                      int32
	PowerBaseInstance              int32
	CurSavagery                    int32
	MaxSavagery                    int32
	SavageryBase                   int32
	CurDissonance                  int32
	MaxDissonance                  int32
	DissonanceBase                 int32
	AssignedAA                     int16
	UnassignedAA                   int16
	TradeskillAA                   int16
	UnassignedTradeskillAA         int16
	PrestigeAA                     int16
	UnassignedPrestigeAA           int16
	TradeskillPrestigeAA           int16
	UnassignedTradeskillPrestigeAA int16
	AAXPRewards                    int32
}

// MovementLocation represents a point in a movement path
type MovementLocation struct {
	X                float32
	Y                float32
	Z                float32
	Speed            float32
	Attackable       bool
	LuaFunction      string
	Mapped           bool
	GridID           int32
	Stage            int8
	ResetHPOnRunback bool
	UseNavPath       bool
}

// MovementData contains movement configuration
type MovementData struct {
	X                          float32
	Y                          float32
	Z                          float32
	Speed                      float32
	Delay                      int32
	LuaFunction                string
	Heading                    float32
	UseMovementLocationHeading bool
	UseNavPath                 bool
}

// SpawnUpdate tracks what aspects of a spawn need updating
type SpawnUpdate struct {
	SpawnID     int32
	InfoChanged bool
	VisChanged  bool
	PosChanged  bool
	// TODO: Add Client reference when client system is implemented
	// Client      *Client
}

// SpawnData contains spawn packet data
type SpawnData struct {
	// TODO: Add Spawn reference when implemented
	// Spawn *Spawn
	Data []byte
	Size int32
}

// TimedGridData tracks movement through grid system
type TimedGridData struct {
	Timestamp   int32
	GridID      int32
	X           float32
	Y           float32
	Z           float32
	OffsetY     float32
	ZoneGroundY float32
	NPCSave     bool
	WidgetID    int32
}

// EntityCommand represents an interactable command
type EntityCommand struct {
	Name             string
	Distance         float32
	Command          string
	ErrorText        string
	CastTime         int16
	SpellVisual      int32
	AllowOrDeny      map[int32]bool // player IDs and whether they're allowed
	DefaultAllowList bool           // if false, it's a deny list
}

// SpawnProximity tracks proximity-based events
type SpawnProximity struct {
	X                       float32
	Y                       float32
	Z                       float32
	SpawnValue              int32
	SpawnType               int8
	Distance                float32
	InRangeLuaFunction      string
	LeavingRangeLuaFunction string
	SpawnsInProximity       map[int32]bool
}

var nextSpawnID int32 = 1

// NextID generates the next unique spawn ID
// Handles special cases like avoiding IDs ending in 255 and wraparound
func NextID() int32 {
	for {
		id := atomic.AddInt32(&nextSpawnID, 1)

		// Handle wraparound
		if id == 0xFFFFFFFE {
			atomic.StoreInt32(&nextSpawnID, 1)
			continue
		}

		// Avoid IDs ending in 255 to prevent client confusion/crashes
		if (id-255)%256 == 0 {
			continue
		}

		return id
	}
}

// Spawn represents a game entity that can appear in the world
type Spawn struct {
	// Basic identification
	id         int32
	databaseID int32

	// Appearance and positioning
	appearance common.AppearanceData
	size       int16
	sizeOffset int8

	// State flags (using atomic for thread safety)
	changed         atomic.Bool
	positionChanged atomic.Bool
	infoChanged     atomic.Bool
	visChanged      atomic.Bool
	isRunning       atomic.Bool
	sizeChanged     atomic.Bool
	following       atomic.Bool
	isAlive         atomic.Bool
	deletedSpawn    atomic.Bool
	resetMovement   atomic.Bool
	knockedBack     atomic.Bool

	// Game state
	basicInfo    BasicInfoStruct
	factionID    int32
	spawnType    int8
	target       int32
	lastAttacker int32

	// Commands and interaction
	primaryCommandListID   int32
	secondaryCommandListID int32
	primaryCommandList     []*EntityCommand
	secondaryCommandList   []*EntityCommand

	// Movement and positioning
	lastMovementUpdate int32
	lastLocationUpdate int32
	lastGridUpdate     int32
	forceMapCheck      bool
	movementLocations  []*MovementLocation
	movementLoop       []*MovementData
	movementIndex      int16
	runningTo          int32
	invulnerable       bool

	// Group and spawn relationships
	groupID        int32
	spawnGroupList []*Spawn

	// Location and respawn data
	spawnLocationID       int32
	spawnEntryID          int32
	spawnLocationSpawnsID int32
	respawn               int32
	respawnOffsetLow      int32
	respawnOffsetHigh     int32
	duplicatedSpawn       bool
	expireTime            int32
	expireOffset          int32
	xOffset               float32
	yOffset               float32
	zOffset               float32
	deviation             int32

	// Loot system
	lootItems       []*Item // TODO: Define Item type
	lootCoins       int32
	lootGroupID     int32
	lootMethod      GroupLootMethod
	lootRarity      int8
	looterSpawnID   int32
	lootComplete    map[int32]bool
	isLootComplete  bool
	isLootDispensed bool
	lootName        string
	lootTier        int32
	lootDropType    int32
	trapTriggered   bool
	trapState       int32
	chestDropTime   int32
	trapOpenedTime  int32

	// Merchant data
	merchantID       int32
	merchantType     int8
	merchantMinLevel int32
	merchantMaxLevel int32
	isCollector      bool

	// Transportation
	transporterID    int32
	isTransportSpawn bool
	railID           int64
	railPassengers   map[int32]bool

	// Scripting and AI
	spawnScript      string
	spawnScriptSetDB bool
	questIDs         []int32

	// Quest and access requirements
	hasQuestsRequired        bool
	hasHistoryRequired       bool
	reqQuestsPrivate         bool
	reqQuestsOverride        int16
	reqQuestsContinuedAccess bool
	requiredQuests           map[int32][]int16
	// TODO: Add LUAHistory type
	// requiredHistory          map[int32]*LUAHistory
	allowedAccess map[int32]int8

	// Visual and display state
	tmpVisualState int
	tmpActionState int
	illusionModel  int16

	// Items and pickups
	pickupItemID       int32
	pickupUniqueItemID int32
	houseCharacterID   int32

	// Proximity tracking
	hasSpawnProximities bool
	spawnProximities    []*SpawnProximity

	// Creature flags
	isFlyingCreature      bool
	isWaterCreature       bool
	isPet                 bool
	scaredByStrongPlayers bool

	// Following system
	followTarget   int32
	followDistance int32

	// Temporary variables (for Lua scripting)
	tempVariableTypes map[string]int8
	tempVariables     map[string]string
	tempVariableSpawn map[string]int32
	// TODO: Add other temp variable types when systems are implemented
	// tempVariableZone  map[string]*ZoneServer
	// tempVariableItem  map[string]*Item
	// tempVariableQuest map[string]*Quest

	// Animation and timing
	spawnAnim             int32
	addedToWorldTimestamp int32
	spawnAnimLeeway       int16
	lastHeadingAngle      float32

	// Region and mapping
	// TODO: Add RegionMap and Map types
	// regionMap   *RegionMap
	// currentMap  *Map
	// regions     map[map[*RegionNode]*ZBSPNode]*RegionStatus
	establishedGridID map[int32]*TimedGridData
	ignoredWidgets    map[int32]bool
	triggerWidgetID   int32

	// Equipment
	// TODO: Add EquipmentItemList type
	// equipmentList           *EquipmentItemList
	// appearanceEquipmentList *EquipmentItemList

	// Zone reference
	// TODO: Add ZoneServer reference when implemented
	// zone *ZoneServer

	// Knockback physics
	knockedBackTimeStep  float32
	knockedBackHDistance float32
	knockedBackVDistance float32
	knockedBackDuration  float32
	knockedBackStartX    float32
	knockedBackStartY    float32
	knockedBackStartZ    float32
	knockedBackEndTime   int32
	knockedAngle         float32
	// TODO: Add glm::vec3 equivalent
	// knockedVelocity       Vec3

	// Database omission flag
	isOmittedByDBFlag bool

	// Sound settings
	disableSounds bool

	// Thread safety
	updateMutex          sync.RWMutex
	spawnMutex           sync.RWMutex
	lootItemsMutex       sync.RWMutex
	commandMutex         sync.Mutex
	regionMutex          sync.Mutex
	railMutex            sync.Mutex
	gridMutex            sync.Mutex
	movementMutex        sync.RWMutex
	requiredQuestsMutex  sync.RWMutex
	requiredHistoryMutex sync.RWMutex
	ignoredWidgetsMutex  sync.RWMutex
}

// NewSpawn creates a new spawn instance with default values
// Initializes all fields to appropriate defaults matching the C++ constructor
func NewSpawn() *Spawn {
	s := &Spawn{
		id:                       NextID(),
		size:                     32,
		sizeOffset:               0,
		factionID:                0,
		spawnType:                0,
		target:                   0,
		lastAttacker:             0,
		primaryCommandListID:     0,
		secondaryCommandListID:   0,
		primaryCommandList:       make([]*EntityCommand, 0),
		secondaryCommandList:     make([]*EntityCommand, 0),
		groupID:                  0,
		spawnLocationID:          0,
		spawnEntryID:             0,
		spawnLocationSpawnsID:    0,
		respawn:                  0,
		respawnOffsetLow:         0,
		respawnOffsetHigh:        0,
		duplicatedSpawn:          true,
		expireTime:               0,
		expireOffset:             0,
		xOffset:                  0,
		yOffset:                  0,
		zOffset:                  0,
		deviation:                0,
		lootItems:                make([]*Item, 0),
		lootCoins:                0,
		lootGroupID:              0,
		lootMethod:               MethodFFA,
		lootRarity:               0,
		looterSpawnID:            0,
		lootComplete:             make(map[int32]bool),
		isLootComplete:           false,
		isLootDispensed:          false,
		lootTier:                 0,
		lootDropType:             0,
		trapTriggered:            false,
		trapState:                0,
		chestDropTime:            0,
		trapOpenedTime:           0,
		merchantID:               0,
		merchantType:             0,
		merchantMinLevel:         0,
		merchantMaxLevel:         0,
		isCollector:              false,
		transporterID:            0,
		isTransportSpawn:         false,
		railID:                   0,
		railPassengers:           make(map[int32]bool),
		questIDs:                 make([]int32, 0),
		hasQuestsRequired:        false,
		hasHistoryRequired:       false,
		reqQuestsPrivate:         false,
		reqQuestsOverride:        0,
		reqQuestsContinuedAccess: false,
		requiredQuests:           make(map[int32][]int16),
		allowedAccess:            make(map[int32]int8),
		tmpVisualState:           -1,
		tmpActionState:           -1,
		illusionModel:            0,
		pickupItemID:             0,
		pickupUniqueItemID:       0,
		houseCharacterID:         0,
		hasSpawnProximities:      false,
		spawnProximities:         make([]*SpawnProximity, 0),
		isFlyingCreature:         false,
		isWaterCreature:          false,
		isPet:                    false,
		scaredByStrongPlayers:    false,
		followTarget:             0,
		followDistance:           0,
		tempVariableTypes:        make(map[string]int8),
		tempVariables:            make(map[string]string),
		tempVariableSpawn:        make(map[string]int32),
		spawnAnim:                0,
		addedToWorldTimestamp:    0,
		spawnAnimLeeway:          0,
		lastHeadingAngle:         0.0,
		lastMovementUpdate:       int32(time.Now().Unix()),
		lastLocationUpdate:       0,
		lastGridUpdate:           0,
		forceMapCheck:            false,
		movementLocations:        make([]*MovementLocation, 0),
		movementLoop:             make([]*MovementData, 0),
		movementIndex:            0,
		runningTo:                0,
		invulnerable:             false,
		spawnGroupList:           make([]*Spawn, 0),
		establishedGridID:        make(map[int32]*TimedGridData),
		ignoredWidgets:           make(map[int32]bool),
		triggerWidgetID:          0,
		isOmittedByDBFlag:        false,
		disableSounds:            false,
	}

	// Initialize appearance with defaults
	s.appearance.Pos.State = 0x4080
	s.appearance.Difficulty = 6
	s.appearance.Pos.CollisionRadius = 32
	s.appearance.Pos.Speed1 = 0

	// Set alive state
	s.isAlive.Store(true)

	// Initialize encounter state
	s.SetLockedNoLoot(EncounterStateAvailable)

	return s
}

// GetID returns the spawn's unique identifier
func (s *Spawn) GetID() int32 {
	return s.id
}

// SetID updates the spawn's unique identifier
func (s *Spawn) SetID(id int32) {
	s.id = id
}

// GetDatabaseID returns the database ID for this spawn
func (s *Spawn) GetDatabaseID() int32 {
	return s.databaseID
}

// SetDatabaseID updates the database ID for this spawn
func (s *Spawn) SetDatabaseID(id int32) {
	s.databaseID = id
}

// GetName returns the spawn's display name
func (s *Spawn) GetName() string {
	return string(s.appearance.Name[:])
}

// SetName updates the spawn's display name and marks info as changed
func (s *Spawn) SetName(name string) {
	s.updateMutex.Lock()
	defer s.updateMutex.Unlock()

	copy(s.appearance.Name[:], name)
	s.infoChanged.Store(true)
	s.changed.Store(true)
	s.addChangedZoneSpawn()
}

// GetLevel returns the spawn's level
func (s *Spawn) GetLevel() int16 {
	return s.appearance.Level
}

// SetLevel updates the spawn's level and marks info as changed
func (s *Spawn) SetLevel(level int16) {
	s.updateMutex.Lock()
	defer s.updateMutex.Unlock()

	s.appearance.Level = level
	s.infoChanged.Store(true)
	s.changed.Store(true)
	s.addChangedZoneSpawn()
}

// GetX returns the spawn's X coordinate
func (s *Spawn) GetX() float32 {
	return s.appearance.Pos.X
}

// SetX updates the spawn's X coordinate and marks position as changed
func (s *Spawn) SetX(x float32) {
	s.updateMutex.Lock()
	defer s.updateMutex.Unlock()

	s.appearance.Pos.X = x
	s.positionChanged.Store(true)
	s.infoChanged.Store(true)
	s.visChanged.Store(true)
	s.changed.Store(true)
	s.addChangedZoneSpawn()
}

// GetY returns the spawn's Y coordinate
func (s *Spawn) GetY() float32 {
	return s.appearance.Pos.Y
}

// SetY updates the spawn's Y coordinate with optional Y map fixing
func (s *Spawn) SetY(y float32, disableYMapFix bool) {
	s.updateMutex.Lock()
	defer s.updateMutex.Unlock()

	s.appearance.Pos.Y = y
	s.positionChanged.Store(true)
	s.infoChanged.Store(true)
	s.visChanged.Store(true)
	s.changed.Store(true)
	s.addChangedZoneSpawn()

	// TODO: Implement Y map fixing when map system is available
	if !disableYMapFix {
		// FixZ() would be called here
	}
}

// GetZ returns the spawn's Z coordinate
func (s *Spawn) GetZ() float32 {
	return s.appearance.Pos.Z
}

// SetZ updates the spawn's Z coordinate and marks position as changed
func (s *Spawn) SetZ(z float32) {
	s.updateMutex.Lock()
	defer s.updateMutex.Unlock()

	s.appearance.Pos.Z = z
	s.positionChanged.Store(true)
	s.infoChanged.Store(true)
	s.visChanged.Store(true)
	s.changed.Store(true)
	s.addChangedZoneSpawn()
}

// GetHeading returns the spawn's heading in degrees
func (s *Spawn) GetHeading() float32 {
	if s.appearance.Pos.Dir1 == 0 {
		return 0
	}

	heading := float32(s.appearance.Pos.Dir1) / 64.0
	if heading >= 180 {
		heading -= 180
	} else {
		heading += 180
	}
	return heading
}

// SetHeading updates the spawn's heading using raw direction values
func (s *Spawn) SetHeading(dir1, dir2 int16) {
	s.updateMutex.Lock()
	defer s.updateMutex.Unlock()

	s.appearance.Pos.Dir1 = dir1
	s.appearance.Pos.Dir2 = dir2
	s.positionChanged.Store(true)
	s.infoChanged.Store(true)
	s.visChanged.Store(true)
	s.changed.Store(true)
	s.addChangedZoneSpawn()
}

// SetHeadingFromFloat updates the spawn's heading using degrees
func (s *Spawn) SetHeadingFromFloat(heading float32) {
	s.lastHeadingAngle = heading
	if heading != 180 {
		heading = (heading - 180) * 64
	}
	s.SetHeading(int16(heading), int16(heading))
}

// GetDistance calculates distance to specific coordinates
func (s *Spawn) GetDistance(x, y, z float32, ignoreY bool) float32 {
	dx := s.GetX() - x
	dy := s.GetY() - y
	dz := s.GetZ() - z

	if ignoreY {
		return float32(math.Sqrt(float64(dx*dx + dz*dz)))
	}
	return float32(math.Sqrt(float64(dx*dx + dy*dy + dz*dz)))
}

// GetDistanceToSpawn calculates distance to another spawn
func (s *Spawn) GetDistanceToSpawn(target *Spawn, ignoreY, includeRadius bool) float32 {
	if target == nil {
		return 0
	}

	distance := s.GetDistance(target.GetX(), target.GetY(), target.GetZ(), ignoreY)

	if includeRadius {
		distance -= s.CalculateRadius(target)
	}

	return distance
}

// CalculateRadius calculates the combined collision radius for two spawns
func (s *Spawn) CalculateRadius(target *Spawn) float32 {
	if target == nil {
		return 0
	}

	myRadius := float32(s.appearance.Pos.CollisionRadius)
	targetRadius := float32(target.appearance.Pos.CollisionRadius)

	return myRadius + targetRadius
}

// IsAlive returns whether the spawn is currently alive
func (s *Spawn) IsAlive() bool {
	return s.isAlive.Load()
}

// SetAlive updates the spawn's alive state
func (s *Spawn) SetAlive(alive bool) {
	s.isAlive.Store(alive)
}

// GetHP returns the spawn's current hit points
func (s *Spawn) GetHP() int32 {
	return s.basicInfo.CurHP
}

// SetHP updates the spawn's current hit points
func (s *Spawn) SetHP(hp int32) {
	s.updateMutex.Lock()
	defer s.updateMutex.Unlock()

	s.basicInfo.CurHP = hp
	s.infoChanged.Store(true)
	s.changed.Store(true)
	s.addChangedZoneSpawn()
}

// GetTotalHP returns the spawn's maximum hit points
func (s *Spawn) GetTotalHP() int32 {
	return s.basicInfo.MaxHP
}

// SetTotalHP updates the spawn's maximum hit points
func (s *Spawn) SetTotalHP(maxHP int32) {
	s.basicInfo.MaxHP = maxHP
}

// GetPower returns the spawn's current power points
func (s *Spawn) GetPower() int32 {
	return s.basicInfo.CurPower
}

// SetPower updates the spawn's current power points
func (s *Spawn) SetPower(power int32) {
	s.updateMutex.Lock()
	defer s.updateMutex.Unlock()

	s.basicInfo.CurPower = power
	s.infoChanged.Store(true)
	s.changed.Store(true)
	s.addChangedZoneSpawn()
}

// GetTotalPower returns the spawn's maximum power points
func (s *Spawn) GetTotalPower() int32 {
	return s.basicInfo.MaxPower
}

// SetTotalPower updates the spawn's maximum power points
func (s *Spawn) SetTotalPower(maxPower int32) {
	s.basicInfo.MaxPower = maxPower
}

// GetLockedNoLoot returns the spawn's loot lock state
func (s *Spawn) GetLockedNoLoot() int8 {
	return s.appearance.LockedNoLoot
}

// SetLockedNoLoot updates the spawn's loot lock state
func (s *Spawn) SetLockedNoLoot(state int8) {
	s.updateMutex.Lock()
	defer s.updateMutex.Unlock()

	s.appearance.LockedNoLoot = state
	s.visChanged.Store(true)
	s.changed.Store(true)
	s.addChangedZoneSpawn()
}

// AddPrimaryEntityCommand adds a new primary command to the spawn
func (s *Spawn) AddPrimaryEntityCommand(name string, distance float32, command, errorText string, castTime int16, spellVisual int32, defaultAllowList bool) {
	s.commandMutex.Lock()
	defer s.commandMutex.Unlock()

	entityCommand := &EntityCommand{
		Name:             name,
		Distance:         distance,
		Command:          command,
		ErrorText:        errorText,
		CastTime:         castTime,
		SpellVisual:      spellVisual,
		AllowOrDeny:      make(map[int32]bool),
		DefaultAllowList: defaultAllowList,
	}

	s.primaryCommandList = append(s.primaryCommandList, entityCommand)
}

// RemovePrimaryCommands clears all primary commands
func (s *Spawn) RemovePrimaryCommands() {
	s.commandMutex.Lock()
	defer s.commandMutex.Unlock()

	s.primaryCommandList = make([]*EntityCommand, 0)
}

// GetPrimaryCommands returns the list of primary commands
func (s *Spawn) GetPrimaryCommands() []*EntityCommand {
	s.commandMutex.Lock()
	defer s.commandMutex.Unlock()

	// Return a copy to prevent race conditions
	commands := make([]*EntityCommand, len(s.primaryCommandList))
	copy(commands, s.primaryCommandList)
	return commands
}

// GetSecondaryCommands returns the list of secondary commands
func (s *Spawn) GetSecondaryCommands() []*EntityCommand {
	s.commandMutex.Lock()
	defer s.commandMutex.Unlock()

	// Return a copy to prevent race conditions
	commands := make([]*EntityCommand, len(s.secondaryCommandList))
	copy(commands, s.secondaryCommandList)
	return commands
}

// GetFactionID returns the spawn's faction ID
func (s *Spawn) GetFactionID() int32 {
	return s.factionID
}

// SetFactionID updates the spawn's faction ID
func (s *Spawn) SetFactionID(factionID int32) {
	s.updateMutex.Lock()
	defer s.updateMutex.Unlock()

	s.factionID = factionID
	s.infoChanged.Store(true)
	s.changed.Store(true)
	s.addChangedZoneSpawn()
}

// GetTarget returns the spawn's current target ID
func (s *Spawn) GetTarget() int32 {
	return s.target
}

// SetTarget updates the spawn's current target
func (s *Spawn) SetTarget(targetID int32) {
	s.updateMutex.Lock()
	defer s.updateMutex.Unlock()

	s.target = targetID
	s.infoChanged.Store(true)
	s.changed.Store(true)
	s.addChangedZoneSpawn()
}

// GetSize returns the spawn's size
func (s *Spawn) GetSize() int16 {
	return s.size
}

// SetSize updates the spawn's size
func (s *Spawn) SetSize(size int16) {
	s.updateMutex.Lock()
	defer s.updateMutex.Unlock()

	s.size = size
	s.sizeChanged.Store(true)
	s.infoChanged.Store(true)
	s.changed.Store(true)
	s.addChangedZoneSpawn()
}

// GetSpawnType returns the spawn's type
func (s *Spawn) GetSpawnType() int8 {
	return s.spawnType
}

// SetSpawnType updates the spawn's type
func (s *Spawn) SetSpawnType(spawnType int8) {
	s.updateMutex.Lock()
	defer s.updateMutex.Unlock()

	s.spawnType = spawnType
	s.infoChanged.Store(true)
	s.changed.Store(true)
	s.addChangedZoneSpawn()
}

// SetSpawnScript updates the spawn's Lua script
func (s *Spawn) SetSpawnScript(script string, dbSet bool) {
	s.spawnScript = script
	s.spawnScriptSetDB = dbSet
}

// GetSpawnScript returns the spawn's Lua script
func (s *Spawn) GetSpawnScript() string {
	return s.spawnScript
}

// AddTempVariable stores a temporary string variable for Lua scripting
func (s *Spawn) AddTempVariable(name, value string) {
	s.updateMutex.Lock()
	defer s.updateMutex.Unlock()

	s.tempVariables[name] = value
	s.tempVariableTypes[name] = 0 // String type
}

// GetTempVariable retrieves a temporary string variable
func (s *Spawn) GetTempVariable(name string) string {
	s.updateMutex.RLock()
	defer s.updateMutex.RUnlock()

	if value, exists := s.tempVariables[name]; exists {
		return value
	}
	return ""
}

// DeleteTempVariable removes a temporary variable
func (s *Spawn) DeleteTempVariable(name string) {
	s.updateMutex.Lock()
	defer s.updateMutex.Unlock()

	delete(s.tempVariables, name)
	delete(s.tempVariableTypes, name)
	delete(s.tempVariableSpawn, name)
}

// IsNPC returns whether this spawn is an NPC (to be overridden by subclasses)
func (s *Spawn) IsNPC() bool {
	return false
}

// IsPlayer returns whether this spawn is a player (to be overridden by subclasses)
func (s *Spawn) IsPlayer() bool {
	return false
}

// IsObject returns whether this spawn is an object (to be overridden by subclasses)
func (s *Spawn) IsObject() bool {
	return false
}

// IsWidget returns whether this spawn is a widget (to be overridden by subclasses)
func (s *Spawn) IsWidget() bool {
	return false
}

// IsSign returns whether this spawn is a sign (to be overridden by subclasses)
func (s *Spawn) IsSign() bool {
	return false
}

// IsGroundSpawn returns whether this spawn is a ground spawn (to be overridden by subclasses)
func (s *Spawn) IsGroundSpawn() bool {
	return false
}

// IsEntity returns whether this spawn is an entity (to be overridden by subclasses)
func (s *Spawn) IsEntity() bool {
	return false
}

// IsBot returns whether this spawn is a bot (to be overridden by subclasses)
func (s *Spawn) IsBot() bool {
	return false
}

// GetAppearanceData returns the spawn's appearance data
func (s *Spawn) GetAppearanceData() *common.AppearanceData {
	s.updateMutex.RLock()
	defer s.updateMutex.RUnlock()
	return &s.appearance
}

// GetBasicInfo returns the spawn's basic info structure
func (s *Spawn) GetBasicInfo() *BasicInfoStruct {
	s.updateMutex.RLock()
	defer s.updateMutex.RUnlock()
	return &s.basicInfo
}

// SetBasicInfo updates the spawn's basic info structure
func (s *Spawn) SetBasicInfo(info *BasicInfoStruct) {
	s.updateMutex.Lock()
	defer s.updateMutex.Unlock()

	if info != nil {
		s.basicInfo = *info
		s.infoChanged.Store(true)
		s.changed.Store(true)
		s.addChangedZoneSpawn()
	}
}

// GetClient returns the client associated with this spawn (overridden by Player)
func (s *Spawn) GetClient() interface{} {
	return nil // Base spawns have no client
}

// HasChanged returns whether the spawn has any pending changes
func (s *Spawn) HasChanged() bool {
	return s.changed.Load()
}

// HasInfoChanged returns whether the spawn's info has changed
func (s *Spawn) HasInfoChanged() bool {
	return s.infoChanged.Load()
}

// HasPositionChanged returns whether the spawn's position has changed
func (s *Spawn) HasPositionChanged() bool {
	return s.positionChanged.Load()
}

// HasVisualChanged returns whether the spawn's visual appearance has changed
func (s *Spawn) HasVisualChanged() bool {
	return s.visChanged.Load()
}

// ClearChanged resets all change flags
func (s *Spawn) ClearChanged() {
	s.changed.Store(false)
	s.infoChanged.Store(false)
	s.positionChanged.Store(false)
	s.visChanged.Store(false)
	s.sizeChanged.Store(false)
}

// GetLastAttacker returns the ID of the spawn's last attacker
func (s *Spawn) GetLastAttacker() int32 {
	return s.lastAttacker
}

// SetLastAttacker updates the spawn's last attacker
func (s *Spawn) SetLastAttacker(attackerID int32) {
	s.updateMutex.Lock()
	defer s.updateMutex.Unlock()

	s.lastAttacker = attackerID
}

// addChangedZoneSpawn notifies the zone that this spawn has changed
// TODO: Implement when zone system is available
func (s *Spawn) addChangedZoneSpawn() {
	// This would notify the zone server of spawn changes
	// Implementation depends on zone system architecture
}

// TODO: Additional methods to implement:
// - Serialization methods (serialize, spawn_serialize, etc.)
// - Movement system methods
// - Combat and damage methods
// - Loot system methods
// - Group and faction methods
// - Equipment methods
// - Proximity and region methods
// - Knockback physics methods
// - Many more getter/setter methods

// Item placeholder - to be moved to appropriate package when implemented
// Basic structure to prevent compilation errors in Entity system
type Item struct {
	ID          int32  // Item ID
	UniqueID    int32  // Unique instance ID
	Name        string // Item name
	Description string // Item description
	Icon        int32  // Icon ID
	Count       int16  // Stack count
	Price       int32  // Item price
	Slot        int8   // Equipment slot
	Type        int8   // Item type
	SubType     int8   // Item subtype
	// TODO: Add complete item structure when item system is implemented
	// This is a minimal placeholder to allow Entity system compilation
}
