## DamageProcessor — Apply hit results and track kills.
##
## Receives hit results from WeaponServer (via LagCompensation), applies
## damage to the target entity, and emits events for scoreboard/UI updates.
##
## Usage (add as child of GameServer or combat manager):
##
##   var dp = DamageProcessor.new()
##   add_child(dp)
##   dp.register_player(entity_id, 100.0)
##   dp.process_hit(victim_id, shooter_id, 30.0)
##
class_name DamageProcessor
extends Node

# ---------------------------------------------------------------------------
# Signals
# ---------------------------------------------------------------------------
## Emitted when a player takes damage.
## victim_id:  entity_id of the damaged player
## shooter_id: entity_id of the player who dealt the damage (-1 if world/fall)
## damage:     raw damage amount before any modifiers
## killed:     true if this damage reduced health to 0
signal player_damaged(victim_id: int, shooter_id: int, damage: float, killed: bool)

## Emitted when a player dies from damage.
## victim_id:  entity_id of the killed player
## shooter_id: entity_id of the killer (-1 if world/fall)
signal player_killed(victim_id: int, shooter_id: int)

# ---------------------------------------------------------------------------
# State
# ---------------------------------------------------------------------------
## Current health per entity_id.
var _health: Dictionary = {}  # entity_id (int) → current_health (float)

## Maximum health per entity_id (for respawn reset).
var _max_health: Dictionary = {}  # entity_id (int) → max_health (float)

## Kill count per shooter entity_id.
var _kills: Dictionary = {}  # shooter_id (int) → kill_count (int)

# ---------------------------------------------------------------------------
# Public API
# ---------------------------------------------------------------------------

## Register a player for damage tracking with the given maximum health.
func register_player(entity_id: int, max_health: float = 100.0) -> void:
    _health[entity_id] = max_health
    _max_health[entity_id] = max_health
    # Preserve existing kill count if re-registering

## Remove a player from damage tracking (on disconnect / respawn).
func unregister_player(entity_id: int) -> void:
    _health.erase(entity_id)
    _max_health.erase(entity_id)
    _kills.erase(entity_id)

## Reset a player's health to their maximum (on respawn).
## Does NOT reset kill count.
func reset_health(entity_id: int) -> void:
    if _max_health.has(entity_id):
        _health[entity_id] = _max_health[entity_id]

## Process a hit result: apply damage, check for kill, emit signals.
##
## hit_result format (from WeaponServer.fire()):
##   {hit: bool, position: Vector3, target_id: int,
##    damage: float, weapon_id: String}
##
## shooter_id is the entity_id of the player who fired.
func process_hit(hit_result: Dictionary, shooter_id: int) -> void:
    if not hit_result.get("hit", false):
        return

    var victim_id: int = hit_result.get("target_id", -1)
    var damage: float = hit_result.get("damage", 0.0)

    if victim_id < 0 or damage <= 0.0:
        return

    # Apply damage
    var current: float = _health.get(victim_id, 100.0)
    current -= damage
    var killed: bool = false

    if current <= 0.0:
        current = 0.0
        killed = true
        # Track kill for the shooter
        if shooter_id >= 0:
            _kills[shooter_id] = _kills.get(shooter_id, 0) + 1
        player_killed.emit(victim_id, shooter_id)

    _health[victim_id] = max(current, 0.0)

    # Emit damage event (always, even on kill)
    player_damaged.emit(victim_id, shooter_id, damage, killed)

## Convenience: process a hit result with distance-based damage falloff.
## hit_result must contain a "position" key. damage_falloff_start is the
## distance in Godot units beyond which damage begins to decrease.
## damage_falloff_min is the minimum damage multiplier (clamped 0.0–1.0).
func process_hit_with_falloff(
    hit_result: Dictionary,
    shooter_id: int,
    origin: Vector3,
    falloff_start: float = 100.0,
    falloff_min: float = 0.5
) -> void:
    if not hit_result.get("hit", false):
        return

    var damage: float = hit_result.get("damage", 0.0)
    var hit_pos: Vector3 = hit_result.get("position", Vector3.ZERO)
    var distance: float = origin.distance_to(hit_pos)

    if distance > falloff_start:
        # Linear falloff from falloff_start to some max range (2x falloff_start)
        var max_range: float = falloff_start * 2.0
        var t: float = clamp(
            (distance - falloff_start) / (max_range - falloff_start),
            0.0, 1.0
        )
        var multiplier: float = 1.0 - (1.0 - falloff_min) * t
        damage *= multiplier

    var modified_result: Dictionary = hit_result.duplicate()
    modified_result["damage"] = damage
    process_hit(modified_result, shooter_id)

# ---------------------------------------------------------------------------
# Query
# ---------------------------------------------------------------------------

## Get current health for an entity, or -1 if not registered.
func get_health(entity_id: int) -> float:
    return _health.get(entity_id, -1.0)

## Get kill count for a shooter entity_id.
func get_kills(player_id: int) -> int:
    return _kills.get(player_id, 0)

## Get all kills (shooter_id → kill_count).
func get_all_kills() -> Dictionary:
    return _kills.duplicate()

## Reset all state (for round restart).
func reset_all() -> void:
    _health.clear()
    _max_health.clear()
    _kills.clear()
