package spells

import (
	"fmt"
	"sync"
)

// SpellTargeting handles spell target selection and validation
type SpellTargeting struct {
	// TODO: Add references to zone and entity systems when available
	// zoneServer *ZoneServer
	// entityManager *EntityManager

	mutex sync.RWMutex
}

// TargetingResult represents the result of spell targeting
type TargetingResult struct {
	ValidTargets   []int32 // List of valid target IDs
	InvalidTargets []int32 // List of invalid target IDs
	ErrorCode      int32   // Error code if targeting failed
	ErrorMessage   string  // Human readable error message
}

// TargetingOptions contains options for target selection
type TargetingOptions struct {
	BypassSpellChecks bool    // Skip spell-specific target validation
	BypassRangeChecks bool    // Skip range validation
	MaxRange          float32 // Override maximum range
	IgnoreLineOfSight bool    // Ignore line of sight requirements
}

// NewSpellTargeting creates a new spell targeting system
func NewSpellTargeting() *SpellTargeting {
	return &SpellTargeting{}
}

// GetSpellTargets finds all valid targets for a spell and adds them to the LuaSpell
// This is the main targeting function converted from C++ SpellProcess::GetSpellTargets
func (st *SpellTargeting) GetSpellTargets(luaSpell *LuaSpell, options *TargetingOptions) *TargetingResult {
	if luaSpell == nil || luaSpell.Spell == nil {
		return &TargetingResult{
			ValidTargets:   make([]int32, 0),
			InvalidTargets: make([]int32, 0),
			ErrorCode:      FailureReasonInvalidTarget,
			ErrorMessage:   "Invalid spell or LuaSpell",
		}
	}

	spell := luaSpell.Spell
	spellData := spell.GetSpellData()
	if spellData == nil {
		return &TargetingResult{
			ValidTargets:   make([]int32, 0),
			InvalidTargets: make([]int32, 0),
			ErrorCode:      FailureReasonInvalidTarget,
			ErrorMessage:   "Invalid spell data",
		}
	}

	// Default options if none provided
	if options == nil {
		options = &TargetingOptions{}
	}

	result := &TargetingResult{
		ValidTargets:   make([]int32, 0),
		InvalidTargets: make([]int32, 0),
		ErrorCode:      0,
		ErrorMessage:   "",
	}

	// Determine targeting method based on spell target type
	switch spellData.TargetType {
	case TargetTypeSelf:
		st.getSelfTargets(luaSpell, result, options)
	case TargetTypeSingle:
		st.getSingleTarget(luaSpell, result, options)
	case TargetTypeGroup:
		st.getGroupTargets(luaSpell, result, options)
	case TargetTypeGroupAE:
		st.getGroupAETargets(luaSpell, result, options)
	case TargetTypeAE:
		st.getAETargets(luaSpell, result, options)
	case TargetTypePBAE:
		st.getPBAETargets(luaSpell, result, options)
	default:
		result.ErrorCode = FailureReasonInvalidTarget
		result.ErrorMessage = fmt.Sprintf("Unsupported target type: %d", spellData.TargetType)
	}

	return result
}

// getSelfTargets handles self-targeting spells
func (st *SpellTargeting) getSelfTargets(luaSpell *LuaSpell, result *TargetingResult, options *TargetingOptions) {
	if luaSpell.CasterID != 0 {
		result.ValidTargets = append(result.ValidTargets, luaSpell.CasterID)
		luaSpell.AddTarget(luaSpell.CasterID)
	} else {
		result.ErrorCode = FailureReasonInvalidTarget
		result.ErrorMessage = "No valid caster for self-targeting spell"
	}
}

// getSingleTarget handles single-target spells
func (st *SpellTargeting) getSingleTarget(luaSpell *LuaSpell, result *TargetingResult, options *TargetingOptions) {
	targetID := luaSpell.InitialTarget
	if targetID == 0 {
		targetID = luaSpell.CasterID // Default to self if no target
	}

	if st.isValidTarget(luaSpell, targetID, options) {
		result.ValidTargets = append(result.ValidTargets, targetID)
		luaSpell.AddTarget(targetID)
	} else {
		result.InvalidTargets = append(result.InvalidTargets, targetID)
		result.ErrorCode = FailureReasonInvalidTarget
		result.ErrorMessage = "Target is not valid for this spell"
	}
}

// getGroupTargets handles group-targeting spells
func (st *SpellTargeting) getGroupTargets(luaSpell *LuaSpell, result *TargetingResult, options *TargetingOptions) {
	// TODO: Implement group targeting when group system is available
	// This would:
	// 1. Get the caster's group
	// 2. Iterate through group members
	// 3. Validate each member as a target
	// 4. Add valid members to the target list

	// For now, just target self as placeholder
	if luaSpell.CasterID != 0 {
		result.ValidTargets = append(result.ValidTargets, luaSpell.CasterID)
		luaSpell.AddTarget(luaSpell.CasterID)
	}
}

// getGroupAETargets handles group area effect spells
func (st *SpellTargeting) getGroupAETargets(luaSpell *LuaSpell, result *TargetingResult, options *TargetingOptions) {
	// TODO: Implement group AE targeting
	// This is similar to group targeting but may include pets and other considerations
	st.getGroupTargets(luaSpell, result, options)
}

// getAETargets handles area effect spells (true AOE)
func (st *SpellTargeting) getAETargets(luaSpell *LuaSpell, result *TargetingResult, options *TargetingOptions) {
	// TODO: Implement AOE targeting when zone system is available
	// This would:
	// 1. Get the spell's radius from spell data
	// 2. Find all entities within radius of the target location
	// 3. Filter based on spell criteria (friendly/hostile, etc.)
	// 4. Apply maximum target limits
	// 5. Add valid targets to the list

	// For now, implement basic logic
	spellData := luaSpell.Spell.GetSpellData()
	maxTargets := int32(10) // TODO: Use spellData.AOENodeNumber when field exists
	if maxTargets <= 0 {
		maxTargets = 10 // Default limit
	}

	// Placeholder: just target the initial target for now
	if luaSpell.InitialTarget != 0 && st.isValidTarget(luaSpell, luaSpell.InitialTarget, options) {
		result.ValidTargets = append(result.ValidTargets, luaSpell.InitialTarget)
		luaSpell.AddTarget(luaSpell.InitialTarget)
	}

	// TODO: Use actual AOE node number when SpellData is updated
	_ = maxTargets // Suppress unused variable warning
}

// getPBAETargets handles point-blank area effect spells (centered on caster)
func (st *SpellTargeting) getPBAETargets(luaSpell *LuaSpell, result *TargetingResult, options *TargetingOptions) {
	// TODO: Implement PBAE targeting when zone system is available
	// This is similar to AE but centered on the caster instead of a target location

	// For now, just target self
	if luaSpell.CasterID != 0 {
		result.ValidTargets = append(result.ValidTargets, luaSpell.CasterID)
		luaSpell.AddTarget(luaSpell.CasterID)
	}
}

// isValidTarget validates whether a target is valid for a spell
func (st *SpellTargeting) isValidTarget(luaSpell *LuaSpell, targetID int32, options *TargetingOptions) bool {
	if targetID == 0 {
		return false
	}

	if options.BypassSpellChecks {
		return true
	}

	// TODO: Implement comprehensive target validation when entity system is available
	// This would check:
	// 1. Target exists and is alive/dead as required
	// 2. Target is within range
	// 3. Line of sight if required
	// 4. Spell can affect target type (player/NPC/etc.)
	// 5. Faction/relationship requirements
	// 6. Target is not immune to spell effects
	// 7. Spell-specific requirements

	return true // Placeholder - accept all targets for now
}

// ValidateRange checks if a target is within spell range
func (st *SpellTargeting) ValidateRange(casterID, targetID int32, spell *Spell, options *TargetingOptions) bool {
	if options != nil && options.BypassRangeChecks {
		return true
	}

	// TODO: Implement range checking when position system is available
	// This would:
	// 1. Get caster position
	// 2. Get target position
	// 3. Calculate distance
	// 4. Compare to spell range

	return true // Placeholder - all targets in range for now
}

// ValidateLineOfSight checks if caster has line of sight to target
func (st *SpellTargeting) ValidateLineOfSight(casterID, targetID int32, options *TargetingOptions) bool {
	if options != nil && options.IgnoreLineOfSight {
		return true
	}

	// TODO: Implement line of sight checking when zone geometry is available
	// This would use ray tracing to check for obstacles between caster and target

	return true // Placeholder - always have LOS for now
}

// GetPlayerGroupTargets gets valid group member targets for a spell
// This is converted from C++ SpellProcess::GetPlayerGroupTargets
func (st *SpellTargeting) GetPlayerGroupTargets(targetPlayerID, casterID int32, luaSpell *LuaSpell, options *TargetingOptions) bool {
	// TODO: Implement when group system is available
	// This would:
	// 1. Get the target player's group
	// 2. Validate caster can target this group
	// 3. Add all valid group members as targets
	// 4. Handle pets if included in group targeting

	// For now, just add the target player
	if st.isValidTarget(luaSpell, targetPlayerID, options) {
		luaSpell.AddTarget(targetPlayerID)
		return true
	}

	return false
}

// AddSelfAndPet adds caster and their pet to spell targets
func (st *SpellTargeting) AddSelfAndPet(luaSpell *LuaSpell, casterID int32, onlyPet bool) {
	if !onlyPet && casterID != 0 {
		luaSpell.AddTarget(casterID)
	}

	// TODO: Add pet targeting when pet system is available
	// This would:
	// 1. Get caster's active pet
	// 2. Validate pet as target
	// 3. Add pet to target list
}

// AddNPCGroupOrSelfTarget adds NPC group members or self to targets
func (st *SpellTargeting) AddNPCGroupOrSelfTarget(luaSpell *LuaSpell, targetID int32) {
	// TODO: Implement NPC group targeting when NPC AI system is available
	// NPCs may have different grouping mechanics than players

	// For now, just add the target
	if targetID != 0 {
		luaSpell.AddTarget(targetID)
	}
}

// CalculateDistance calculates distance between two entities
func (st *SpellTargeting) CalculateDistance(entity1ID, entity2ID int32) float32 {
	// TODO: Implement when position system is available
	// This would get positions and calculate 3D distance

	return 0.0 // Placeholder
}

// IsInRange checks if target is within spell range
func (st *SpellTargeting) IsInRange(spell *Spell, distance float32) bool {
	if spell == nil {
		return false
	}

	spellData := spell.GetSpellData()
	if spellData == nil {
		return false
	}

	maxRange := spellData.Range
	if maxRange <= 0 {
		return true // No range limit
	}

	return distance <= maxRange
}

// GetSpellRange returns the maximum range for a spell
func (st *SpellTargeting) GetSpellRange(spell *Spell) float32 {
	if spell == nil {
		return 0.0
	}

	spellData := spell.GetSpellData()
	if spellData == nil {
		return 0.0
	}

	return spellData.Range
}

// GetSpellRadius returns the area effect radius for a spell
func (st *SpellTargeting) GetSpellRadius(spell *Spell) float32 {
	if spell == nil {
		return 0.0
	}

	spellData := spell.GetSpellData()
	if spellData == nil {
		return 0.0
	}

	return spellData.Radius
}

// GetMaxTargets returns the maximum number of targets for an AOE spell
func (st *SpellTargeting) GetMaxTargets(spell *Spell) int32 {
	if spell == nil {
		return 1
	}

	spellData := spell.GetSpellData()
	if spellData == nil {
		return 1
	}

	// TODO: Use spellData.AOENodeNumber when field exists
	maxTargets := int32(1) // Default to single target

	return maxTargets
}

// IsHostileSpell determines if a spell is hostile/aggressive
func (st *SpellTargeting) IsHostileSpell(spell *Spell) bool {
	if spell == nil {
		return false
	}

	// Check if spell is classified as offensive
	return spell.IsOffenseSpell()
}

// IsFriendlySpell determines if a spell is beneficial/friendly
func (st *SpellTargeting) IsFriendlySpell(spell *Spell) bool {
	if spell == nil {
		return false
	}

	// Check if spell is classified as beneficial
	return spell.IsBuffSpell() || spell.IsHealSpell()
}

// CanTargetSelf checks if spell can target the caster
func (st *SpellTargeting) CanTargetSelf(spell *Spell) bool {
	if spell == nil {
		return false
	}

	spellData := spell.GetSpellData()
	if spellData == nil {
		return false
	}

	// Check target type and spell flags
	return spellData.TargetType == TargetTypeSelf ||
		spellData.TargetType == TargetTypeGroup ||
		spellData.TargetType == TargetTypePBAE
}

// CanTargetOthers checks if spell can target entities other than caster
func (st *SpellTargeting) CanTargetOthers(spell *Spell) bool {
	if spell == nil {
		return false
	}

	spellData := spell.GetSpellData()
	if spellData == nil {
		return false
	}

	// Most target types can target others, self-only spells cannot
	return spellData.TargetType != TargetTypeSelf
}

// RequiresTarget checks if spell requires an explicit target selection
func (st *SpellTargeting) RequiresTarget(spell *Spell) bool {
	if spell == nil {
		return false
	}

	spellData := spell.GetSpellData()
	if spellData == nil {
		return false
	}

	// Single target spells typically require explicit targeting
	return spellData.TargetType == TargetTypeSingle
}

// GetTargetingInfo returns information about spell targeting requirements
func (st *SpellTargeting) GetTargetingInfo(spell *Spell) map[string]interface{} {
	info := make(map[string]interface{})

	if spell == nil {
		return info
	}

	spellData := spell.GetSpellData()
	if spellData == nil {
		return info
	}

	info["target_type"] = spellData.TargetType
	info["range"] = spellData.Range
	info["radius"] = spellData.Radius
	info["max_targets"] = int32(1) // TODO: Use spellData.AOENodeNumber when field exists
	info["can_target_self"] = st.CanTargetSelf(spell)
	info["can_target_others"] = st.CanTargetOthers(spell)
	info["requires_target"] = st.RequiresTarget(spell)
	info["is_hostile"] = st.IsHostileSpell(spell)
	info["is_friendly"] = st.IsFriendlySpell(spell)

	return info
}
