using System;
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.Engine.Runtime.Modules
{
    public enum PuzzleEventType
    {
        Restarted, Solved, WrongInput
    }

    public static class PuzzleEventExtensions
    {
        public static string Description(this PuzzleEventType type, PuzzleModule module)
        {
            return type switch
            {
                PuzzleEventType.Restarted => $"{module} has been restarted",
                PuzzleEventType.Solved => $"{module} has been solved",
                PuzzleEventType.WrongInput => $"Wrong input for {module}",
                _ => throw new ArgumentOutOfRangeException(nameof(type), type, null)
            };
        }
    }

    public delegate void PuzzleEventHandler(PuzzleModule source, PuzzleEventType e);
    
    /// <summary>
    /// The <see cref="ModuleState"/> of a <see cref="PuzzleModule"/>. Handles all events that can occur for a door.
    /// </summary>
    [SelectionBase]
    public class PuzzleState : ModuleState
    {
        /// <summary>
        /// Add event handlers to this hook to receive all events concerning this puzzle.
        /// </summary>
        public event PuzzleEventHandler PuzzleEvent;

        /// <summary>
        /// The puzzle module this state belongs to.
        /// </summary>
        protected PuzzleModule Module { get; private set; }
        public bool Solved
        {
            get => _solved;
            private set
            {
                var type =
                    !_solved && value ? Some<PuzzleEventType>.Of(PuzzleEventType.Solved)
                    : _solved && !value ? Some<PuzzleEventType>.Of(PuzzleEventType.Restarted)
                    : None<PuzzleEventType>.New();
                _solved = value;
                type.Match(some: OnPuzzleEvent);
            }
        }
        
        private bool _solved;

        protected virtual void Start()
        {
            OnPuzzleEvent(PuzzleEventType.Restarted);
        }

        private void OnPuzzleEvent(PuzzleEventType type)
        {
            Logger.Log(type.Description(Module), LogType.PuzzleFlow);
            
            PuzzleEvent?.Invoke(Module, type);
        }

        public override void SetModule(Module module)
        {
            Module = PuzzleModule.FromModule(module);
        }

        #region Debug Buttons

        [Button(enabledMode: EButtonEnableMode.Playmode)]
        public void Solve() => Solved = true;
        [UsedImplicitly]
        [Button(enabledMode: EButtonEnableMode.Playmode)]
        public void Restart() => Solved = false;
        [Button("Trigger Wrong Input", EButtonEnableMode.Playmode)]
        public void WrongInput()
        {
            if (!Solved)
            {
                OnPuzzleEvent(PuzzleEventType.WrongInput);
            }
        }
        
        #endregion

        public static PuzzleState FromState(ModuleState state)
        {
            if (state is PuzzleState puzzleState)
            {
                return puzzleState;
            }

            throw new WrongTypeException(typeof(PuzzleState), state.GetType(), typeof(ModuleState));
        }
    }
}