using Theriapolis.Core.Rules.Combat;
using Theriapolis.Core.Util;

namespace Theriapolis.Core.Entities.Ai;

/// <summary>
/// Like <see cref="BrigandBehavior"/>, but flees when reduced below
/// <c>FLEE_HP_FRACTION</c> of max HP — wild animals don't have honor.
/// </summary>
public sealed class WildAnimalBehavior : INpcBehavior
{
    public const float FLEE_HP_FRACTION = 0.25f;

    public void TakeTurn(Combatant self, AiContext ctx)
    {
        if (self.IsDown) return;
        var target = ctx.FindClosestHostile(self);
        if (target is null) return;

        // Flee at low HP — move directly away, don't attack.
        if (self.CurrentHp <= self.MaxHp * FLEE_HP_FRACTION)
        {
            int tiles = ctx.Encounter.CurrentTurn.RemainingMovementFt / 5;
            for (int i = 0; i < tiles; i++)
            {
                var away = StepAwayFrom(self.Position, target.Position);
                if ((int)away.X == (int)self.Position.X && (int)away.Y == (int)self.Position.Y) break;
                self.Position = away;
                ctx.Encounter.AppendLog(CombatLogEntry.Kind.Move,
                    $"{self.Name} flees to ({(int)away.X},{(int)away.Y}).");
            }
            ctx.Encounter.CurrentTurn.ConsumeMovement(tiles * 5);
            return;
        }

        // Otherwise, identical to Brigand: close + attack.
        var attack = self.AttackOptions[0];
        int tilesAvail = ctx.Encounter.CurrentTurn.RemainingMovementFt / 5;
        while (!ReachAndCover.IsInReach(self, target, attack) && tilesAvail > 0)
        {
            var next = ReachAndCover.StepToward(self.Position, target.Position);
            if ((int)next.X == (int)self.Position.X && (int)next.Y == (int)self.Position.Y) break;
            self.Position = next;
            ctx.Encounter.AppendLog(CombatLogEntry.Kind.Move,
                $"{self.Name} moves to ({(int)next.X},{(int)next.Y}).");
            tilesAvail--;
        }
        int consumedTiles = (ctx.Encounter.CurrentTurn.RemainingMovementFt / 5) - tilesAvail;
        ctx.Encounter.CurrentTurn.ConsumeMovement(consumedTiles * 5);

        if (!ReachAndCover.IsInReach(self, target, attack)) return;
        Resolver.AttemptAttack(ctx.Encounter, self, target, attack);
        ctx.Encounter.CurrentTurn.ConsumeAction();
    }

    private static Vec2 StepAwayFrom(Vec2 from, Vec2 menace)
    {
        // Reverse of StepToward — increment by sign of (from - menace) so we
        // move away from the menace.
        int dx = System.Math.Sign(from.X - menace.X);
        int dy = System.Math.Sign(from.Y - menace.Y);
        if (dx == 0 && dy == 0) dx = 1;  // pick a direction if standing on top
        return new Vec2((int)from.X + dx, (int)from.Y + dy);
    }
}
