using Intrepid.Diagnostics;
using Intrepid.GameManagers;
using Intrepid.Gameplay.Entities.Player.ActionBelt.Events;
using Intrepid.Gameplay.Entities.Player.Events;
using Intrepid.Gameplay.Journey.Runtime.Messages;
using Intrepid.Integration.Input;
using UnityEngine;

namespace Intrepid.Gameplay.Journey.Runtime.Gameplay
{
    public sealed class JourneyGameplayGate : MonoBehaviour
    {
        public JourneyGameplayGateState State { get; private set; } = JourneyGameplayGateState.Open;

        public bool IsLocked => State == JourneyGameplayGateState.Locked;

        public JourneyGameplayGateContext Lock(JourneyTravelRequest request)
        {
            if (IsLocked)
            {
                DLogger.LogWarn($"tried to lock gameplay, but it is already locked. Request: {request}", ctx: this);
                return JourneyGameplayGateContext.Invalid;
            }

            var previousInputSchema = InputService.Instance.CurrentInputSchema;

            InputService.Instance.ChangeInputSchema(InputSchema.None);
            
            Proxy.Instance.EventBus.Raise(new ActionBeltDisableEvent());
            
            Proxy.Instance.EventBus.Raise(new PlayerCommandRevokedEvent());
            
            State = JourneyGameplayGateState.Locked;

            DLogger.LogDebug($"locked gameplay. Previous input schema: {previousInputSchema}. Request: {request}", ctx: this);

            return new JourneyGameplayGateContext(previousInputSchema);
        }

        public void Unlock(JourneyGameplayGateContext context, JourneyTravelRequest request)
        {
            if (!IsLocked)
            {
                DLogger.LogWarn($"tried to unlock gameplay, but it is already open. Request: {request}", ctx: this);
                return;
            }

            if (context is not { IsValid: true })
            {
                DLogger.LogWarn($"received an invalid unlock context. Falling back to Sentinel input. Request: {request}", ctx: this);
                RestoreInput(InputSchema.Sentinel);
            }
            else
            {
                var schemaToRestore = context.PreviousInputSchema == InputSchema.None
                    ? InputSchema.Sentinel
                    : context.PreviousInputSchema;

                RestoreInput(schemaToRestore);
            }

            Proxy.Instance.EventBus.Raise(new ActionBeltEnableEvent());
            Proxy.Instance.EventBus.Raise(new PlayerCommandGrantedEvent());

            State = JourneyGameplayGateState.Open;

            DLogger.LogDebug($"unlocked gameplay. Request: {request}", ctx: this);
        }
        
        public void OpenForBoot()
        {
            InputService.Instance.ChangeInputSchema(InputSchema.Sentinel);

            Proxy.Instance.EventBus.Raise(new ActionBeltEnableEvent());
            Proxy.Instance.EventBus.Raise(new PlayerCommandGrantedEvent());

            State = JourneyGameplayGateState.Open;

            DLogger.LogDebug("opened gameplay for boot.", ctx: this);
        }

        private static void RestoreInput(InputSchema inputSchema)
        {
            InputService.Instance.ChangeInputSchema(inputSchema);
        }
    }
}