using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.XR.Interaction.Toolkit;

namespace _PROJECT.NewHandPresence
{
    public class TutorialController : MonoBehaviour
    {
        private enum State
        {
            Initializing,
            Turn,
            Move,
            Teleport,
            WaitForGrip,
            Grip,
            Done
        }

        public ActionBasedController leftHand;
        public ActionBasedController rightHand;

        public ActionBasedContinuousMoveProvider moveProvider;
        public ActionBasedSnapTurnProvider turnProvider;
        public TeleportationProvider teleportProvider;

        public GameObject billboardTemplate;
        
        private XRControllerHintController _leftHintController;
        private XRControllerHintController _rightHintController;

        private SmartHandPresence _leftSmartHandPresence;
        private SmartHandPresence _rightSmartHandPresence;

        private State _state = State.Initializing;

        private Camera _camera;

        private List<XRGrabInteractable> _grabInteractables = new List<XRGrabInteractable>();
        private XRGrabInteractable _grabInteractable;

        private GameObject _billboard;

        private void Update()
        {
            if (_state == State.Initializing)
            {
                TryInitialize();
            }

            if (_state == State.WaitForGrip)
            {
                TryFindXRGrabInteractable();
            }
        }

        private void TryFindXRGrabInteractable()
        {
            // Find closest interactable in list
            XRGrabInteractable closestInteractable = null;
            var closestDistance = float.MaxValue;
            foreach (var grabInteractable in _grabInteractables)
            {
                var distance = Vector3.Distance(grabInteractable.transform.position, _camera.transform.position);
                if (!(distance < closestDistance)) continue;
                if (grabInteractable.CompareTag("Door")) continue;
                closestDistance = distance;
                closestInteractable = grabInteractable;
            }
            
            if (closestInteractable == null) return;
            
            _grabInteractable = closestInteractable;
            UpdateState(_state.Next());
        }

        private void UpdateState(State newState)
        {
            _state = newState;
            Debug.Log($"Tutorial state: {_state}");

            if (_state == State.Initializing) return;
            SetHandsVisibility(true);

            _rightHintController.HideHint();
            _leftHintController.HideHint();

            switch (_state)
            {
                case State.Initializing:
                    break;
                case State.Turn:
                    ShowTurnHint();
                    break;
                case State.Move:
                    ShowLocomotionHint();
                    break;
                case State.Teleport:
                    ShowTeleportHint();
                    break;
                case State.WaitForGrip:
                    SetHandsVisibility(true);
                    break;
                case State.Grip:
                    SetHandsVisibility(false);
                    CreateBillboard(_grabInteractable.gameObject, "Grab me!");
                    ShowGripHint();
                    break;
                case State.Done:
                    SetHandsVisibility(true);
                    DestroyBillboard();
                    break;
                default:
                    throw new ArgumentOutOfRangeException();
            }
        }

        private void CreateBillboard(GameObject gmeObject, string gripMe)
        {
            var number = 1 / gmeObject.transform.localScale.x;
            // Create billboard as a child on grab interactable
            _billboard = Instantiate(billboardTemplate, gmeObject.transform);
            // Set local position above grab interactable
            _billboard.transform.localPosition = new Vector3(0, 1, 0);
            // Unscale billboard relative to parent
            _billboard.transform.localScale = Vector3.one * number;
            // Get ControllerTextHint on billboard
            var controllerTextHint = _billboard.GetComponentInChildren<Billboard>();
            // Set text
            controllerTextHint.defaultText = gripMe;
            controllerTextHint.ShowHint(gripMe);
            // Set interactable to outlined layer
            //gmeObject.layer = LayerMask.NameToLayer("Outlined Objects");
        }

        private void DestroyBillboard()
        {
            Destroy(_billboard);
        }

        private bool CanInitialize()
        {
            if (leftHand == null || rightHand == null)
            {
                Debug.Log("Left or right hand is null");
                return false;
            }

            if (turnProvider.rightHandSnapTurnAction.action == null)
            {
                Debug.Log("Right hand snap turn action is null");
                return false;
            }

            if (moveProvider.leftHandMoveAction.action == null)
            {
                Debug.Log("Left hand move action is null");
                return false;
            }

            if (teleportProvider == null)
            {
                Debug.Log("Teleport provider is null");
                return false;
            }

            return true;
        }

        private void TryInitialize()
        {
            if (!CanInitialize()) return;

            _camera = Camera.main;

            Debug.Log("Initializing tutorial");

            _leftHintController = leftHand.GetComponentInChildren<XRControllerHintController>();
            _rightHintController = rightHand.GetComponentInChildren<XRControllerHintController>();

            Debug.Log($"Left hint controller: {_leftHintController}");
            Debug.Log($"Right hint controller: {_rightHintController}");

            _leftSmartHandPresence = leftHand.GetComponentInChildren<SmartHandPresence>();
            _rightSmartHandPresence = rightHand.GetComponentInChildren<SmartHandPresence>();

            Debug.Log($"Left smart hand presence: {_leftSmartHandPresence}");
            Debug.Log($"Right smart hand presence: {_rightSmartHandPresence}");

            if (_leftHintController == null ||
                _rightHintController == null ||
                _leftSmartHandPresence == null ||
                _rightSmartHandPresence == null)
            {
                Debug.Log("Hint controller or smart hand presence is null");
                return;
            }

            _leftHintController.xrBaseController = leftHand;
            _rightHintController.xrBaseController = rightHand;

            // Setup action listeners
            turnProvider.rightHandSnapTurnAction.action.performed += OnTurnPerformed;
            moveProvider.leftHandMoveAction.action.performed += OnMovePerformed;
            teleportProvider.endLocomotion += OnTeleportPerformed;

            leftHand.GetComponent<XRDirectInteractor>().selectEntered.AddListener(OnGripPerformed);
            rightHand.GetComponent<XRDirectInteractor>().selectEntered.AddListener(OnGripPerformed);

            UpdateState(_state.Next());
            Debug.Log("Tutorial initialized");
        }

        private void OnGripPerformed(SelectEnterEventArgs arg0)
        {
            if (_state != State.Grip) return;
            Debug.Log("Grip performed");
            UpdateState(_state.Next());
        }
        
        private void OnTeleportPerformed(LocomotionSystem obj)
        {
            if (_state != State.Teleport) return;
            Debug.Log("Teleport performed");
            UpdateState(_state.Next());
        }

        private void OnMovePerformed(InputAction.CallbackContext obj)
        {
            if (_state != State.Move) return;
            Debug.Log("Move performed");
            UpdateState(_state.Next());
        }

        private void OnTurnPerformed(InputAction.CallbackContext obj)
        {
            if (_state != State.Turn) return;
            Debug.Log("Turn performed");
            UpdateState(_state.Next());
        }

        private void ShowTeleportHint()
        {
            _rightHintController.ShowTeleportHint();
            _leftHintController.ShowTeleportHint();
        }

        private void ShowTurnHint()
        {
            _rightHintController.ShowTurnHint();
            _leftHintController.ShowTurnHint();
        }

        private void ShowLocomotionHint()
        {
            _rightHintController.ShowSmoothLocomotionHint();
            _leftHintController.ShowSmoothLocomotionHint();
        }

        private void ShowGripHint()
        {
            _rightHintController.ShowGripHint();
            _leftHintController.ShowGripHint();
        }

        private void SetControllerVisibility(bool visible)
        {
            _leftSmartHandPresence.showController = visible;
            _rightSmartHandPresence.showController = visible;
        }

        private void SetHandsVisibility(bool visible)
        {
            _leftSmartHandPresence.showHand = visible;
            _rightSmartHandPresence.showHand = visible;
        }

        private void OnTriggerEnter(Collider other)
        {
            // If has component add to list
            var grabInteractable = other.GetComponent<XRGrabInteractable>();
            if (grabInteractable != null)
            {
                _grabInteractables.Add(grabInteractable);
            }
        }

        private void OnTriggerExit(Collider other)
        {
            // If has component remove from list
            var grabInteractable = other.GetComponent<XRGrabInteractable>();
            if (grabInteractable != null)
            {
                _grabInteractables.Remove(grabInteractable);
            }
        }

    }
}