using System.Collections;
using System.Collections.Generic;
using EscapeRoomEngine.Desert.Runtime.Dispenser;
using EscapeRoomEngine.Engine.Runtime.Modules;
using EscapeRoomEngine.Engine.Runtime.Utilities;
using NaughtyAttributes;
using UnityEngine;

namespace EscapeRoomEngine.Desert.Runtime
{
    /// <summary>
    /// The main component for the orb throwing module.
    /// </summary>
    [RequireComponent(typeof(Collider))]
    public class Hoop : StatePuzzle
    {
        [Tooltip("How long to wait in seconds until the state is updated. Orbs that do not stay in the hoop should not be counted.")]
        public float updateDelay;
        [BoxGroup("Internal")] [SerializeField]
        private List<Emission> rings;

        private readonly HashSet<GameObject> _orbs = new();
        private Dispenser.Dispenser _dispenser;

        protected override void Start()
        {
            base.Start();
            
            PuzzleEvent += (_, type) =>
            {
                // ReSharper disable once SwitchStatementMissingSomeEnumCasesNoDefault
                switch (type)
                {
                    case PuzzleEventType.Restarted:
                        var color = Engine.Runtime.Engine.Theme.puzzleColor;
                        rings.ForEach(ring => ring.color = color.hdr);
                        _dispenser.Reset();
                        break;
                    case PuzzleEventType.Solved:
                        color = Engine.Runtime.Engine.Theme.solvedColor;
                        rings.ForEach(ring => ring.color = color.hdr);
                        _dispenser.Solve();
                        break;
                }
            };
        }

        protected override void SetState(int index, int value, bool checkSolution)
        {
            base.SetState(index, value, checkSolution);

            for (var i = 0; i < rings.Count; i++)
            {
                rings[i].active = i < value;
            }
        }

        private IEnumerator SetOrbCount()
        {
            if (!Solved)
            {
                yield return new WaitForSeconds(updateDelay);
                SetState(0, _orbs.Count, true);
            }
        }

        private void OnTriggerEnter(Collider other)
        {
            var orb = other.GetComponent<DispenserOrb>();
            if (orb)
            {
                _orbs.Add(orb.gameObject);
                StartCoroutine(SetOrbCount());
            }
        }

        private void OnTriggerExit(Collider other)
        {
            var orb = other.GetComponent<DispenserOrb>();
            if (orb)
            {
                _orbs.Remove(orb.gameObject);
                StartCoroutine(SetOrbCount());
            }
        }

        public override void SetModule(Module module)
        {
            base.SetModule(module);
            
            // The hoop requires a related dispenser module
            var firstRelatedModule = Module.relatedModules[0];
            if (firstRelatedModule.State is Dispenser.Dispenser dispenser)
            {
                _dispenser = dispenser;
            }
            else
            {
                throw new EngineException("Hoop was not assigned a related Dispenser.");
            }
        }
    }
}