#nullable enable
using System;
using System.Threading;
using System.Threading.Tasks;
using IGP.Multiplayer.Models;
using IGP.UnitySDK;

namespace IGP.Multiplayer
{
    public partial class IGPMultiplayerRuntime
    {
        public async Task<bool> TryCreateRuntimeAsync(CancellationToken cancellationToken = default)
        {
            EnsureExtensionInitialized();

            await runtimeCreationGate.WaitAsync(cancellationToken);
            using var creationCancellation = CancellationTokenSource.CreateLinkedTokenSource(
                RuntimeCancellationToken,
                cancellationToken);
            CancellationToken creationToken = creationCancellation.Token;
            try
            {
                if (sdkInitialized && IsRealtimeReady)
                {
                    LogSdkDebug("runtime-create", "event=skipped descriptorSource=core-host reason=already-ready");
                    return true;
                }
                runtimeCreationInProgress = true;

                bool attached = IsHostedSessionAttached;
                if (!attached)
                {
                    attached = HasHostedLaunchContext(pendingLaunchOptions)
                        ? await TryBootstrapFromLaunchTicketAsync(null, creationToken)
                        : await TryAttachHostedRoomAsync(creationToken);
                }

                if (!attached)
                {
                    creationToken.ThrowIfCancellationRequested();
                    LogSdkDebug(
                        "runtime-create",
                        "event=skipped descriptorSource=core-host reason=host-descriptor-path-unavailable");
                    return false;
                }

                await ConnectDataPlaneAsync(creationToken);
                PublishConnectionRecoveredIfPending("explicit-reconnect");
                return sdkInitialized;
            }
            catch (OperationCanceledException)
            {
                DisposeRuntimeResources("host-runtime-create-canceled");
                if (cancellationToken.IsCancellationRequested) throw;
                return false;
            }
            catch (Exception exception)
            {
                DisposeRuntimeResources("host-runtime-create-failed");
                UpdateRealtimeConnectionState(IGPRealtimeConnectionState.WaitingForRoom, "host-runtime-create-failed");
                LogSdkWarning(
                    "runtime-create",
                    $"event=failed descriptorSource=core-host error={FormatNetworkLogValue(exception.Message)}");
                return false;
            }
            finally
            {
                if (KcpClient != null) KcpClient.DowngradeErrorsToWarnings = false;
                runtimeCreationInProgress = false;
                runtimeCreationGate.Release();
            }
        }

        public async Task<bool> TryCreateRuntimeAsync(
            IGPMultiplayerDescriptor descriptor,
            CancellationToken cancellationToken = default)
        {
            if (descriptor == null) throw new ArgumentNullException(nameof(descriptor));
            EnsureExtensionInitialized();

            await runtimeCreationGate.WaitAsync(cancellationToken);
            using var creationCancellation = CancellationTokenSource.CreateLinkedTokenSource(
                RuntimeCancellationToken,
                cancellationToken);
            CancellationToken creationToken = creationCancellation.Token;
            try
            {
                if (sdkInitialized && IsRealtimeReady &&
                    string.Equals(currentRoomId, descriptor.RoomId, StringComparison.Ordinal) &&
                    string.Equals(playerId, descriptor.PlayerId, StringComparison.Ordinal))
                {
                    return true;
                }
                runtimeCreationInProgress = true;

                if (sdkInitialized)
                {
                    DisposeRuntimeResources("explicit-descriptor-runtime-recreate");
                }

                var hostedDescriptor = descriptor.ToHostedDescriptor();
                currentRoomId = descriptor.RoomId;
                playerId = descriptor.PlayerId;
                roomData = new Room { id = descriptor.RoomId, status = "playing" };
                currentPlayerData = new Player { id = descriptor.PlayerId, roomId = descriptor.RoomId };
                realtimeRoomGeneration++;
                EnsureRuntimeResources();

                int attemptId = ++hostedDataPlaneAttemptId;
                IGPRealtimeTransport selectedTransport = SelectReliableTransport(
                    hostedDescriptor,
                    ReliableTransportPreference,
                    false);
                UpdateRealtimeConnectionState(IGPRealtimeConnectionState.Connecting);
                await AttachHostedDataPlaneAsync(
                    descriptor.RoomId,
                    descriptor.PlayerId,
                    new IGPHostSessionDataPlaneResponse
                    {
                        Success = true,
                        DataPlane = hostedDescriptor,
                    },
                    selectedTransport,
                    attemptId,
                    realtimeRoomGeneration);
                await WaitForHostedDataPlaneReadyAsync(
                    HostedDataPlaneReadyTimeout,
                    attemptId,
                    realtimeRoomGeneration,
                    creationToken);
                PublishConnectionRecoveredIfPending("explicit-reconnect");
                return true;
            }
            catch (OperationCanceledException)
            {
                DisposeRuntimeResources("explicit-descriptor-runtime-create-canceled");
                if (cancellationToken.IsCancellationRequested) throw;
                return false;
            }
            catch (Exception exception)
            {
                DisposeRuntimeResources("explicit-descriptor-runtime-create-failed");
                UpdateRealtimeConnectionState(IGPRealtimeConnectionState.Idle, "explicit-descriptor-runtime-create-failed");
                LogSdkError(
                    "runtime-create",
                    $"event=failed descriptorSource=explicit error={FormatNetworkLogValue(exception.Message)}");
                return false;
            }
            finally
            {
                if (KcpClient != null) KcpClient.DowngradeErrorsToWarnings = false;
                runtimeCreationInProgress = false;
                runtimeCreationGate.Release();
            }
        }

        /// <summary>
        /// 通过 Core Host provider 显式重建 Game data plane。SDK 不会自动调用此方法。
        /// </summary>
        public Task<bool> ReconnectDataPlaneAsync(CancellationToken cancellationToken = default)
        {
            return TryCreateRuntimeAsync(cancellationToken);
        }

        /// <summary>
        /// 使用调用方取得的新 descriptor 显式重建 Game data plane。
        /// </summary>
        public Task<bool> ReconnectDataPlaneAsync(
            IGPMultiplayerDescriptor descriptor,
            CancellationToken cancellationToken = default)
        {
            return TryCreateRuntimeAsync(descriptor, cancellationToken);
        }
    }
}
