//
//  SimpleAttackTechnique.cs
//
//  Copyright (c) František Boháček. All rights reserved.
//  Licensed under the MIT license. See LICENSE file in the project root for full license information.
using NosSmooth.Data.Abstractions.Enums;
using NosSmooth.Extensions.Combat.Errors;
using NosSmooth.Extensions.Combat.Extensions;
using NosSmooth.Extensions.Combat.Operations;
using NosSmooth.Extensions.Combat.Selectors;
using NosSmooth.Extensions.Pathfinding;
using NosSmooth.Game.Data.Characters;
using NosSmooth.Game.Data.Entities;
using Remora.Results;
namespace NosSmooth.Extensions.Combat.Techniques;
/// 
/// A combat technique that will attack on the specified enemy, walk within range and use skill until the enemy is dead.
/// 
public class SimpleAttackTechnique : ICombatTechnique
{
    private readonly long _targetId;
    private readonly WalkManager _walkManager;
    private readonly ISkillSelector _skillSelector;
    private Skill? _currentSkill;
    private ILivingEntity? _target;
    /// 
    /// Initializes a new instance of the  class.
    /// 
    /// The target entity id.
    /// The walk manager.
    /// The skill selector.
    public SimpleAttackTechnique
    (
        long targetId,
        WalkManager walkManager,
        ISkillSelector skillSelector
    )
    {
        _targetId = targetId;
        _walkManager = walkManager;
        _skillSelector = skillSelector;
    }
    /// 
    public bool ShouldContinue(ICombatState state)
    {
        var map = state.Game.CurrentMap;
        if (map is null)
        {
            return false;
        }
        var entity = map.Entities.GetEntity(_targetId);
        return !(entity is null || (entity.Hp is not null && (entity.Hp.Amount <= 0 || entity.Hp.Percentage <= 0)));
    }
    /// 
    public Result HandleCombatStep(ICombatState state)
    {
        var map = state.Game.CurrentMap;
        if (map is null)
        {
            return new MapNotInitializedError();
        }
        if (_target is null)
        {
            var entity = map.Entities.GetEntity(_targetId);
            if (entity is null)
            {
                return new EntityNotFoundError();
            }
            _target = entity;
        }
        var character = state.Game.Character;
        if (character is null)
        {
            return new CharacterNotInitializedError();
        }
        if (_currentSkill is null)
        {
            var skills = state.Game.Skills;
            if (skills is null)
            {
                return new CharacterNotInitializedError("Skills");
            }
            var characterMp = character.Mp?.Amount ?? 0;
            var usableSkills = new[] { skills.PrimarySkill, skills.SecondarySkill }
                .Concat(skills.OtherSkills)
                .Where(x => x.Info is not null && x.Info.HitType != HitType.AlliesInZone)
                .Where(x => !x.IsOnCooldown && characterMp >= (x.Info?.MpCost ?? long.MaxValue));
            var skillResult = _skillSelector.GetSelectedSkill(usableSkills);
            if (!skillResult.IsSuccess)
            {
                if (skillResult.Error is SkillNotFoundError)
                {
                    return _target.Id;
                }
                return Result.FromError(skillResult);
            }
            _currentSkill = skillResult.Entity;
        }
        if (_currentSkill.Info is null)
        {
            var currentSkill = _currentSkill;
            _currentSkill = null;
            return new MissingInfoError("skill", currentSkill.SkillVNum);
        }
        if (character.Position is null)
        {
            return new CharacterNotInitializedError("Position");
        }
        if (_target.Position is null)
        {
            return new EntityNotFoundError();
        }
        var range = _currentSkill.Info.Range;
        if (_currentSkill.Info.TargetType == TargetType.Self && _currentSkill.Info.HitType == HitType.EnemiesInZone)
        {
            range = _currentSkill.Info.ZoneRange;
        }
        if (!character.Position.Value.IsInRange(_target.Position.Value, range))
        {
            state.WalkInRange(_walkManager, _target, _currentSkill.Info.Range);
        }
        else
        {
            state.UseSkill(_currentSkill, _target);
            _currentSkill = null;
        }
        return _target.Id;
    }
    /// 
    public Result HandleError(ICombatState state, ICombatOperation operation, Result result)
    {
        return result;
    }
}