namespace NeonSprawl.Server.Game.Combat;

/// <summary>
/// Orchestrates deterministic combat resolution: ability catalog damage + prototype target HP (NEO-81).
/// Cast HTTP wiring: <see cref="AbilityInput.AbilityCastApi"/> (NEO-82).
/// NEO-84 telemetry hook sites: <see cref="TryResolve"/> (<c>ability_used</c>, <c>enemy_defeat</c>).
/// </summary>
public static class CombatOperations
{
    // --- Reserved telemetry hook sites (NEO-84): no emit until future modules ---
    // encounter_start — future E5.M3 encounter spawn/engage path (not wired in Slice 1).
    // player_death — future player HP / death resolution (player HP out of scope in Slice 1).
    /// <summary>
    /// Resolves one ability against one prototype combat target using catalog damage and the health store.
    /// Defeated targets deny before damage application; zero-damage abilities succeed without mutating HP.
    /// </summary>
    public static CombatResult TryResolve(
        string abilityId,
        string targetId,
        IAbilityDefinitionRegistry abilityRegistry,
        ICombatEntityHealthStore healthStore)
    {
        if (!abilityRegistry.TryNormalizeKnown(abilityId, out var normalizedAbility) ||
            !abilityRegistry.TryGetDefinition(normalizedAbility, out var definition))
        {
            return Deny(CombatReasonCodes.UnknownAbility);
        }

        if (!healthStore.TryGet(targetId, out var snapshot))
        {
            return Deny(CombatReasonCodes.UnknownTarget);
        }

        if (snapshot.Defeated)
        {
            return Deny(CombatReasonCodes.TargetDefeated, snapshot.CurrentHp);
        }

        if (definition.BaseDamage == 0)
        {
            // --- Telemetry hook site (NEO-84): future E9.M1 catalog event `ability_used` ---
            // TODO(E9.M1): catalog emit — on every successful resolve (including zero-damage abilities).
            // Planned payload fields: abilityId, targetId, damageDealt (0), targetRemainingHp, targetDefeated.
            // playerId is not in engine scope; correlate route id at AbilityCastApi accept (NEO-30 funnel).
            return new CombatResult(
                Success: true,
                ReasonCode: null,
                DamageDealt: 0,
                TargetRemainingHp: snapshot.CurrentHp,
                TargetDefeated: snapshot.Defeated);
        }

        if (!healthStore.TryApplyDamage(targetId, definition.BaseDamage, out var after))
        {
            return Deny(CombatReasonCodes.UnknownTarget);
        }

        if (after.Defeated)
        {
            // --- Telemetry hook site (NEO-84): future E9.M1 catalog event `enemy_defeat` ---
            // TODO(E9.M1): catalog emit — once per target defeat transition (lethal hit only; re-hit denied earlier).
            // Planned payload fields: targetId, abilityId (killing blow), damageDealt, final HP 0; correlate playerId at cast layer.
        }

        // --- Telemetry hook site (NEO-84): future E9.M1 catalog event `ability_used` ---
        // TODO(E9.M1): catalog emit — on every successful resolve after damage application.
        // Planned payload fields: abilityId, targetId, damageDealt, targetRemainingHp, targetDefeated.
        // playerId is not in engine scope; correlate route id at AbilityCastApi accept (NEO-30 funnel).
        return new CombatResult(
            Success: true,
            ReasonCode: null,
            DamageDealt: definition.BaseDamage,
            TargetRemainingHp: after.CurrentHp,
            TargetDefeated: after.Defeated);
    }

    private static CombatResult Deny(string reasonCode, int? targetRemainingHp = null) =>
        new(
            Success: false,
            ReasonCode: reasonCode,
            DamageDealt: 0,
            TargetRemainingHp: targetRemainingHp,
            TargetDefeated: false);
}
