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

namespace Theriapolis.Tests.Combat;

public sealed class InitiativeTests
{
    private readonly ContentResolver _content = new(new ContentLoader(TestHelpers.DataDirectory));

    [Fact]
    public void NewEncounter_AppendsInitiativeLogEntry()
    {
        var combatants = MakeThree();
        var enc = new Encounter(0xCAFEUL, 1, combatants);
        Assert.Contains(enc.Log, l => l.Type == CombatLogEntry.Kind.Initiative);
    }

    [Fact]
    public void InitiativeOrder_ContainsEveryCombatantExactlyOnce()
    {
        var combatants = MakeThree();
        var enc = new Encounter(0xCAFEUL, 1, combatants);
        Assert.Equal(combatants.Count, enc.InitiativeOrder.Count);
        Assert.Equal(combatants.Count, enc.InitiativeOrder.Distinct().Count());
        foreach (int idx in enc.InitiativeOrder)
            Assert.InRange(idx, 0, combatants.Count - 1);
    }

    [Fact]
    public void EndTurn_AdvancesToNextLivingCombatant()
    {
        var combatants = MakeThree();
        var enc = new Encounter(0xCAFEUL, 1, combatants);
        var first = enc.CurrentActor;
        enc.EndTurn();
        Assert.NotEqual(first.Id, enc.CurrentActor.Id);
    }

    [Fact]
    public void EndTurn_WrappingIncrementsRoundCounter()
    {
        var combatants = MakeThree();
        var enc = new Encounter(0xCAFEUL, 1, combatants);
        Assert.Equal(1, enc.RoundNumber);
        // Advance N turns to wrap once.
        for (int i = 0; i < combatants.Count; i++) enc.EndTurn();
        Assert.Equal(2, enc.RoundNumber);
    }

    [Fact]
    public void CheckForVictory_EndsWhenOnlyOneSideRemains()
    {
        var brigand = _content.Npcs.Templates.First(t => t.Id == "brigand_footpad");
        var wolf    = _content.Npcs.Templates.First(t => t.Id == "wolf");
        var hero    = Combatant.FromNpcTemplate(brigand, id: 1, new Vec2(0, 0));
        var foe     = Combatant.FromNpcTemplate(wolf,    id: 2, new Vec2(1, 0));
        // Force opposite allegiances. Brigand defaults Hostile; rebuild "hero" as Player-side.
        hero = Combatant.FromNpcTemplate(brigand with { DefaultAllegiance = "player" }, id: 1, new Vec2(0, 0));
        var enc = new Encounter(1UL, 1, new[] { hero, foe });
        // Knock out the foe.
        Resolver.ApplyDamage(foe, foe.MaxHp);
        Assert.True(enc.CheckForVictory());
        Assert.True(enc.IsOver);
        Assert.Contains(enc.Log, l => l.Type == CombatLogEntry.Kind.EncounterEnd);
    }

    private List<Combatant> MakeThree()
    {
        var brigand = _content.Npcs.Templates.First(t => t.Id == "brigand_footpad");
        var wolf    = _content.Npcs.Templates.First(t => t.Id == "wolf");
        var captain = _content.Npcs.Templates.First(t => t.Id == "brigand_captain");
        // Mix allegiances so CheckForVictory doesn't end the encounter immediately.
        return new List<Combatant>
        {
            Combatant.FromNpcTemplate(brigand with { DefaultAllegiance = "player" }, id: 1, new Vec2(0, 0)),
            Combatant.FromNpcTemplate(wolf,    id: 2, new Vec2(2, 0)),
            Combatant.FromNpcTemplate(captain, id: 3, new Vec2(4, 0)),
        };
    }
}
