using System;
using Grid;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.Serialization;

namespace Actions {
    public class UnitActionSystem : MonoBehaviour {
        [FormerlySerializedAs("UnitsLayerMask")] [SerializeField]
        private LayerMask unitsLayerMask;

        [SerializeField] private Unit selectedUnit;
        private bool isBusy;
        private BaseAction selectedAction;

        public static UnitActionSystem Instance { get; private set; }

        private bool IsBusy {
            get => isBusy;
            set {
                isBusy = value;
                OnBusyChanged?.Invoke(this, IsBusy);
            }
        }

        public BaseAction SelectedAction {
            get => selectedAction;
            private set {
                selectedAction = value;
                OnSelectedActionChanged?.Invoke(this, EventArgs.Empty);
            }
        }

        public Unit SelectedUnit {
            get => selectedUnit;
            private set {
                selectedUnit = value;
                OnSelectedUnitChanged?.Invoke(this, EventArgs.Empty);
            }
        }

        private void Awake() {
            if (Instance is not null) {
                Debug.LogError($"There is more than one UnitActionSystem! {transform} - {Instance}");
                Destroy(gameObject);
                return;
            }

            Instance = this;
        }

        private void Start() => SetSelectedUnit(SelectedUnit);

        private void Update() {
            if (IsBusy) return;
            if (!TurnSystem.Instance.IsPlayerTurn) return;
            if (EventSystem.current.IsPointerOverGameObject()) return;
            if (TryHandleUnitSelection()) return;
            if (SelectedUnit is not null) HandleSelectedAction();
        }

        private void HandleSelectedAction() {
            if (!InputManager.Instance.IsMouseButtonDownThisFrame()) return;
            GridPosition mouseGridPosition = LevelGrid.Instance.GetGridPosition(MouseWorld.GetPosition());
            if (!SelectedAction.IsValidActionGridPosition(mouseGridPosition)) return;
            if (!selectedUnit.TrySpendActionPointsToTakeAction(SelectedAction)) return;
            SetBusy();
            SelectedAction.TakeAction(mouseGridPosition, ClearBusy);
            OnActionStarted?.Invoke(this, EventArgs.Empty);
        }

        private void SetBusy() => IsBusy = true;

        private void ClearBusy() => IsBusy = false;

        public event EventHandler OnSelectedUnitChanged;
        public event EventHandler OnSelectedActionChanged;
        public event EventHandler<bool> OnBusyChanged;
        public event EventHandler OnActionStarted;

        private bool TryHandleUnitSelection() {
            if (!InputManager.Instance.IsMouseButtonDownThisFrame()) return false;
            if (!Physics.Raycast(Camera.main.ScreenPointToRay(InputManager.Instance.GetMouseScreenPosition()), out RaycastHit raycastHit, float.MaxValue, unitsLayerMask)) return false;
            if (!raycastHit.transform.TryGetComponent(out Unit unit) || unit is null) return false;
            if (unit == selectedUnit) return false;
            if (unit.IsEnemy) return false;

            SetSelectedUnit(unit);
            return true;
        }

        private void SetSelectedUnit(Unit unit) {
            SelectedUnit = unit;
            SelectedAction = unit.GetAction<MoveAction>();
        }

        public void SetSelectedAction(BaseAction baseAction) => SelectedAction = baseAction;
    }
}