using EscapeRoomEngine.Engine.Runtime.Utilities;
using NaughtyAttributes;

namespace EscapeRoomEngine.Engine.Runtime.Modules
{
    public enum DoorEventType
    {
        Locked, Unlocked, Connected, ExitedFrom
    }
    
    public delegate void DoorEventHandler(DoorModule source, DoorEventType e);
    
    /// <summary>
    /// The <see cref="ModuleState"/> of a <see cref="DoorModule"/>. Handles all events that can occur for a door.
    /// </summary>
    public class DoorState : ModuleState
    {
        /// <summary>
        /// Add event handlers to this hook to receive all events concerning this door.
        /// </summary>
        public event DoorEventHandler DoorEvent;

        /// <summary>
        /// The door module this state belongs to.
        /// </summary>
        protected DoorModule Module { get; set; }
        public bool Unlocked
        {
            get => _unlocked;
            private set
            {
                var type =
                    !_unlocked && value ? Some<DoorEventType>.Of(DoorEventType.Unlocked)
                    : _unlocked && !value ? Some<DoorEventType>.Of(DoorEventType.Locked)
                    : None<DoorEventType>.New();
                _unlocked = value;
                type.Match(some: OnDoorEvent);
            }
        }

        private bool _unlocked;

        protected void OnDoorEvent(DoorEventType type)
        {
            Logger.Log($"{Module} has been {type}", LogType.PuzzleFlow);
            
            DoorEvent?.Invoke(Module, type);
        }

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

        public void Connect() => OnDoorEvent(DoorEventType.Connected);
            
        #region Debug Buttons

        [Button(enabledMode: EButtonEnableMode.Playmode)]
        internal void Unlock() => Unlocked = true;

        [Button(enabledMode: EButtonEnableMode.Playmode)]
        internal void Lock() => Unlocked = false;

        [Button(enabledMode: EButtonEnableMode.Playmode)]
        protected internal void ExitFrom() => OnDoorEvent(DoorEventType.ExitedFrom);
        
        #endregion

        public static DoorState FromState(ModuleState state)
        {
            if (state is DoorState doorState)
            {
                return doorState;
            }
            
            throw new WrongTypeException(typeof(DoorState), state.GetType(), typeof(ModuleState));
        }
    }
}