package spawn

import (
	"sync"
)

// Spawn entry types
const (
	SpawnEntryTypeNPC         = 0
	SpawnEntryTypeObject      = 1
	SpawnEntryTypeWidget      = 2
	SpawnEntryTypeSign        = 3
	SpawnEntryTypeGroundSpawn = 4
)

// SpawnEntry represents a possible spawn at a location with its configuration
type SpawnEntry struct {
	SpawnEntryID      int32   // Unique identifier for this spawn entry
	SpawnLocationID   int32   // ID of the location this entry belongs to
	SpawnType         int8    // Type of spawn (NPC, Object, Widget, etc.)
	SpawnID           int32   // ID of the actual spawn template
	SpawnPercentage   float32 // Chance this spawn will appear
	Respawn           int32   // Base respawn time in seconds
	RespawnOffsetLow  int32   // Minimum random offset for respawn
	RespawnOffsetHigh int32   // Maximum random offset for respawn
	DuplicatedSpawn   bool    // Whether this spawn can appear multiple times
	ExpireTime        int32   // Time before spawn expires (0 = permanent)
	ExpireOffset      int32   // Random offset for expire time

	// Spawn location overrides - these override the base spawn's stats
	LevelOverride        int32 // Override spawn level
	HPOverride           int32 // Override spawn HP
	MPOverride           int32 // Override spawn MP/Power
	StrengthOverride     int32 // Override strength stat
	StaminaOverride      int32 // Override stamina stat
	WisdomOverride       int32 // Override wisdom stat
	IntelligenceOverride int32 // Override intelligence stat
	AgilityOverride      int32 // Override agility stat
	HeatOverride         int32 // Override heat resistance
	ColdOverride         int32 // Override cold resistance
	MagicOverride        int32 // Override magic resistance
	MentalOverride       int32 // Override mental resistance
	DivineOverride       int32 // Override divine resistance
	DiseaseOverride      int32 // Override disease resistance
	PoisonOverride       int32 // Override poison resistance
	DifficultyOverride   int32 // Override encounter level/difficulty
}

// NewSpawnEntry creates a new spawn entry with default values
func NewSpawnEntry() *SpawnEntry {
	return &SpawnEntry{
		SpawnEntryID:         0,
		SpawnLocationID:      0,
		SpawnType:            SpawnEntryTypeNPC,
		SpawnID:              0,
		SpawnPercentage:      100.0,
		Respawn:              600, // 10 minutes default
		RespawnOffsetLow:     0,
		RespawnOffsetHigh:    0,
		DuplicatedSpawn:      true,
		ExpireTime:           0,
		ExpireOffset:         0,
		LevelOverride:        0,
		HPOverride:           0,
		MPOverride:           0,
		StrengthOverride:     0,
		StaminaOverride:      0,
		WisdomOverride:       0,
		IntelligenceOverride: 0,
		AgilityOverride:      0,
		HeatOverride:         0,
		ColdOverride:         0,
		MagicOverride:        0,
		MentalOverride:       0,
		DivineOverride:       0,
		DiseaseOverride:      0,
		PoisonOverride:       0,
		DifficultyOverride:   0,
	}
}

// GetSpawnTypeName returns a human-readable name for the spawn type
func (se *SpawnEntry) GetSpawnTypeName() string {
	switch se.SpawnType {
	case SpawnEntryTypeNPC:
		return "NPC"
	case SpawnEntryTypeObject:
		return "Object"
	case SpawnEntryTypeWidget:
		return "Widget"
	case SpawnEntryTypeSign:
		return "Sign"
	case SpawnEntryTypeGroundSpawn:
		return "GroundSpawn"
	default:
		return "Unknown"
	}
}

// HasStatOverrides returns true if this entry has any stat overrides configured
func (se *SpawnEntry) HasStatOverrides() bool {
	return se.LevelOverride != 0 || se.HPOverride != 0 || se.MPOverride != 0 ||
		se.StrengthOverride != 0 || se.StaminaOverride != 0 || se.WisdomOverride != 0 ||
		se.IntelligenceOverride != 0 || se.AgilityOverride != 0 || se.HeatOverride != 0 ||
		se.ColdOverride != 0 || se.MagicOverride != 0 || se.MentalOverride != 0 ||
		se.DivineOverride != 0 || se.DiseaseOverride != 0 || se.PoisonOverride != 0 ||
		se.DifficultyOverride != 0
}

// GetActualRespawnTime calculates the actual respawn time including random offset
func (se *SpawnEntry) GetActualRespawnTime() int32 {
	if se.RespawnOffsetLow == 0 && se.RespawnOffsetHigh == 0 {
		return se.Respawn
	}

	// TODO: Implement random number generation
	// For now, return base respawn time
	return se.Respawn
}

// SpawnLocation represents a location in the world where spawns can appear
type SpawnLocation struct {
	// Position data
	X       float32 // X coordinate in world space
	Y       float32 // Y coordinate in world space
	Z       float32 // Z coordinate in world space
	Heading float32 // Direction the spawn faces
	Pitch   float32 // Pitch angle
	Roll    float32 // Roll angle

	// Offset ranges for randomizing spawn positions
	XOffset float32 // Random X offset range (+/-)
	YOffset float32 // Random Y offset range (+/-)
	ZOffset float32 // Random Z offset range (+/-)

	// Location metadata
	PlacementID int32  // Unique placement identifier
	GridID      int32  // Grid cell this location belongs to
	Script      string // Lua script to run for this location
	Conditional int8   // Conditional flag for spawn logic

	// Spawn management
	Entities        []*SpawnEntry // List of possible spawns at this location
	TotalPercentage float32       // Sum of all spawn percentages

	// Thread safety
	mutex sync.RWMutex
}

// NewSpawnLocation creates a new spawn location with default values
func NewSpawnLocation() *SpawnLocation {
	return &SpawnLocation{
		X:               0.0,
		Y:               0.0,
		Z:               0.0,
		Heading:         0.0,
		Pitch:           0.0,
		Roll:            0.0,
		XOffset:         0.0,
		YOffset:         0.0,
		ZOffset:         0.0,
		PlacementID:     0,
		GridID:          0,
		Script:          "",
		Conditional:     0,
		Entities:        make([]*SpawnEntry, 0),
		TotalPercentage: 0.0,
	}
}

// AddSpawnEntry adds a spawn entry to this location
func (sl *SpawnLocation) AddSpawnEntry(entry *SpawnEntry) {
	if entry == nil {
		return
	}

	sl.mutex.Lock()
	defer sl.mutex.Unlock()

	sl.Entities = append(sl.Entities, entry)
	sl.TotalPercentage += entry.SpawnPercentage
}

// RemoveSpawnEntry removes a spawn entry by its ID
func (sl *SpawnLocation) RemoveSpawnEntry(entryID int32) bool {
	sl.mutex.Lock()
	defer sl.mutex.Unlock()

	for i, entry := range sl.Entities {
		if entry.SpawnEntryID == entryID {
			// Remove from slice
			sl.Entities = append(sl.Entities[:i], sl.Entities[i+1:]...)
			sl.TotalPercentage -= entry.SpawnPercentage
			return true
		}
	}
	return false
}

// GetSpawnEntries returns a copy of all spawn entries
func (sl *SpawnLocation) GetSpawnEntries() []*SpawnEntry {
	sl.mutex.RLock()
	defer sl.mutex.RUnlock()

	entries := make([]*SpawnEntry, len(sl.Entities))
	copy(entries, sl.Entities)
	return entries
}

// GetSpawnEntryCount returns the number of spawn entries at this location
func (sl *SpawnLocation) GetSpawnEntryCount() int {
	sl.mutex.RLock()
	defer sl.mutex.RUnlock()

	return len(sl.Entities)
}

// GetSpawnEntryByID finds a spawn entry by its ID
func (sl *SpawnLocation) GetSpawnEntryByID(entryID int32) *SpawnEntry {
	sl.mutex.RLock()
	defer sl.mutex.RUnlock()

	for _, entry := range sl.Entities {
		if entry.SpawnEntryID == entryID {
			return entry
		}
	}
	return nil
}

// CalculateRandomPosition returns a randomized position within the offset ranges
// TODO: Implement proper random number generation
func (sl *SpawnLocation) CalculateRandomPosition() (float32, float32, float32) {
	sl.mutex.RLock()
	defer sl.mutex.RUnlock()

	// For now, return base position
	// In full implementation, would add random offsets within ranges
	return sl.X, sl.Y, sl.Z
}

// SelectRandomSpawn selects a spawn entry based on spawn percentages
// Returns nil if no spawn is selected (based on random chance)
func (sl *SpawnLocation) SelectRandomSpawn() *SpawnEntry {
	sl.mutex.RLock()
	defer sl.mutex.RUnlock()

	if len(sl.Entities) == 0 || sl.TotalPercentage <= 0 {
		return nil
	}

	// TODO: Implement proper random selection based on percentages
	// For now, return first entry if any exist
	if len(sl.Entities) > 0 {
		return sl.Entities[0]
	}

	return nil
}

// HasScript returns true if this location has a Lua script
func (sl *SpawnLocation) HasScript() bool {
	return sl.Script != ""
}

// IsConditional returns true if this location has conditional spawning
func (sl *SpawnLocation) IsConditional() bool {
	return sl.Conditional != 0
}

// GetDistance calculates distance from this location to specified coordinates
func (sl *SpawnLocation) GetDistance(x, y, z float32, ignoreY bool) float32 {
	dx := sl.X - x
	dy := sl.Y - y
	dz := sl.Z - z

	if ignoreY {
		return float32(dx*dx + dz*dz)
	}
	return float32(dx*dx + dy*dy + dz*dz)
}

// SetPosition updates the location's position coordinates
func (sl *SpawnLocation) SetPosition(x, y, z float32) {
	sl.mutex.Lock()
	defer sl.mutex.Unlock()

	sl.X = x
	sl.Y = y
	sl.Z = z
}

// SetRotation updates the location's rotation angles
func (sl *SpawnLocation) SetRotation(heading, pitch, roll float32) {
	sl.mutex.Lock()
	defer sl.mutex.Unlock()

	sl.Heading = heading
	sl.Pitch = pitch
	sl.Roll = roll
}

// SetOffsets updates the location's position offset ranges
func (sl *SpawnLocation) SetOffsets(xOffset, yOffset, zOffset float32) {
	sl.mutex.Lock()
	defer sl.mutex.Unlock()

	sl.XOffset = xOffset
	sl.YOffset = yOffset
	sl.ZOffset = zOffset
}

// RecalculateTotalPercentage recalculates the total spawn percentage
// Should be called if spawn entry percentages are modified directly
func (sl *SpawnLocation) RecalculateTotalPercentage() {
	sl.mutex.Lock()
	defer sl.mutex.Unlock()

	sl.TotalPercentage = 0.0
	for _, entry := range sl.Entities {
		sl.TotalPercentage += entry.SpawnPercentage
	}
}

// Cleanup releases resources used by this spawn location
func (sl *SpawnLocation) Cleanup() {
	sl.mutex.Lock()
	defer sl.mutex.Unlock()

	// Clear spawn entries
	sl.Entities = nil
	sl.TotalPercentage = 0.0
}

// SpawnLocationManager manages collections of spawn locations
type SpawnLocationManager struct {
	locations map[int32]*SpawnLocation // Key is placement ID
	mutex     sync.RWMutex
}

// NewSpawnLocationManager creates a new spawn location manager
func NewSpawnLocationManager() *SpawnLocationManager {
	return &SpawnLocationManager{
		locations: make(map[int32]*SpawnLocation),
	}
}

// AddLocation adds a spawn location to the manager
func (slm *SpawnLocationManager) AddLocation(placementID int32, location *SpawnLocation) {
	if location == nil {
		return
	}

	slm.mutex.Lock()
	defer slm.mutex.Unlock()

	location.PlacementID = placementID
	slm.locations[placementID] = location
}

// RemoveLocation removes a spawn location by placement ID
func (slm *SpawnLocationManager) RemoveLocation(placementID int32) bool {
	slm.mutex.Lock()
	defer slm.mutex.Unlock()

	if location, exists := slm.locations[placementID]; exists {
		location.Cleanup()
		delete(slm.locations, placementID)
		return true
	}
	return false
}

// GetLocation retrieves a spawn location by placement ID
func (slm *SpawnLocationManager) GetLocation(placementID int32) *SpawnLocation {
	slm.mutex.RLock()
	defer slm.mutex.RUnlock()

	return slm.locations[placementID]
}

// GetAllLocations returns a copy of all spawn locations
func (slm *SpawnLocationManager) GetAllLocations() map[int32]*SpawnLocation {
	slm.mutex.RLock()
	defer slm.mutex.RUnlock()

	locations := make(map[int32]*SpawnLocation)
	for id, location := range slm.locations {
		locations[id] = location
	}
	return locations
}

// GetLocationCount returns the number of spawn locations
func (slm *SpawnLocationManager) GetLocationCount() int {
	slm.mutex.RLock()
	defer slm.mutex.RUnlock()

	return len(slm.locations)
}

// GetLocationsInRange returns all locations within the specified distance of coordinates
func (slm *SpawnLocationManager) GetLocationsInRange(x, y, z, maxDistance float32, ignoreY bool) []*SpawnLocation {
	slm.mutex.RLock()
	defer slm.mutex.RUnlock()

	var locations []*SpawnLocation
	maxDistanceSquared := maxDistance * maxDistance

	for _, location := range slm.locations {
		distance := location.GetDistance(x, y, z, ignoreY)
		if distance <= maxDistanceSquared {
			locations = append(locations, location)
		}
	}

	return locations
}

// GetLocationsByGridID returns all locations in a specific grid
func (slm *SpawnLocationManager) GetLocationsByGridID(gridID int32) []*SpawnLocation {
	slm.mutex.RLock()
	defer slm.mutex.RUnlock()

	var locations []*SpawnLocation
	for _, location := range slm.locations {
		if location.GridID == gridID {
			locations = append(locations, location)
		}
	}

	return locations
}

// Clear removes all spawn locations
func (slm *SpawnLocationManager) Clear() {
	slm.mutex.Lock()
	defer slm.mutex.Unlock()

	// Cleanup all locations
	for _, location := range slm.locations {
		location.Cleanup()
	}

	slm.locations = make(map[int32]*SpawnLocation)
}
