using Theriapolis.Core.Data;
using Theriapolis.Core.Entities.Ai;
using Theriapolis.Core.Rules.Character;
using Theriapolis.Core.Rules.Combat;
using Theriapolis.Core.Util;
using Xunit;

namespace Theriapolis.Tests.Combat;

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

    [Fact]
    public void Brigand_MovesTowardTargetWhenOutOfReach()
    {
        var brigand = MakeNpc("brigand_footpad", new Vec2(0, 0), Allegiance.Hostile);
        var hero    = MakeNpc("brigand_footpad", new Vec2(5, 0), Allegiance.Player);
        var enc     = new Encounter(0xCAFEUL, 1, new[] { brigand, hero });
        // Skip past initiative and start brigand's turn explicitly.
        while (enc.CurrentActor.Id != brigand.Id) enc.EndTurn();

        new BrigandBehavior().TakeTurn(brigand, new AiContext(enc));
        Assert.True((int)brigand.Position.X > 0);  // moved toward hero
    }

    [Fact]
    public void WildAnimal_FleesBelowQuarterHp()
    {
        var wolf = MakeNpc("wolf", new Vec2(5, 0), Allegiance.Hostile);
        var hero = MakeNpc("wolf", new Vec2(0, 0), Allegiance.Player);
        wolf.CurrentHp = 1;  // well below 25% of 11
        var enc = new Encounter(0xCAFEUL, 1, new[] { wolf, hero });
        while (enc.CurrentActor.Id != wolf.Id) enc.EndTurn();

        var startX = (int)wolf.Position.X;
        new WildAnimalBehavior().TakeTurn(wolf, new AiContext(enc));
        Assert.True((int)wolf.Position.X > startX, "Wounded wolf should flee away from hero (positive X)");
    }

    [Fact]
    public void Behavior_NoTargetMakesNoMoves()
    {
        var lone = MakeNpc("brigand_footpad", new Vec2(0, 0), Allegiance.Hostile);
        var enc  = new Encounter(0xCAFEUL, 1, new[] { lone });
        var startPos = lone.Position;
        new BrigandBehavior().TakeTurn(lone, new AiContext(enc));
        Assert.Equal(startPos.X, lone.Position.X);
        Assert.Equal(startPos.Y, lone.Position.Y);
    }

    [Fact]
    public void Behavior_AttacksWhenInReach()
    {
        var brigand = MakeNpc("brigand_footpad", new Vec2(0, 0), Allegiance.Hostile);
        var hero    = MakeNpc("brigand_footpad", new Vec2(1, 0), Allegiance.Player);
        var enc     = new Encounter(0xCAFEUL, 1, new[] { brigand, hero });
        while (enc.CurrentActor.Id != brigand.Id) enc.EndTurn();

        int logBefore = enc.Log.Count;
        new BrigandBehavior().TakeTurn(brigand, new AiContext(enc));
        Assert.Contains(enc.Log.Skip(logBefore), e => e.Type == CombatLogEntry.Kind.Attack);
    }

    [Fact]
    public void Registry_UnknownIdFallsBackToBrigand()
    {
        var b = BehaviorRegistry.For("nonsense_behavior");
        Assert.IsType<BrigandBehavior>(b);
    }

    private Combatant MakeNpc(string templateId, Vec2 pos, Allegiance side)
    {
        var t = _content.Npcs.Templates.First(x => x.Id == templateId);
        var swapped = t with { DefaultAllegiance = side.ToString().ToLowerInvariant() };
        return Combatant.FromNpcTemplate(swapped, id: pos.X.GetHashCode() ^ pos.Y.GetHashCode(), pos);
    }
}
