using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Interactions;
using UnityEngine.UIElements;
using VOID.UserInterface;

namespace VOID.Generic.Player.Input
{
    [DefaultExecutionOrder(1)]
    public class PlayerInputManager : MonoBehaviour
    {
        public static PlayerInputManager current { get; private set; }
        
        private Inputs _inputs;
        
        // Input actions for player controlling
        private InputAction _defaultUse;
        private InputAction _contextUse;
        private InputAction _defaultUseOnUI;
        private InputAction _forceAttack;
        private InputAction _forceMove;
        private InputAction _dragging;
        private InputAction _highlight;
        
        // Events
        public event Action OnDefaultUse; 
        public event Action OnContextUse; 
        public event Action<Vector2, List<VisualElement>> OnDefaultUseUI; 
        public event Action<Vector2, List<VisualElement>> OnDefaultAlternateUseUI; 
        public event Action OnForceAttackStart; 
        public event Action OnForceAttackEnd; 
        public event Action OnForceMoveStart; 
        public event Action OnForceMoveEnd;
        public event Action OnDraggingPrepare;
        public event Action OnDraggingStart; 
        public event Action OnDraggingEnd; 
        public event Action OnHighlightStart; 
        public event Action OnHighlightEnd; 

        // States
        public bool isForceAttack { get; private set; }
        public bool isForceMove { get; private set; }
        public bool isDragging { get; private set; }
        public bool isHighlight { get; private set; }
        
        // Cache - position and VisualElement when clicking on UI started
        private Vector2 _cursorPositionOnUI;
        private List<VisualElement> _elementsUnderCursor;

        private void Awake()
        {
            current = this;
            _inputs = new Inputs();

            // Default on SCENE
            _defaultUse = _inputs.PlayerControls.DefaultUse;
            _defaultUse.performed += _ => OnDefaultUse?.Invoke();
            
            // Context on SCENE
            _contextUse = _inputs.PlayerControls.ContextUse;
            _contextUse.performed += _ => OnContextUse?.Invoke();
            
            // Default on UI (or alternate default)
            _defaultUseOnUI = _inputs.PlayerControls.DefaultUseOnUI;
            _defaultUseOnUI.started += callback =>
            {
                if (callback.interaction is not MultiTapInteraction) return;
                _cursorPositionOnUI = default;
                _elementsUnderCursor = default;
                if (GameUI.current.isCursorOverUI == false) return;
                _cursorPositionOnUI = GameUI.current.cursorPositionOnUI;
                _elementsUnderCursor = GameUI.current.elementsUnderCursor.ToList();
            };
            _defaultUseOnUI.performed += callback =>
            {
                if (_cursorPositionOnUI == default) return;
                if (callback.interaction is MultiTapInteraction) OnDefaultUseUI?.Invoke(_cursorPositionOnUI, _elementsUnderCursor);
                else OnDefaultAlternateUseUI?.Invoke(_cursorPositionOnUI, _elementsUnderCursor);
            };

            // PlayerForceAttack
            _forceAttack = _inputs.PlayerControls.ForceAttack;
            _forceAttack.started += _ => isForceAttack = true;
            _forceAttack.started += _ => OnForceAttackStart?.Invoke();
            _forceAttack.canceled += _ => isForceAttack = false;
            _forceAttack.canceled += _ => OnForceAttackEnd?.Invoke();

            // PlayerForceMove
            _forceMove = _inputs.PlayerControls.ForceMove;
            _forceMove.started += _ => isForceMove = true;
            _forceMove.started += _ => OnForceMoveStart?.Invoke();
            _forceMove.canceled += _ => isForceMove = false;
            _forceMove.canceled += _ => OnForceMoveEnd?.Invoke();
            
            // PlayerDragging
            _dragging = _inputs.PlayerControls.Dragging;
            _dragging.started += _ => isDragging = true;
            _dragging.started += _ => OnDraggingPrepare?.Invoke();
            _dragging.performed += _ => OnDraggingStart?.Invoke();
            _dragging.canceled += _ => isDragging = false;
            _dragging.canceled += _ => OnDraggingEnd?.Invoke();
            
            // Highlight
            _highlight = _inputs.PlayerControls.Highlight;
            _highlight.started += _ => isHighlight = true;
            _highlight.started += _ => OnHighlightStart?.Invoke();
            _highlight.canceled += _ => isHighlight = false;
            _highlight.canceled += _ => OnHighlightEnd?.Invoke();
        }

        private void OnEnable()
        {
            _defaultUse.Enable();
            _defaultUseOnUI.Enable();
            _contextUse.Enable();
            _forceAttack.Enable();
            _forceMove.Enable();
            _dragging.Enable();
            _highlight.Enable();
        }

        private void OnDisable()
        {
            _defaultUse.Disable();
            _defaultUseOnUI.Disable();
            _contextUse.Disable();
            _forceAttack.Disable();
            _forceMove.Disable();
            _dragging.Disable();
            _highlight.Disable();
        }
    }
}
