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

namespace Theriapolis.Tests.Dungeons;

/// <summary>
/// Phase 7 M2 — central consumable-dispatch tests + Phase 6.5 M4 carryover
/// (Hybrid Medical Incompatibility scaling on healing potions).
/// </summary>
public sealed class ConsumableHandlerTests
{
    private readonly ContentResolver _content = new(new ContentLoader(TestHelpers.DataDirectory));

    // ── Healing potion ───────────────────────────────────────────────────

    [Fact]
    public void Consume_HealingPotion_RestoresHp()
    {
        var pc = MakePurebred();
        pc.CurrentHp = 1;
        var potion = _content.Items["healing_potion"];

        var result = ConsumableHandler.Consume(potion, pc, seed: 0xFEEDUL);

        Assert.True(result.IsSuccess);
        Assert.Equal(ConsumeResult.ResultKind.Healed, result.Kind);
        Assert.True(result.HealedAmount >= 4,         // 2d4+2 minimum = 4
                    $"healing potion should heal ≥ 4 HP, healed {result.HealedAmount}");
        Assert.True(pc.CurrentHp > 1);
    }

    [Fact]
    public void Consume_HealingPotion_OnHybridPc_AppliesMedicalIncompatibility()
    {
        var pcPure   = MakePurebred();
        var pcHybrid = MakeHybrid();
        // Pin both at 1 HP; same seed so the dice are identical.
        pcPure.CurrentHp   = 1;
        pcHybrid.CurrentHp = 1;
        // Boost MaxHp so neither caps to MaxHp.
        pcPure.MaxHp   = 100;
        pcHybrid.MaxHp = 100;
        var potion = _content.Items["healing_potion"];

        var pureResult   = ConsumableHandler.Consume(potion, pcPure,   seed: 42UL);
        var hybridResult = ConsumableHandler.Consume(potion, pcHybrid, seed: 42UL);

        // Hybrid should heal 75% (round down, min 1) of the same dice roll.
        // 0.75 * 4 = 3, 0.75 * 8 = 6, 0.75 * 10 = 7. So hybrid amount < pure
        // amount whenever the pure roll is ≥ 4 (which 2d4+2 always is).
        Assert.True(hybridResult.IsSuccess);
        Assert.True(hybridResult.WasScaledForHybrid);
        Assert.True(hybridResult.HealedAmount < pureResult.HealedAmount,
            $"hybrid {hybridResult.HealedAmount} should be < pure {pureResult.HealedAmount}");
        Assert.True(hybridResult.HealedAmount >= 1, "min-heal floor is 1 even for hybrids");
    }

    [Fact]
    public void Consume_HealingPotion_DeterministicForSameSeed()
    {
        var pcA = MakePurebred(); pcA.CurrentHp = 1; pcA.MaxHp = 100;
        var pcB = MakePurebred(); pcB.CurrentHp = 1; pcB.MaxHp = 100;
        var potion = _content.Items["healing_potion"];

        var a = ConsumableHandler.Consume(potion, pcA, seed: 0xC0FFEEUL);
        var b = ConsumableHandler.Consume(potion, pcB, seed: 0xC0FFEEUL);
        Assert.Equal(a.HealedAmount, b.HealedAmount);
    }

    // ── Scent masks ──────────────────────────────────────────────────────

    [Fact]
    public void Consume_ScentMaskBasic_OnHybridPc_SetsBasicTier()
    {
        var pc = MakeHybrid();
        var mask = _content.Items["scent_mask_basic"];

        var result = ConsumableHandler.Consume(mask, pc, seed: 0);

        Assert.True(result.IsSuccess);
        Assert.Equal(ConsumeResult.ResultKind.MaskApplied, result.Kind);
        Assert.Equal(ScentMaskTier.Basic, result.MaskTier);
        Assert.True(result.MaskHadEffect);
        Assert.Equal(ScentMaskTier.Basic, pc.Hybrid!.ActiveMaskTier);
    }

    [Fact]
    public void Consume_ScentMaskMilitary_OnHybridPc_SetsMilitaryTier()
    {
        var pc = MakeHybrid();
        var mask = _content.Items["scent_mask_military"];
        var result = ConsumableHandler.Consume(mask, pc, seed: 0);
        Assert.Equal(ScentMaskTier.Military, result.MaskTier);
        Assert.Equal(ScentMaskTier.Military, pc.Hybrid!.ActiveMaskTier);
    }

    [Fact]
    public void Consume_ScentMaskDeepCover_OnHybridPc_SetsDeepCoverTier()
    {
        var pc = MakeHybrid();
        var mask = _content.Items["scent_mask_deep_cover"];
        var result = ConsumableHandler.Consume(mask, pc, seed: 0);
        Assert.Equal(ScentMaskTier.DeepCover, result.MaskTier);
        Assert.Equal(ScentMaskTier.DeepCover, pc.Hybrid!.ActiveMaskTier);
    }

    [Fact]
    public void Consume_ScentMask_OnPurebredPc_SucceedsWithoutEffect()
    {
        var pc = MakePurebred();
        var mask = _content.Items["scent_mask_basic"];
        var result = ConsumableHandler.Consume(mask, pc, seed: 0);
        Assert.True(result.IsSuccess);
        Assert.False(result.MaskHadEffect);
    }

    // ── Rejection paths ──────────────────────────────────────────────────

    [Fact]
    public void Consume_NonConsumable_Rejects()
    {
        var pc = MakePurebred();
        var weapon = _content.Items["fang_knife"];   // kind = "weapon"
        var result = ConsumableHandler.Consume(weapon, pc, seed: 0);
        Assert.Equal(ConsumeResult.ResultKind.Rejected, result.Kind);
    }

    [Fact]
    public void Consume_UnknownConsumableKind_ReturnsUnrecognized()
    {
        var pc = MakePurebred();
        var unknown = new ItemDef { Id = "fake_consumable", Kind = "consumable", ConsumableKind = "tea_party" };
        var result = ConsumableHandler.Consume(unknown, pc, seed: 0);
        Assert.Equal(ConsumeResult.ResultKind.Unrecognized, result.Kind);
        Assert.Equal("fake_consumable", result.UnrecognizedItemId);
    }

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

    private Character MakePurebred()
    {
        var b = new CharacterBuilder
        {
            Clade         = _content.Clades["canidae"],
            Species       = _content.Species["wolf"],
            ClassDef      = _content.Classes["fangsworn"],
            Background    = _content.Backgrounds["pack_raised"],
            BaseAbilities = new AbilityScores(15, 14, 13, 10, 12, 8),
        };
        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 Character MakeHybrid()
    {
        var b = new CharacterBuilder
        {
            ClassDef      = _content.Classes["fangsworn"],
            Background    = _content.Backgrounds["pack_raised"],
            BaseAbilities = new AbilityScores(15, 14, 13, 10, 12, 8),
            IsHybridOrigin       = true,
            HybridSireClade      = _content.Clades["canidae"],
            HybridSireSpecies    = _content.Species["wolf"],
            HybridDamClade       = _content.Clades["leporidae"],
            HybridDamSpecies     = _content.Species["hare"],
            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 { }
        }
        Assert.True(b.TryBuildHybrid(_content.Items, out var ch, out _));
        return ch!;
    }
}
