using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.InputSystem;

namespace Nox.Game {
    public class PartyMovementHandler {
        private readonly PartyReference partyReference;
        private readonly ICameraController cameraController;
        private readonly InputSystem_Actions inputActions;
        private readonly MapLocationsReference mapLocationsReference;
        private readonly EncounterHandler encounterHandler;
        private readonly AdventureSettings adventureSettings;
        private readonly float maxDistance = 100f;
        private readonly AdventureData adventureData;

        private bool partyCanMove;
        private Vector3 targetPosition;
        private bool shouldHover;
        private MapLocation currentSelectedPoi;
        private bool hasClicked;
        private bool skipNextClick;

        public LayerMask clickToMoveMask = LayerMask.GetMask("Clickable");
        public LayerMask hoverOverMask = LayerMask.GetMask("Hoverable");

        public PartyMovementHandler(
            PartyReference partyReference,
            ICameraController cameraController,
            MapLocationsReference mapLocationsReference,
            Input.InputSettings inputSettings,
            EncounterHandler encounterHandler,
            AdventureData adventureData,
            AdventureSettings adventureSettings) {
            this.partyReference = partyReference;
            this.cameraController = cameraController;
            this.mapLocationsReference = mapLocationsReference;
            this.encounterHandler = encounterHandler;
            this.adventureData = adventureData;
            this.adventureSettings = adventureSettings;
            inputActions = inputSettings.inputActions;
        }

        public void Initialize() {
            inputActions.Player.ClickOnMap.performed += OnClickOnMap;
        }

        public void ConsumeNextClick() {
            skipNextClick = true;
        }

        private void OnClickOnMap(InputAction.CallbackContext obj) {
            if(!obj.action.WasReleasedThisFrame()) {
                return;
            }
            hasClicked = true;
        }

        private void HandleHoverableLocation(RaycastHit hitInfo) {
            var go = hitInfo.collider.gameObject;
            if(!go.transform.parent.TryGetComponent(out MapLocation mapLocation)) {
                return;
            }
            currentSelectedPoi = mapLocation;
            mapLocation.highlight.gameObject.SetActive(true);
        }

        private void HandleClickableLocation() {
            var screenPos = inputActions.Player.Point.ReadValue<Vector2>();
            var ray = cameraController.CameraReference.mainCamera.ScreenPointToRay(screenPos);
            if(!Physics.Raycast(ray, out var clickHit, maxDistance, clickToMoveMask, QueryTriggerInteraction.Ignore)) {
                return;
            }
            if(!clickHit.collider.gameObject.transform.parent.TryGetComponent(out MapReference mapReference)) {
                return;
            }
            if(ValidateMoveLocation(clickHit)) {
                targetPosition = clickHit.point;
                partyCanMove = true;
            }
        }

        private bool ValidateMoveLocation(object hitInfoPoint) {
            return true;
        }

        public void Tick() {
            HandleHover();

            if(hasClicked) {
                hasClicked = false;
                // should it skip next click to avoid click-through from the pause menu?
                if(skipNextClick) {
                    skipNextClick = false;
                    return;
                }
                if(EventSystem.current.IsPointerOverGameObject()) {
                    return;
                }
                HandleClickableLocation();
            }

            if(adventureData.isEncounterActive) {
                partyCanMove = false;
                adventureData.isPartyMoving = false;
                return;
            }

            if(!partyCanMove) {
                adventureData.isPartyMoving = false;
                return;
            }

            if(Vector3.Distance(partyReference.transform.position, targetPosition) < 0.05f) {
                partyReference.transform.position = targetPosition;
                VerifyPointsOfInterest();
                partyCanMove = false;
            }

            adventureData.isPartyMoving = true;
            var pos = Vector3.MoveTowards(
                new Vector3(partyReference.transform.position.x, 0,
                    partyReference.transform.position.z), targetPosition,
                Time.deltaTime * adventureSettings.partyBaseSpeed);
            partyReference.transform.position = pos;
            encounterHandler?.CheckForEncounters(pos);
        }

        private void HandleHover() {
            if(EventSystem.current.IsPointerOverGameObject()) {
                return;
            }
            var screenPos = inputActions.Player.Point.ReadValue<Vector2>();
            var ray = cameraController.CameraReference.mainCamera.ScreenPointToRay(screenPos);
            if(Physics.Raycast(ray, out var hoverHit, maxDistance, hoverOverMask, QueryTriggerInteraction.Ignore)) {
                HandleHoverableLocation(hoverHit);
            }
            else {
                currentSelectedPoi?.highlight.gameObject.SetActive(false);
                currentSelectedPoi = null;
            }
        }

        private void VerifyPointsOfInterest() {
            foreach(var location in mapLocationsReference.mapLocations) {
                if(Vector3.Distance(location.mapLocation.transform.position, partyReference.transform.position) < 0.4f) {
                    Debug.Log($"Arrived at {location.locationId}");
                    break;
                }
            }
        }

        public void Dispose() {
            inputActions.Player.ClickOnMap.performed -= OnClickOnMap;
        }
    }
}
