using System;
using System.Collections;
using Intrepid.Core.Camera;
using Intrepid.Diagnostics;
using Intrepid.GameManagers;
using Intrepid.Gameplay.Data;
using Intrepid.Gameplay.Journey.Runtime.Gameplay;
using Intrepid.Gameplay.Journey.Runtime.Loading;
using Intrepid.Theatre.Curtain;
using Intrepid.Utilities;
using UnityEngine;

namespace Intrepid.Gameplay.Journey.Runtime
{
    public class JourneyBootDirector : MonoBehaviour
    {
#if UNITY_EDITOR
        [GroupDebug, SerializeField] private StageTag stageToTest;
        [GroupDebug, SerializeField] private Transform landingStart;
#endif

        [GroupInjections, SerializeField] private JourneyStageLoader stageLoader;
        [GroupInjections, SerializeField] private JourneyGameplayGate gameplayGate;
        [GroupInjections, SerializeField] private JourneyLandingService landingService;
        
        public void StartBoot(
            JourneyRuntimeBootMode bootMode,
            Action<StageTag> onFinished,
            Action onFailed)
        {
#if UNITY_EDITOR
            if (JourneyRuntimeBootMode.UseOpenedTestStage == bootMode)
            {
                StartCoroutine(BootOpenedStageForTesting(stageToTest, landingStart, onFinished, onFailed));
                return;
            }

            if (JourneyRuntimeBootMode.LoadTestStage == bootMode)
            {
                if (landingStart.IsNull())
                {
                    DLogger.LogWarn("cannot boot test stage because landing start is null.", ctx: this);
                    onFailed?.Invoke();
                    return;
                }

                StartCoroutine(BootStage(
                    stageToTest,
                    landingStart.position,
                    Vector2.down,
                    true,
                    onFinished,
                    onFailed));

                return;
            }
#endif

            if (SentinelDataStorage.Instance.HasLatestSaveStationData)
            {
                var request = SentinelDataStorage.Instance.LoadJourneyRequest;

                StartCoroutine(BootStage(
                    request.StageTag,
                    request.DeploymentPoint,
                    Vector2.down,
                    false,
                    onFinished,
                    onFailed));

                return;
            }

            DLogger.LogWarn("no boot request.", ctx: this);
            onFailed?.Invoke();
        }
        
        private IEnumerator BootStage(StageTag stageTag,
            Vector3 deploymentPoint,
            Vector2 deploymentDirection,
            bool isFakeBoot,
            Action<StageTag> onFinished = null,
            Action onFailed = null)
        {
            if (stageTag.IsNull())
            {
                DLogger.LogWarn("cannot boot null stage.", ctx: this);
                onFailed?.Invoke();
                yield break;
            }

            DLogger.LogDebug($"booting stage: {stageTag.StageId}", ctx: this);

            stageLoader.Init(stageTag);

            yield return new WaitUntil(() => stageLoader.IsLoadingFinished);

            landingService.DeployAt(deploymentPoint, deploymentDirection);

            yield return null;

            yield return new WaitUntil(() => !CameraSystem.Instance.IsCameraBlending);

            if (isFakeBoot)
            {
                var openCurtainProgress = Proxy.Instance.CurtainStateMachine.RaiseCommand(CurtainCommandType.Open);
                if (openCurtainProgress != null)
                {
                    yield return new WaitUntil(openCurtainProgress.IsFinished);
                }
            }
            
            gameplayGate.OpenForBoot();
            onFinished?.Invoke(stageTag);

            DLogger.LogDebug($"booted stage: {stageTag.StageId}", ctx: this);
        }
        
#if UNITY_EDITOR
        private IEnumerator BootOpenedStageForTesting(
            StageTag stageTag,
            Transform landing,
            Action<StageTag> onFinished = null,
            Action onFailed = null)
        {
            if (stageTag.IsNull())
            {
                DLogger.LogWarn("cannot boot opened test stage because stage tag is null.", ctx: this);
                onFailed?.Invoke();
                yield break;
            }

            if (landing.IsNull())
            {
                DLogger.LogWarn("cannot boot opened test stage because landing is null.", ctx: this);
                onFailed?.Invoke();
                yield break;
            }

            DLogger.LogDebug($"adopting opened stage: {stageTag.StageId}", ctx: this);

            if (!stageLoader.AdoptLoadedStage(stageTag))
            {
                DLogger.LogWarn($"could not adopt opened stage: {stageTag.StageId}", ctx: this);
                onFailed?.Invoke();
                yield break;
            }

            landingService.DeployAt(landing.position, Vector2.down);

            yield return null;

            yield return new WaitUntil(() => !CameraSystem.Instance.IsCameraBlending);

            var openCurtainProgress = Proxy.Instance.CurtainStateMachine.RaiseCommand(CurtainCommandType.Open);

            if (openCurtainProgress != null)
            {
                yield return new WaitUntil(openCurtainProgress.IsFinished);
            }

            gameplayGate.OpenForBoot();
            onFinished?.Invoke(stageTag);

            DLogger.LogDebug($"booted opened test stage: {stageTag.StageId}", ctx: this);
        }
#endif
    }
}