// Copyright (c) 2026 GeWuYou
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// 
//     http://www.apache.org/licenses/LICENSE-2.0
// 
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

using System;
using GFramework.Core.Abstractions.Logging;
using GFramework.Core.Extensions;
using GFramework.Core.Logging;
using GFramework.Game.Abstractions.Enums;
using GFramework.Game.Abstractions.Scene;
using GFramework.Game.Routing;

namespace GFramework.Game.Scene;

/// <summary>
/// 场景路由基类，提供场景切换和卸载的基础功能。
/// 实现了 <see cref="ISceneRouter"/> 接口，用于管理场景的加载、替换和卸载操作。
/// </summary>
public abstract class SceneRouterBase
    : RouterBase<ISceneBehavior, ISceneEnterParam>, ISceneRouter
{
    private static readonly ILogger Log =
        LoggerFactoryResolver.Provider.CreateLogger(nameof(SceneRouterBase));

    private readonly SceneTransitionPipeline _pipeline = new();

    private readonly SemaphoreSlim _transitionLock = new(1, 1);
    private ISceneFactory _factory = null!;

    /// <summary>
    ///  场景根节点
    /// </summary>
    protected ISceneRoot? Root;

    /// <summary>
    /// 获取当前场景行为对象。
    /// </summary>
    public new ISceneBehavior? Current => Stack.Count > 0 ? Stack.Peek() : null;

    /// <summary>
    /// 获取当前场景的键名。
    /// </summary>
    public new string? CurrentKey => Current?.Key;

    /// <summary>
    /// 获取场景栈的只读视图，按压入顺序排列（从栈底到栈顶）。
    /// </summary>
    IEnumerable<ISceneBehavior> ISceneRouter.Stack => base.Stack.Reverse();

    /// <summary>
    /// 获取是否正在进行场景转换。
    /// </summary>
    public bool IsTransitioning { get; private set; }

    /// <summary>
    /// 绑定场景根节点。
    /// </summary>
    /// <param name="root">场景根节点实例。</param>
    public void BindRoot(ISceneRoot root)
    {
        Root = root;
        Log.Debug("Bind Scene Root: {0}", root.GetType().Name);
    }

    #region Replace

    /// <summary>
    /// 替换当前场景为指定场景。
    /// </summary>
    /// <param name="sceneKey">目标场景键名。</param>
    /// <param name="param">场景进入参数。</param>
    /// <returns>异步任务。</returns>
    public async ValueTask ReplaceAsync(
        string sceneKey,
        ISceneEnterParam? param = null)
    {
        await _transitionLock.WaitAsync().ConfigureAwait(true);
        try
        {
            IsTransitioning = true;

            var @event = CreateEvent(sceneKey, SceneTransitionType.Replace, param);

            await _pipeline.ExecuteAroundAsync(
                @event,
                () => ExecuteReplaceCoreAsync(@event, sceneKey, param)).ConfigureAwait(true);
        }
        finally
        {
            IsTransitioning = false;
            _transitionLock.Release();
        }
    }

    #endregion

    #region Query

    /// <summary>
    /// 检查指定场景是否在栈中。
    /// </summary>
    /// <param name="sceneKey">场景键名。</param>
    /// <returns>如果场景在栈中返回true，否则返回false。</returns>
    public new bool Contains(string sceneKey)
    {
        return Stack.Any(s => string.Equals(s.Key, sceneKey, StringComparison.Ordinal));
    }

    #endregion

    /// <summary>
    /// 注册场景过渡处理器。
    /// </summary>
    /// <param name="handler">处理器实例。</param>
    /// <param name="options">执行选项。</param>
    public void RegisterHandler(ISceneTransitionHandler handler, SceneTransitionHandlerOptions? options = null)
    {
        _pipeline.RegisterHandler(handler, options);
    }

    /// <summary>
    /// 注销场景过渡处理器。
    /// </summary>
    /// <param name="handler">处理器实例。</param>
    public void UnregisterHandler(ISceneTransitionHandler handler)
    {
        _pipeline.UnregisterHandler(handler);
    }

    /// <summary>
    /// 注册环绕场景过渡处理器。
    /// </summary>
    /// <param name="handler">环绕处理器实例。</param>
    /// <param name="options">处理器选项配置。</param>
    public void RegisterAroundHandler(
        ISceneAroundTransitionHandler handler,
        SceneTransitionHandlerOptions? options = null)
    {
        _pipeline.RegisterAroundHandler(handler, options);
    }

    /// <summary>
    /// 注销环绕场景过渡处理器。
    /// </summary>
    /// <param name="handler">环绕处理器实例。</param>
    public void UnregisterAroundHandler(
        ISceneAroundTransitionHandler handler)
    {
        _pipeline.UnregisterAroundHandler(handler);
    }

    /// <summary>
    /// 注册场景过渡处理器的抽象方法，由子类实现。
    /// </summary>
    protected override abstract void RegisterHandlers();

    /// <summary>
    /// 系统初始化方法，获取场景工厂并注册处理器。
    /// </summary>
    protected override void OnInit()
    {
        _factory = this.GetUtility<ISceneFactory>()!;
        Log.Debug("SceneRouterBase initialized. Factory={0}", _factory.GetType().Name);
        RegisterHandlers();
    }

    #region Push

    /// <summary>
    /// 将指定场景推入栈顶。
    /// </summary>
    /// <param name="sceneKey">目标场景键名。</param>
    /// <param name="param">场景进入参数。</param>
    /// <returns>异步任务。</returns>
    public async ValueTask PushAsync(
        string sceneKey,
        ISceneEnterParam? param = null)
    {
        await _transitionLock.WaitAsync().ConfigureAwait(true);
        try
        {
            IsTransitioning = true;

            var @event = CreateEvent(sceneKey, SceneTransitionType.Push, param);

            await _pipeline.ExecuteAroundAsync(
                @event,
                () => ExecutePushCoreAsync(@event, sceneKey, param)).ConfigureAwait(true);
        }
        finally
        {
            IsTransitioning = false;
            _transitionLock.Release();
        }
    }

    /// <summary>
    /// 内部推送场景实现方法。
    /// 执行守卫检查、场景创建、添加到场景树、加载资源、暂停当前场景、压入栈等操作。
    /// </summary>
    /// <param name="sceneKey">场景键名。</param>
    /// <param name="param">场景进入参数。</param>
    /// <returns>异步任务。</returns>
    private async ValueTask PushInternalAsync(
        string sceneKey,
        ISceneEnterParam? param)
    {
        if (Contains(sceneKey))
        {
            Log.Warn("Scene already in stack: {0}", sceneKey);
            return;
        }

        // 守卫检查
        if (!await ExecuteEnterGuardsAsync(sceneKey, param).ConfigureAwait(true))
        {
            Log.Warn("Push blocked by guard: {0}", sceneKey);
            return;
        }

        // 通过 Factory 创建场景实例
        var scene = _factory.Create(sceneKey);

        // 添加到场景树
        Root!.AddScene(scene);

        // 加载资源
        await scene.OnLoadAsync(param).ConfigureAwait(true);

        // 暂停当前场景
        if (Stack.Count > 0)
        {
            var current = Stack.Peek();
            await current.OnPauseAsync().ConfigureAwait(true);
        }

        // 压入栈
        Stack.Push(scene);

        // 进入场景
        await scene.OnEnterAsync().ConfigureAwait(true);

        Log.Debug("Push Scene: {0}, stackCount={1}",
            sceneKey, Stack.Count);
    }

    #endregion

    #region Pop

    /// <summary>
    /// 弹出栈顶场景。
    /// </summary>
    /// <returns>异步任务。</returns>
    public async ValueTask PopAsync()
    {
        await _transitionLock.WaitAsync().ConfigureAwait(true);
        try
        {
            IsTransitioning = true;

            var @event = CreateEvent(null, SceneTransitionType.Pop);

            await _pipeline.ExecuteAroundAsync(
                @event,
                () => ExecutePopCoreAsync(@event)).ConfigureAwait(true);
        }
        finally
        {
            IsTransitioning = false;
            _transitionLock.Release();
        }
    }

    /// <summary>
    /// 内部弹出场景实现方法。
    /// 执行守卫检查、退出场景、卸载资源、从场景树移除、恢复下一个场景等操作。
    /// </summary>
    /// <returns>异步任务。</returns>
    private async ValueTask PopInternalAsync()
    {
        if (Stack.Count == 0)
            return;

        var top = Stack.Peek();

        // 守卫检查
        if (!await ExecuteLeaveGuardsAsync(top.Key).ConfigureAwait(true))
        {
            Log.Warn("Pop blocked by guard: {0}", top.Key);
            return;
        }

        Stack.Pop();

        // 退出场景
        await top.OnExitAsync().ConfigureAwait(true);

        // 卸载资源
        await top.OnUnloadAsync().ConfigureAwait(true);

        // 从场景树移除
        Root!.RemoveScene(top);

        // 恢复下一个场景
        if (Stack.Count > 0)
        {
            var next = Stack.Peek();
            await next.OnResumeAsync().ConfigureAwait(true);
        }

        Log.Debug("Pop Scene, stackCount={0}", Stack.Count);
    }

    #endregion

    #region Clear

    /// <summary>
    /// 清空所有场景栈。
    /// </summary>
    /// <returns>异步任务。</returns>
    public async ValueTask ClearAsync()
    {
        await _transitionLock.WaitAsync().ConfigureAwait(true);
        try
        {
            IsTransitioning = true;

            var @event = CreateEvent(null, SceneTransitionType.Clear);

            await _pipeline.ExecuteAroundAsync(
                @event,
                () => ExecuteClearCoreAsync(@event)).ConfigureAwait(true);
        }
        finally
        {
            IsTransitioning = false;
            _transitionLock.Release();
        }
    }

    /// <summary>
    /// 内部清空场景栈实现方法。
    /// 循环调用弹出操作直到栈为空。
    /// </summary>
    /// <returns>异步任务。</returns>
    private async ValueTask ClearInternalAsync()
    {
        while (Stack.Count > 0)
        {
            await PopInternalAsync().ConfigureAwait(true);
        }
    }

    #endregion

    #region Helper Methods

    // Scene 生命周期回调和 pipeline handlers 可能依赖引擎线程，因此这些核心切换顺序统一显式保留上下文。

    /// <summary>
    /// 执行 Replace 的核心切换顺序。
    /// </summary>
    /// <param name="event">场景转换事件。</param>
    /// <param name="sceneKey">目标场景键名。</param>
    /// <param name="param">场景进入参数。</param>
    /// <returns>异步任务。</returns>
    private async Task ExecuteReplaceCoreAsync(
        SceneTransitionEvent @event,
        string sceneKey,
        ISceneEnterParam? param)
    {
        await BeforeChangeAsync(@event).ConfigureAwait(true);
        await ClearInternalAsync().ConfigureAwait(true);
        await PushInternalAsync(sceneKey, param).ConfigureAwait(true);
        await AfterChangeAsync(@event).ConfigureAwait(true);
    }

    /// <summary>
    /// 执行 Push 的核心切换顺序。
    /// </summary>
    /// <param name="event">场景转换事件。</param>
    /// <param name="sceneKey">目标场景键名。</param>
    /// <param name="param">场景进入参数。</param>
    /// <returns>异步任务。</returns>
    private async Task ExecutePushCoreAsync(
        SceneTransitionEvent @event,
        string sceneKey,
        ISceneEnterParam? param)
    {
        await BeforeChangeAsync(@event).ConfigureAwait(true);
        await PushInternalAsync(sceneKey, param).ConfigureAwait(true);
        await AfterChangeAsync(@event).ConfigureAwait(true);
    }

    /// <summary>
    /// 执行 Pop 的核心切换顺序。
    /// </summary>
    /// <param name="event">场景转换事件。</param>
    /// <returns>异步任务。</returns>
    private async Task ExecutePopCoreAsync(SceneTransitionEvent @event)
    {
        await BeforeChangeAsync(@event).ConfigureAwait(true);
        await PopInternalAsync().ConfigureAwait(true);
        await AfterChangeAsync(@event).ConfigureAwait(true);
    }

    /// <summary>
    /// 执行 Clear 的核心切换顺序。
    /// </summary>
    /// <param name="event">场景转换事件。</param>
    /// <returns>异步任务。</returns>
    private async Task ExecuteClearCoreAsync(SceneTransitionEvent @event)
    {
        await BeforeChangeAsync(@event).ConfigureAwait(true);
        await ClearInternalAsync().ConfigureAwait(true);
        await AfterChangeAsync(@event).ConfigureAwait(true);
    }

    /// <summary>
    /// 创建场景转换事件对象。
    /// </summary>
    /// <param name="toSceneKey">目标场景键名。</param>
    /// <param name="type">转换类型。</param>
    /// <param name="param">进入参数。</param>
    /// <returns>场景转换事件实例。</returns>
    private SceneTransitionEvent CreateEvent(
        string? toSceneKey,
        SceneTransitionType type,
        ISceneEnterParam? param = null)
    {
        return new SceneTransitionEvent
        {
            FromSceneKey = CurrentKey,
            ToSceneKey = toSceneKey,
            TransitionType = type,
            EnterParam = param
        };
    }

    /// <summary>
    /// 执行转换前阶段的处理逻辑。
    /// </summary>
    /// <param name="event">场景转换事件。</param>
    /// <returns>异步任务。</returns>
    private async Task BeforeChangeAsync(SceneTransitionEvent @event)
    {
        Log.Debug("BeforeChange phases started: {0}", @event.TransitionType);
        await _pipeline.ExecuteAsync(@event, SceneTransitionPhases.BeforeChange).ConfigureAwait(true);
        Log.Debug("BeforeChange phases completed: {0}", @event.TransitionType);
    }

    /// <summary>
    /// 执行转换后阶段的处理逻辑。
    /// </summary>
    /// <param name="event">场景转换事件。</param>
    private async Task AfterChangeAsync(SceneTransitionEvent @event)
    {
        Log.Debug("AfterChange phases started: {0}", @event.TransitionType);
        await _pipeline.ExecuteAsync(@event, SceneTransitionPhases.AfterChange).ConfigureAwait(true);
        Log.Debug("AfterChange phases completed: {0}", @event.TransitionType);
    }

    #endregion
}
