using Theriapolis.Core.Items;

namespace Theriapolis.Core.Rules.Stats;

/// <summary>
/// Pure computed values derived from a <see cref="Character"/>'s ability
/// scores, equipped items, conditions, and encumbrance state. Recomputed on
/// demand — nothing here mutates the character. UI panels and the combat
/// resolver call these helpers to surface the current AC, speed, etc.
///
/// Phase 5 M3 ships the AC, Speed, and CarryCap formulas plus the
/// encumbrance band. Class/feature-driven AC bonuses (Bovid Herd Wall +1
/// adjacent ally, Feral Unarmored Defense, etc.) are layered on at combat
/// resolution time, not here — those need positional context.
/// </summary>
public static class DerivedStats
{
    public enum EncumbranceBand : byte
    {
        Light = 0,   // ≤ soft threshold — no penalty
        Heavy = 1,   // > soft threshold — speed -10 ft.
        Over  = 2,   // > hard threshold — speed halved + disadvantage on STR/DEX/CON
    }

    /// <summary>
    /// Armor Class from base 10 (or unarmored-defense pseudo-armor) plus DEX
    /// (capped by armor type) plus shield. Out-of-combat baseline; does not
    /// include feature/positional bonuses.
    /// </summary>
    public static int ArmorClass(Theriapolis.Core.Rules.Character.Character c)
    {
        int dexMod = c.Abilities.ModFor(AbilityId.DEX);
        int ac;

        var body = c.Inventory.GetEquipped(EquipSlot.Body);
        if (body is null)
        {
            // Unarmored: 10 + DEX. Feral's "Unarmored Defense" (10 + DEX + CON)
            // ships at M6 with the rest of class-feature combat effects.
            ac = 10 + dexMod;
        }
        else
        {
            int dexAllowed = body.Def.AcMaxDex < 0 ? dexMod : Math.Min(dexMod, body.Def.AcMaxDex);
            ac = body.Def.AcBase + dexAllowed;
        }

        var off = c.Inventory.GetEquipped(EquipSlot.OffHand);
        if (off is not null && string.Equals(off.Def.Kind, "shield", StringComparison.OrdinalIgnoreCase))
            ac += off.Def.AcBase;

        // Phase 5 M6: class features may replace the unarmored baseline
        // (Feral Unarmored Defense). Per-encounter combat-time bonuses
        // (Sentinel Stance) are added at attack-resolution time, not here.
        ac = Theriapolis.Core.Rules.Combat.FeatureProcessor.ApplyAcFeatures(c, ac);

        return Math.Clamp(ac, C.AC_FLOOR, C.AC_CEILING);
    }

    /// <summary>Initiative = DEX modifier. Class features that add to it (Feral L7) layered later.</summary>
    public static int Initiative(Theriapolis.Core.Rules.Character.Character c) => c.Abilities.ModFor(AbilityId.DEX);

    /// <summary>
    /// Movement speed in feet per turn. Base from species, modified by
    /// encumbrance band and (later) by conditions and class features.
    /// </summary>
    public static int SpeedFt(Theriapolis.Core.Rules.Character.Character c)
    {
        int speed = c.Species.BaseSpeedFt;
        switch (Encumbrance(c))
        {
            case EncumbranceBand.Heavy: speed -= 10; break;
            case EncumbranceBand.Over:  speed /= 2; break;
        }
        return Math.Max(0, speed);
    }

    /// <summary>
    /// Carrying capacity in pounds. Base = STR × 15, scaled by size category
    /// (Small ½×, Large 2×, etc. per equipment.md).
    /// </summary>
    public static float CarryCapacityLb(Theriapolis.Core.Rules.Character.Character c)
    {
        return c.Abilities.STR * 15f * c.Size.CarryCapacityMult();
    }

    /// <summary>Current encumbrance band given inventory weight vs. carry capacity.</summary>
    public static EncumbranceBand Encumbrance(Theriapolis.Core.Rules.Character.Character c)
    {
        float cap = CarryCapacityLb(c);
        if (cap <= 0f) return EncumbranceBand.Over;

        float w = c.Inventory.TotalWeightLb;
        float ratio = w / cap;
        if (ratio > C.ENCUMBRANCE_HARD_MULT) return EncumbranceBand.Over;
        if (ratio > C.ENCUMBRANCE_SOFT_MULT) return EncumbranceBand.Heavy;
        return EncumbranceBand.Light;
    }

    /// <summary>
    /// Speed multiplier applied to <see cref="C.TACTICAL_PLAYER_PX_PER_SEC"/>.
    /// 1.0 = normal walking pace; smaller = encumbered drag. Light = 1.0,
    /// Heavy ≈ 0.66, Over = 0.5.
    /// </summary>
    public static float TacticalSpeedMult(Theriapolis.Core.Rules.Character.Character c) => Encumbrance(c) switch
    {
        EncumbranceBand.Light => 1.0f,
        EncumbranceBand.Heavy => 0.66f,
        EncumbranceBand.Over  => 0.50f,
        _                     => 1.0f,
    };
}
