using System;
using Intrepid.Core.Camera;
using Intrepid.Diagnostics;
using Intrepid.GameManagers;
using Intrepid.Gameplay.Journey.Messages.Events;
using Intrepid.Gameplay.Journey.Runtime.Gameplay;
using Intrepid.Gameplay.Journey.Runtime.Loading;
using Intrepid.Gameplay.Journey.Runtime.Messages;
using Intrepid.Theatre.BackStage;
using Intrepid.Theatre.Curtain;
using Intrepid.Utilities;
using UnityEngine;

namespace Intrepid.Gameplay.Journey.Runtime.Transition
{
    public sealed class JourneyTransitionDirector : MonoBehaviour, IBackStageProcessor
    {
        private const string Id = "JourneyTransitionDirector";

        [GroupConfigurations, SerializeField] private CurtainTypeTag curtainType;
        [GroupInjections, SerializeField] private BackStageService backStageService;
        [GroupInjections, SerializeField] private JourneyGameplayGate gameplayGate;
        [GroupInjections, SerializeField] private JourneyStageLoader stageLoader;
        [GroupInjections, SerializeField] private JourneyLandingService landingService;

        private JourneyTravelRequest CurrentRequest { get; set; }
        private JourneyGameplayGateContext GameplayGateContext { get; set; }

        private Action<JourneyTravelRequest> OnFinished { get; set; }
        private Action<JourneyTravelRequest> OnFailed { get; set; }
        
        public bool IsRunning => CurrentRequest is not null;
        public bool IsProcessFinished =>
            CurrentRequest is not null &&
            stageLoader.IsLoadingFinished &&
            !CameraSystem.Instance.IsCameraBlending;


        public bool StartTravel(
            JourneyTravelRequest request,
            Action<JourneyTravelRequest> onFinished,
            Action<JourneyTravelRequest> onFailed = null)
        {
            if (IsRunning)
            {
                DLogger.LogWarn($"is already running. Request: {request}", ctx: this);
                onFailed?.Invoke(request);
                return false;
            }

            CurrentRequest = request;
            OnFinished = onFinished;
            OnFailed = onFailed;

            var kicked = backStageService.KickProcess(curtainType, Id, this);

            if (!kicked)
            {
                DLogger.LogWarn($"could not kick backstage process. Request: {request}", ctx: this);
                Clear();
                onFailed?.Invoke(request);
                return false;
            }

            DLogger.LogDebug($"started travel: {request}", ctx: this);
            return true;
        }

        public void BeforeClosing()
        {
            DLogger.LogDebug($"BeforeClosing: {CurrentRequest}", ctx: this);
            GameplayGateContext = gameplayGate.Lock(CurrentRequest);
            Proxy.Instance.EventBus.Raise(new ChangeStageTouchedEvent());
        }

        public void AfterClosed()
        {
            DLogger.LogDebug($"AfterClosed: {CurrentRequest}", ctx: this);
            Proxy.Instance.EventBus.Raise(new ChangeStageStartEvent());
            stageLoader.ChangeTo(CurrentRequest.DestinyStageTag);
        }

        public void BeforeOpening()
        {
            DLogger.LogDebug($"BeforeOpening: {CurrentRequest}", ctx: this);
            landingService.LandAtEndpoint(CurrentRequest.DestinyStageTag, CurrentRequest.DestinyEndpointId);
        }

        public void AfterOpened()
        {
            DLogger.LogDebug($"AfterOpened: {CurrentRequest}", ctx: this);
            Proxy.Instance.EventBus.Raise(new ChangeStageEndEvent());
            gameplayGate.Unlock(GameplayGateContext, CurrentRequest);
            var finishedRequest = CurrentRequest;
            OnFinished?.Invoke(finishedRequest);
            Clear();
        }

        private void Clear()
        {
            CurrentRequest = null;
            GameplayGateContext = null;
            OnFinished = null;
            OnFailed = null;
        }
    }
}