using EscapeRoomEngine.Engine.Runtime.Modules;
using EscapeRoomEngine.Engine.Runtime.Modules.State;
using EscapeRoomEngine.Engine.Runtime.Utilities;
using NaughtyAttributes;
using Station46.Modules.Dispenser.Scripts;
using Station46.Scripts;
using UnityEngine;
using UnityEngine.XR.Interaction.Toolkit;

namespace Station46.Modules.Rings.Scripts
{
    public class AlignRings : StatePuzzle
    {
        private static readonly int SlotIn = Animator.StringToHash("Slot In");
        
        public float accuracy = 0.1f;
        public float sensitivity = 0.001f;
        public Vector3 movementScaling = Vector3.one;
        [BoxGroup("Internal")] [SerializeField] private Transform target;
        [BoxGroup("Internal")] [SerializeField] private Ring ringX, ringY, ringZ;
        [BoxGroup("Internal")] [SerializeField] private Animator animator;

        private DispenserOrb _orb;
        private Vector3 _previousPosition;

        protected override void Start()
        {
            base.Start();

            PuzzleEvent += (_, type) =>
            {
                // ReSharper disable once SwitchStatementMissingSomeEnumCasesNoDefault
                switch (type)
                {
                    case PuzzleEventType.Restarted:
                        _orb.GetComponent<XRGrabInteractable>().enabled = true;
                        _orb.Color = EscapeRoomEngine.Engine.Runtime.Engine.Theme.puzzleColor.hdr;
                        break;
                    case PuzzleEventType.Solved:
                        _orb.transform.position = target.position;
                        ringX.Displacement = 0;
                        ringY.Displacement = 0;
                        ringZ.Displacement = 0;
                        var color = EscapeRoomEngine.Engine.Runtime.Engine.Theme.solvedColor.hdr;
                        ringX.Emission.color = color;
                        ringY.Emission.color = color;
                        ringZ.Emission.color = color;
                        _orb.Color = color;
                        _orb.GetComponent<XRGrabInteractable>().enabled = false;
                        animator.SetTrigger(SlotIn);
                        break;
                }
            };
        }

        private void Update()
        {
            if (!Solved)
            {
                var position = _orb.transform.position - target.position;
                ringX.Displacement = position.x * movementScaling.x;
                ringY.Displacement = position.y * movementScaling.y;
                ringZ.Displacement = position.z * movementScaling.z;
                
                SetState(0, Mathf.RoundToInt(ringX.Displacement / accuracy), false);
                SetState(1, Mathf.RoundToInt(ringY.Displacement / accuracy), false);
                SetState(2, Mathf.RoundToInt(ringZ.Displacement / accuracy), true);

                if (_orb.Color != EscapeRoomEngine.Engine.Runtime.Engine.Theme.solvedColor.hdr)
                {
                    _orb.Color = (position - _previousPosition).magnitude < sensitivity
                        ? EscapeRoomEngine.Engine.Runtime.Engine.Theme.puzzleColor.hdr
                        : EscapeRoomEngine.Engine.Runtime.Engine.Theme.activeColor.hdr;
                    _previousPosition = position;
                }
            }
        }

        public override void SetModule(Module module)
        {
            base.SetModule(module);
            
            // The holes require a related dispenser module
            var firstRelatedModule = Module.relatedModules[0];
            if (firstRelatedModule.State is DispenserOrbGroup orbGroup)
            {
                _orb = orbGroup.GetComponentInChildren<DispenserOrb>();
            }
            else
            {
                throw new EngineException("Align rings was not assigned a related dispenser orb.");
            }
        }
    }
}