using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Linq;
using UnityEngine.EventSystems;
using System.Threading;
using System.Threading.Tasks;

public class BusyArgs : EventArgs {
    public bool busy {get;}
    public BusyArgs(bool isBusy) {
        this.busy = isBusy;
    }
}

public class UnitActionSystem : MonoBehaviour
{
    public static UnitActionSystem Instance {get; private set;}
    public event EventHandler OnSelectedUnitChanged;
    public event EventHandler OnSelectedActionChanged;
    public event EventHandler OnActionStarted;
    public event EventHandler<BusyArgs> OnBusyChanged;

    [SerializeField] private Unit selectedUnit;
    [SerializeField] private LayerMask unitMask;
    [SerializeField] private TurnSystem turnSystem;
    [SerializeField] private UnitManager unitManager;

    private BaseAction selectedAction;
    private bool isBusy = false;

    private void Awake() 
    {
        if (Instance != null)
        {
            Debug.LogError("There's more than one UnitActionSystem" + transform + " - " + Instance);
            Destroy(gameObject);
            return;
        }
        Instance = this;
    }
    private void Start() {
        Unit.OnAnyUnitDead += Unit_OnAnyUnitDead;
        SetSelectedUnit(selectedUnit);
    }
    private  void Update()
    {

        if (isBusy) {
            return;
        }

        if (!turnSystem.IsPlayerTurn()) {
            return;
        }
        
        if (EventSystem.current.IsPointerOverGameObject()) {
             return;
        }
        if (TryHandleUnitSelection()) {
            return;
        }

        HandleSelectedAction();
    }

    private void HandleSelectedAction() {
        if (!InputManager.Instance.IsMouseButtonDownThisFrame()) return;

        GridPosition targetGridPosition = LevelGrid.Instance.GetGridPosition(MouseWorld.GetPosition());
        
        if (!selectedAction.IsValidActionGridPosition(targetGridPosition)) {
            Debug.Log("Invalid Grid Position");
            return;
        }
        if (!selectedUnit.TrySpendActionPointToTakeAction(selectedAction)) {
            Debug.Log("Cannot Spend Action Points");
            return;
        }
        SetBusy();
        OnActionStarted?.Invoke(this, EventArgs.Empty);
        selectedAction.TakeAction(targetGridPosition, out CancellationToken cancellationToken);
        cancellationToken.Register(ClearBusy);
    }
    private void SetBusy() {
        isBusy = true;
        OnBusyChanged?.Invoke(this, new BusyArgs(isBusy));

    }
    private void ClearBusy() {
        isBusy = false;
        OnBusyChanged?.Invoke(this, new BusyArgs(isBusy));
    }
        
    private bool TryHandleUnitSelection()
    {
        if (!InputManager.Instance.IsMouseButtonDownThisFrame()) {
            return false;
        }

        Ray ray = Camera.main.ScreenPointToRay(InputManager.Instance.GetMouseScreenPosition());

        if (!Physics.Raycast(ray, out RaycastHit raycastHit, float.MaxValue, unitMask)) {
            return false;
        }

        if (!raycastHit.transform.TryGetComponent<Unit>(out Unit unit)) {
            return false;
        }

        if (selectedUnit == unit) {
            return false;
        }

        if (unit.IsEnemy()) {
            return false;
        }

        SetSelectedUnit(unit);
        return true;
    }

    private void SetSelectedUnit(Unit selectedUnit) 
    {
        this.selectedUnit = selectedUnit;
        SetSelectedAction(selectedUnit.GetDefaultAction());
        OnSelectedUnitChanged?.Invoke(this, EventArgs.Empty);
    }

    public void SetSelectedAction(BaseAction baseAction) {
        selectedAction = baseAction;
        OnSelectedActionChanged?.Invoke(this, EventArgs.Empty);
    }

    public Unit GetSelectedUnit()
    {
        return selectedUnit;
    }
    public BaseAction GetSelectedAction() {
        return selectedAction;
    }
    public void Unit_OnAnyUnitDead (object sender, EventArgs empty) {
        Unit unit = sender as Unit;
        if (unit == selectedUnit) {
            var friendlyUnitList = unitManager.GetFriendlyUnitList();
            if (friendlyUnitList.Count > 0) {
                SetSelectedUnit(friendlyUnitList[0]);
            }
        }
    }
}