using System.Collections.Generic;
using System.Linq;
using EscapeRoomEngine.Engine.Runtime.Modules;
using EscapeRoomEngine.Engine.Runtime.Utilities;
using JetBrains.Annotations;
using NaughtyAttributes;
using UnityEngine;
using Logger = EscapeRoomEngine.Engine.Runtime.Utilities.Logger;
using LogType = EscapeRoomEngine.Engine.Runtime.Utilities.LogType;

namespace EscapeRoomEngine.Desert.Runtime.Puzzle_A
{
    /// <summary>
    /// The main component for the terminal module.
    /// </summary>
    [RequireComponent(typeof(Animator))]
    public class Terminal : CyclicStepPuzzle
    {
        private static readonly int LightFlash = Animator.StringToHash("Light Flash");
        
        [BoxGroup("Internal")] [Required]
        public Emission terminalLight;
        [BoxGroup("Internal")]
        [ValidateInput("SymbolCount", "Must have same amount of symbols and slots.")]
        public List<SymbolButton> symbols;
        [BoxGroup("Internal")]
        public List<Transform> symbolSlots;

        private IOption<Symbol> _activeSymbol;
        private Animator _animator;
        private Ball _ball;

        private void Awake()
        {
            _animator = GetComponent<Animator>();

            List<SymbolButton> symbolInstances = new(symbols.Count);
            symbolInstances.AddRange(symbols.Select((t, i) => Instantiate(t, symbolSlots[i], false)));
            symbols = symbolInstances;
        }

        protected override void Start()
        {
            base.Start();
            
            PuzzleEvent += (_, type) =>
            {
                // ReSharper disable once SwitchStatementHandlesSomeKnownEnumValuesWithDefault
                switch (type)
                {
                    case PuzzleEventType.Restarted:
                        _ball.Active = true;
                        terminalLight.color = Engine.Runtime.Engine.Theme.puzzleColor.hdr;
                        symbols.ForEach(symbol => symbol.Enable());
                        break;
                    case PuzzleEventType.Solved:
                        _ball.Active = false;
                        terminalLight.color = Engine.Runtime.Engine.Theme.solvedColor.hdr;
                        symbols.ForEach(symbol => symbol.Disable());
                        break;
                    case PuzzleEventType.WrongInput:
                        _animator.SetTrigger(LightFlash);
                        break;
                }
            };
            
            symbols.ForEach(symbol =>
            {
                symbol.ButtonEvent += (_, type) =>
                {
                    if (type == ButtonEventType.Pressed)
                    {
                        PressedSymbol(symbol.symbolNumber);
                    }
                };
            });
            
            _ball.ring.symbols.ForEach(symbol =>
            {
                symbol.SymbolEvent += type =>
                    _activeSymbol = type == SymbolEventType.Activated ? Some<Symbol>.Of(symbol) : None<Symbol>.New();
            });
        }

        public override void SetModule(Module module)
        {
            base.SetModule(module);
            
            // The terminal requires a related symbol ball module
            var firstRelatedModule = Module.relatedModules[0];
            if (firstRelatedModule.State is Ball ball)
            {
                _ball = ball;
            }
            else
            {
                throw new EngineException("Terminal was not assigned a related Ball.");
            }
            
            TurnOnLights();
        }

        [UsedImplicitly] // required in animation
        internal void TurnOnLights()
        {
            terminalLight.active = true;
            _ball.StatusLight = true;
        }

        [UsedImplicitly] // required in animation
        internal void TurnOffLights()
        {
            terminalLight.active = false;
            _ball.StatusLight = false;
        }

        private void PressedSymbol(int number)
        {
            if (!Solved)
            {
                Logger.Log($"Pressed symbol {number} on {this}", LogType.PuzzleDetail);

                _activeSymbol.Match(some: symbol =>
                {
                    if (number == symbol.symbolNumber)
                    {
                        CheckStep(symbol.symbolPosition);
                    }
                    else
                    {
                        WrongInput();
                    }
                }, none: WrongInput);
            }
        }

        #region Debug Buttons

        [UsedImplicitly]
        [Button(enabledMode: EButtonEnableMode.Playmode)]
        public void PressSymbol0() => PressedSymbol(0);
        [UsedImplicitly]
        [Button(enabledMode: EButtonEnableMode.Playmode)]
        public void PressSymbol1() => PressedSymbol(1);
        [UsedImplicitly]
        [Button(enabledMode: EButtonEnableMode.Playmode)]
        public void PressSymbol2() => PressedSymbol(2);

        #endregion

        [UsedImplicitly] // used for field validation
        private bool SymbolCount(List<SymbolButton> list) => list.Count == symbolSlots.Count;
    }
}