using Jovian.ZoneSystem;
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 ZoneSystem zoneSystem;
        private readonly AdventureSettings adventureSettings;

        private readonly float maxDistance = 100f;

        private AdventureData adventureData;
        private bool partyCanMove;
        private Vector3 targetPosition;
        private bool shouldHover;
        private MapLocation currentSelectedPoi;
        private string previousZoneId;
        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,
            ZoneSystem zoneSystem,
            AdventureData adventureData,
            AdventureSettings adventureSettings) {
            this.partyReference = partyReference;
            this.cameraController = cameraController;
            this.mapLocationsReference = mapLocationsReference;
            this.zoneSystem = zoneSystem;
            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(mapReference.ValidateMoveLocation(hitInfoPoint)) {
            targetPosition = clickHit.point;
            partyCanMove = 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(!partyCanMove) {
                adventureData.isPartyMoving = false;
                return;
            }

            if(Vector3.Distance(partyReference.transform.position, targetPosition) < 0.05f) {
                partyReference.transform.position = targetPosition;
                VerifyPointsOfInterest();
                partyCanMove = false;
            }
            adventureData.isPartyMoving = true;
            partyReference.transform.position = Vector3.MoveTowards(new Vector3(partyReference.transform.position.x, 0, partyReference.transform.position.z), targetPosition, Time.deltaTime * adventureSettings.partyBaseSpeed);
            VerifyZones(partyReference.transform.position);
        }

        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 VerifyZones(Vector3 position) {
            var zoneContext = zoneSystem.QueryZone(position);
            var currentZoneId = zoneContext.resolvedZoneId;

            if(currentZoneId != previousZoneId) {
                if(!string.IsNullOrEmpty(currentZoneId)) {
                    Debug.Log($"Entered zone: {currentZoneId} (encounter: {zoneContext.encounterTableId}, safe: {zoneContext.isSafe})");
                }
                else if(!string.IsNullOrEmpty(previousZoneId)) {
                    Debug.Log($"Left zone: {previousZoneId}");
                }
                previousZoneId = currentZoneId;
            }
        }

        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;
        }
    }

}
