using System;
using UnityEngine;
using UnityEngine.AI;

namespace TagFighter
{
    public class MoveToAction : IAction
    {
        Vector3 _lastTargetPosition;
        NavMeshAgent _agent;
        float _originalStoppingDistance;
        float _desiredStoppingDistance;
        bool _isFirstAdvance = true;
        static State s_resetState = new(false, 0f, false);
        bool _disposed = false;

        public MoveToAction(Weaver unit, Vector3 target) {
            if (unit == null) {
                throw new ArgumentNullException("unit");
            }

            if (unit.TryGetComponent(out _agent) == false) {
                throw new ArgumentException("Missing NavMeshAgent", "unit");
            }

            _lastTargetPosition = target;
            _desiredStoppingDistance = _agent.stoppingDistance;
        }
        public MoveToAction(Weaver unit, Vector3 target, float stoppingDistance) {
            if (unit == null) {
                throw new ArgumentNullException("unit");
            }

            if (stoppingDistance < 0) {
                throw new ArgumentException("Cannot be < 0", "stoppingDistance");
            }

            if (unit.TryGetComponent(out _agent) == false) {
                throw new ArgumentException("Missing NavMeshAgent", "unit");
            }

            _lastTargetPosition = target;
            _desiredStoppingDistance = stoppingDistance;
        }
        public bool SetDestination(Vector3 target) {
            _lastTargetPosition = target;
            return _agent.SetDestination(target);
        }

        public void SetStoppingDistance(float stoppingDistance) {
            if (stoppingDistance < 0) {
                throw new ArgumentException("Cannot be < 0", "stoppingDistance");
            }
            _desiredStoppingDistance = stoppingDistance;
            _agent.stoppingDistance = _desiredStoppingDistance;
        }

        public IActionState Advance() {
            if (_isFirstAdvance) {
                _originalStoppingDistance = _agent.stoppingDistance;
                _agent.stoppingDistance = _desiredStoppingDistance;
                _agent.isStopped = false;
                _agent.SetDestination(_lastTargetPosition);
                _isFirstAdvance = false;
            }

            var isAdvanced = _agent.hasPath || _agent.pathPending || _agent.remainingDistance > _agent.stoppingDistance;

            return new State(isAdvanced, CompletionStatus(), isAdvanced);
        }

        IActionState Cancel() {
            _agent.isStopped = true;
            _agent.stoppingDistance = _originalStoppingDistance;
            _agent.ResetPath();
            _isFirstAdvance = true;

            return s_resetState;
        }

        public float CompletionStatus() {
            return 0;
        }

        public bool IsSimilarAction(IActionRead action) {
            if (action is not MoveToAction other) {
                return false;
            }

            return other != null && _agent == other._agent && _lastTargetPosition == other._lastTargetPosition;
        }

        public override string ToString() {
            return $"Move -> {_lastTargetPosition}";
        }

        public void Dispose() {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        protected virtual void Dispose(bool disposing) {
            if (!_disposed) {
                if (disposing) {
                    Cancel();
                    _agent = null;
                }

                _disposed = true;
            }
        }

        public readonly struct State : IActionState
        {
            public bool IsAdvanced { get; }
            public float CompletionStatus { get; }
            public bool IsMoving { get; }

            public State(bool isAdvanced, float completionStatus, bool isMoving) {
                IsAdvanced = isAdvanced;
                CompletionStatus = completionStatus;
                IsMoving = isMoving;
            }
        }
    }
}