using UnityEngine;
using UnityEngine.AI;
using VOID.Generic.Objects.Abstract;
using VOID.Generic.Objects.Unit.Actions.Exceptions;
using VOID.Generic.Objects.Unit.Events;
using VOID.Generic.Util;

namespace VOID.Generic.Objects.Unit.Actions
{
    public class MoveAction : AbstractAction
    {
        private readonly UnitObject _unit;
        private readonly Vector3 _targetPosition;
        private readonly AbstractObject _targetObject;
        private readonly float _untilInRange;

        public MoveAction(UnitObject unit, Vector3 targetPosition, float untilInRange = 0f)
        {
            _unit = unit;
            _targetPosition = targetPosition;
            _untilInRange = untilInRange;
        }

        public MoveAction(UnitObject unit, AbstractObject targetObject, float untilInRange = 0f)
            : this(unit, targetObject.transform.position, untilInRange + targetObject.basicData.radius)
        {
            _targetObject = targetObject;
        }
        
        public override bool IsInRange()
        {
            // Dont let player ordering move too far away
            var distanceBetween = Vector3.Distance(_unit.transform.position, _targetPosition);
            return distanceBetween < 100f;
        }

        public override bool CanBeStopped()
        {
            return !_unit.unitState.isTraversing;
        }

        protected override void OnEndIt()
        {
            _unit.unitEvents.onMoveEnd.RemoveListener(OnMoveEnd);
        }

        protected override void OnCancelIt()
        {
            _unit.unitEvents.onMoveEnd.RemoveListener(OnMoveEnd);
            _unit.unitNavAgent.Stop();
        }
        
        public override void CanDoIt()
        {
            Check(
                _unit.unitData.canMove,
                UnitActionErrorType.ThisUnitCantMove);
            Check(
                state == ActionState.Ready,
                UnitActionErrorType.ActionStateIsNotReady);
            Check(
                !_unit.unitState.isCasting,
                UnitActionErrorType.ThisUnitIsCasting);
            if (_unit.basicState.turnManager)
            {
                Check(
                    _unit.unitState.isSelfTurn,
                    UnitActionErrorType.UnitSelfTurnNotActive);
                Check(
                    _unit.unitData.currentResources.movePoints > 0.1f,
                    UnitActionErrorType.UnitNotEnoughMovePoints);
            }
            Check(
                _unit.unitState.isControllable,
                UnitActionErrorType.UnitStateIsNotControllable);
            Check(
                IsInRange(),
                UnitActionErrorType.NotInRange);
            _unit.unitNavAgent.CalculatePathTo(_targetPosition, _untilInRange);
            Check(
                _unit.unitNavAgent.GetPath().status is not NavMeshPathStatus.PathInvalid,
                UnitActionErrorType.NoPathToTarget);
        }

        protected override void OnDoIt()
        {
            var isInRange = _targetObject
                ? RangeUtil.IsInRange(_unit, _targetObject, _untilInRange, false)
                : RangeUtil.IsInRange(_unit, _targetPosition, _untilInRange, false);
            
            if (isInRange)
            {
                EndIt();
                return;
            }
            
            BeforePerform();
            
            _unit.unitEvents.onMoveEnd.AddListener(OnMoveEnd);
            var moveStarted = _targetObject
                ? _unit.unitNavAgent.Move(_targetObject, _untilInRange)
                : _unit.unitNavAgent.Move(_targetPosition, _untilInRange);

            // Unit didn't move for some reason (usually path is impossible or already at path destination)
            if (!moveStarted) EndIt();
        }
        
        private void OnMoveEnd(MoveEvent moveEvent)
        {
            AfterPerform();
            EndIt();
        }
    }
}