using Theriapolis.Core;
using Theriapolis.Core.Data;
using Theriapolis.Core.Items;
using Theriapolis.Core.Rules.Character;
using Theriapolis.Core.Rules.Combat;
using Theriapolis.Core.Rules.Stats;
using Theriapolis.Core.Util;
using Xunit;

namespace Theriapolis.Tests.Combat;

/// <summary>
/// Phase 6.5 M4 — Medical Incompatibility scales healing received by a
/// hybrid PC at 75% (round down, min 1). Verified end-to-end via the
/// healer features wired in M1.
/// </summary>
public sealed class HybridMedicalIncompatibilityTests
{
    private readonly ContentResolver _content = new(new ContentLoader(TestHelpers.DataDirectory));

    [Fact]
    public void FieldRepair_OnHybridTarget_ScalesAtSeventyFivePercent()
    {
        var enc = MakeEncounter(out var healer, out var ally,
            healerClass: "claw_wright", isAllyHybrid: true);

        ally.CurrentHp = 5;
        int beforeHp = ally.CurrentHp;
        FeatureProcessor.TryFieldRepair(enc, healer, ally);

        // 1d8 + INT mod (claw_wright kit gives INT 14 → +2 mod) → range 3–10.
        // After 0.75 scale: range 2–7 (rounded down, min 1).
        int gained = ally.CurrentHp - beforeHp;
        Assert.True(gained >= 2, $"hybrid should still gain at least 2 HP after scaling; got {gained}");
        Assert.True(gained <= 7, $"hybrid heal should be 75% of raw range; got {gained}");
    }

    [Fact]
    public void FieldRepair_OnPurebredTarget_DoesNotScale()
    {
        var enc = MakeEncounter(out var healer, out var ally,
            healerClass: "claw_wright", isAllyHybrid: false);
        ally.CurrentHp = 5;
        int beforeHp = ally.CurrentHp;
        FeatureProcessor.TryFieldRepair(enc, healer, ally);
        int gained = ally.CurrentHp - beforeHp;
        // Purebred: full 1d8 + INT 2 → range 3–10.
        Assert.True(gained >= 3, $"purebred should gain full heal; got {gained}");
    }

    [Fact]
    public void LayOnPaws_OnHybridTarget_ScalesDeliveredHpButNotPoolCost()
    {
        var enc = MakeEncounter(out var healer, out var ally,
            healerClass: "covenant_keeper", isAllyHybrid: true);
        FeatureProcessor.EnsureLayOnPawsPoolReady(healer.SourceCharacter!);
        int poolBefore = healer.SourceCharacter!.LayOnPawsPoolRemaining;

        ally.CurrentHp = ally.MaxHp - 4;
        FeatureProcessor.TryLayOnPaws(enc, healer, ally, requestHp: 4);

        // Pool cost is the requested 4 (the inefficiency models the body
        // resisting calibration, not the healer wasting effort).
        Assert.Equal(poolBefore - 4, healer.SourceCharacter.LayOnPawsPoolRemaining);
        // But ally only receives 3 HP (4 * 0.75 = 3, floor).
        int gained = ally.CurrentHp - (ally.MaxHp - 4);
        Assert.Equal(3, gained);
    }

    [Fact]
    public void LayOnPaws_OnPurebredTarget_DeliversFullHp()
    {
        var enc = MakeEncounter(out var healer, out var ally,
            healerClass: "covenant_keeper", isAllyHybrid: false);
        FeatureProcessor.EnsureLayOnPawsPoolReady(healer.SourceCharacter!);

        ally.CurrentHp = ally.MaxHp - 4;
        FeatureProcessor.TryLayOnPaws(enc, healer, ally, requestHp: 4);
        Assert.Equal(ally.MaxHp, ally.CurrentHp);
    }

    // ── Helpers ───────────────────────────────────────────────────────────

    private Encounter MakeEncounter(
        out Combatant healer, out Combatant ally,
        string healerClass, bool isAllyHybrid)
    {
        var hc = MakeChar(healerClass, new AbilityScores(10, 12, 13, 14, 12, 14));
        var ac = isAllyHybrid
            ? MakeHybrid()
            : MakeChar("fangsworn", new AbilityScores(15, 12, 13, 10, 10, 8));
        healer = Combatant.FromCharacter(hc, 1, "Healer", new Vec2(0, 0), Allegiance.Player);
        ally   = Combatant.FromCharacter(ac, 2, "Ally",   new Vec2(1, 0), Allegiance.Allied);
        return new Encounter(0xCAFEUL, 1, new[] { healer, ally });
    }

    private Theriapolis.Core.Rules.Character.Character MakeChar(string classId, AbilityScores a)
    {
        var b = new CharacterBuilder
        {
            Clade        = _content.Clades["canidae"],
            Species      = _content.Species["wolf"],
            ClassDef     = _content.Classes[classId],
            Background   = _content.Backgrounds["pack_raised"],
            BaseAbilities = a,
        };
        int n = b.ClassDef.SkillsChoose;
        foreach (var raw in b.ClassDef.SkillOptions)
        {
            if (b.ChosenClassSkills.Count >= n) break;
            try { b.ChosenClassSkills.Add(SkillIdExtensions.FromJson(raw)); } catch { }
        }
        return b.Build(_content.Items);
    }

    private Theriapolis.Core.Rules.Character.Character MakeHybrid()
    {
        var b = new CharacterBuilder
        {
            ClassDef     = _content.Classes["fangsworn"],
            Background   = _content.Backgrounds["pack_raised"],
            BaseAbilities = new AbilityScores(15, 14, 13, 12, 10, 8),
            IsHybridOrigin = true,
            HybridSireClade   = _content.Clades["canidae"],
            HybridSireSpecies = _content.Species["wolf"],
            HybridDamClade    = _content.Clades["leporidae"],
            HybridDamSpecies  = _content.Species["rabbit"],
            HybridDominantParent = ParentLineage.Sire,
        };
        int n = b.ClassDef.SkillsChoose;
        foreach (var raw in b.ClassDef.SkillOptions)
        {
            if (b.ChosenClassSkills.Count >= n) break;
            try { b.ChosenClassSkills.Add(SkillIdExtensions.FromJson(raw)); } catch { }
        }
        bool ok = b.TryBuildHybrid(_content.Items, out var c, out string err);
        Assert.True(ok, err);
        return c!;
    }
}
