using System.Text.Json;
using MBN_STOCK_WEBVIEW.Infrastructure;
using MMoneyCoderSharp.Data;

namespace MBN_STOCK_WEBVIEW;

public sealed partial class MainWindow
{
    private LegacyComparisonPairImportService? _legacyComparisonImportService;
    private CancellationTokenSource? _legacyComparisonImportCancellation;

    private bool TryHandleLegacyComparisonImportRequest(
        string? requestType,
        JsonElement payload)
    {
        if (!string.Equals(
                requestType,
                "import-legacy-comparison-pairs",
                StringComparison.Ordinal))
        {
            return false;
        }

        var requestId = string.Empty;
        var hasSafeRequestId = payload.ValueKind == JsonValueKind.Object &&
            TryGetSafeToken(
                payload,
                "requestId",
                MaximumPlayoutRequestIdLength,
                out requestId);
        if (!hasSafeRequestId || !HasOnlyProperties(payload, "requestId"))
        {
            PostLegacyComparisonImportError(
                hasSafeRequestId ? requestId : string.Empty,
                "INVALID_REQUEST",
                "원본 비교쌍 가져오기 요청 형식이 올바르지 않습니다.");
            return true;
        }

        _ = HandleLegacyComparisonImportAsync(requestId);
        return true;
    }

    private async Task HandleLegacyComparisonImportAsync(string requestId)
    {
        var runtime = _databaseRuntime;
        if (runtime is null)
        {
            PostLegacyComparisonImportError(
                requestId,
                "DATABASE_UNAVAILABLE",
                "현재 종목 마스터 DB를 사용할 수 없어 원본 비교쌍을 검증할 수 없습니다.");
            return;
        }

        LegacyComparisonPairImportService service;
        try
        {
            service = _legacyComparisonImportService ??=
                new LegacyComparisonPairImportService(
                    runtime.Executor,
                    TrustedLegacyComparisonFileSource.CreateDefault());
        }
        catch (LegacyComparisonImportException exception)
        {
            PostLegacyComparisonImportFailure(requestId, exception.Failure);
            return;
        }

        var cancellation = CancellationTokenSource.CreateLinkedTokenSource(
            _lifetimeCancellation.Token);
        var previous = Interlocked.Exchange(
            ref _legacyComparisonImportCancellation,
            cancellation);
        CancelRequest(previous);

        var gateEntered = false;
        try
        {
            if (Volatile.Read(ref _playoutCommandInFlight) != 0)
            {
                PostLegacyComparisonImportErrorIfCurrent(
                    requestId,
                    cancellation,
                    "PLAYOUT_PRIORITY",
                    "송출 명령이 우선 처리 중이므로 가져오기를 실행하지 않았습니다.");
                return;
            }

            await _databaseActivityGate.WaitAsync(cancellation.Token);
            gateEntered = true;
            cancellation.Token.ThrowIfCancellationRequested();
            if (Volatile.Read(ref _playoutCommandInFlight) != 0)
            {
                PostLegacyComparisonImportErrorIfCurrent(
                    requestId,
                    cancellation,
                    "PLAYOUT_PRIORITY",
                    "송출 명령이 우선 처리 중이므로 가져오기를 실행하지 않았습니다.");
                return;
            }

            var result = await service.ImportAsync(cancellation.Token);
            cancellation.Token.ThrowIfCancellationRequested();
            if (!IsCurrentLegacyComparisonImport(cancellation))
            {
                return;
            }

            PostMessage("legacy-comparison-import-results", new
            {
                requestId,
                result.SourceSha256,
                result.SourceRowCount,
                result.RetrievedAt,
                pairs = result.Pairs.Select(pair => new
                {
                    pair.RowNumber,
                    first = ToLegacyComparisonImportWireTarget(pair.First),
                    second = ToLegacyComparisonImportWireTarget(pair.Second)
                })
            });
        }
        catch (OperationCanceledException) when (cancellation.IsCancellationRequested)
        {
            // A newer explicit import, navigation, playout priority, or shutdown
            // owns the DB slot. Never retry or publish a stale result.
            PostPlayoutPriorityLegacyComparisonImportCancellationIfCurrent(
                requestId,
                cancellation);
        }
        catch (DatabaseOperationException) when (cancellation.IsCancellationRequested)
        {
            // Providers may surface a native exception after cancellation.
            PostPlayoutPriorityLegacyComparisonImportCancellationIfCurrent(
                requestId,
                cancellation);
        }
        catch (Exception) when (cancellation.IsCancellationRequested)
        {
            // Preserve correlation loss and shutdown semantics.
            PostPlayoutPriorityLegacyComparisonImportCancellationIfCurrent(
                requestId,
                cancellation);
        }
        catch (LegacyComparisonImportException exception)
        {
            if (IsCurrentLegacyComparisonImport(cancellation))
            {
                PostLegacyComparisonImportFailure(requestId, exception.Failure);
            }
        }
        catch (DatabaseOperationException)
        {
            if (IsCurrentLegacyComparisonImport(cancellation))
            {
                PostLegacyComparisonImportError(
                    requestId,
                    "DATABASE_UNAVAILABLE",
                    "현재 종목 마스터 DB에서 원본 비교쌍을 검증하지 못했습니다.");
                _ = PostDatabaseStatusAsync(_lifetimeCancellation.Token);
            }
        }
        catch
        {
            if (IsCurrentLegacyComparisonImport(cancellation))
            {
                PostLegacyComparisonImportError(
                    requestId,
                    "IMPORT_FAILED",
                    "원본 비교쌍 가져오기를 안전하게 완료하지 못했습니다.");
            }
        }
        finally
        {
            if (gateEntered)
            {
                _databaseActivityGate.Release();
            }

            Interlocked.CompareExchange(
                ref _legacyComparisonImportCancellation,
                null,
                cancellation);
            cancellation.Dispose();
        }
    }

    private bool IsCurrentLegacyComparisonImport(CancellationTokenSource cancellation) =>
        !_lifetimeCancellation.IsCancellationRequested &&
        ReferenceEquals(
            Volatile.Read(ref _legacyComparisonImportCancellation),
            cancellation);

    private void PostLegacyComparisonImportErrorIfCurrent(
        string requestId,
        CancellationTokenSource cancellation,
        string code,
        string message)
    {
        if (IsCurrentLegacyComparisonImport(cancellation))
        {
            PostLegacyComparisonImportError(requestId, code, message);
        }
    }

    private void PostPlayoutPriorityLegacyComparisonImportCancellationIfCurrent(
        string requestId,
        CancellationTokenSource cancellation)
    {
        if (Volatile.Read(ref _playoutCommandInFlight) != 0 &&
            IsCurrentLegacyComparisonImport(cancellation))
        {
            PostLegacyComparisonImportError(
                requestId,
                "PLAYOUT_PRIORITY",
                "송출 명령을 우선 처리하기 위해 원본 비교쌍 가져오기를 취소했습니다. 자동 재시도하지 않습니다.");
        }
    }

    private void PostLegacyComparisonImportFailure(
        string requestId,
        LegacyComparisonImportFailure failure)
    {
        var (code, message) = failure switch
        {
            LegacyComparisonImportFailure.SourceUnavailable => (
                "SOURCE_UNAVAILABLE",
                "고정된 원본 위치의 종목비교.dat를 안전하게 읽을 수 없습니다."),
            LegacyComparisonImportFailure.SourceInvalid => (
                "SOURCE_INVALID",
                "원본 종목비교.dat가 정확한 CP949 9필드 형식과 일치하지 않습니다."),
            LegacyComparisonImportFailure.IdentityNotFound => (
                "IDENTITY_NOT_FOUND",
                "원본 비교쌍의 종목 또는 시장 식별자를 현재 DB에서 정확히 확인하지 못했습니다."),
            LegacyComparisonImportFailure.IdentityAmbiguous => (
                "IDENTITY_AMBIGUOUS",
                "원본 비교쌍의 종목 식별자가 현재 DB에서 둘 이상 확인되어 가져오기를 중단했습니다."),
            LegacyComparisonImportFailure.DatabaseDataInvalid => (
                "DATABASE_DATA_INVALID",
                "현재 종목 마스터 조회 결과가 엄격한 식별자 형식과 일치하지 않습니다."),
            _ => (
                "IMPORT_FAILED",
                "원본 비교쌍 가져오기를 안전하게 완료하지 못했습니다.")
        };
        PostLegacyComparisonImportError(requestId, code, message);
    }

    private void PostLegacyComparisonImportError(
        string requestId,
        string code,
        string message)
    {
        PostMessage("legacy-comparison-import-error", new
        {
            requestId,
            code,
            message
        });
    }

    private static object ToLegacyComparisonImportWireTarget(
        LegacyComparisonImportTarget target) => target.Kind switch
        {
            LegacyComparisonImportTargetKind.MarketTarget => new
            {
                kind = "market-target",
                target = target.MarketTarget
            },
            LegacyComparisonImportTargetKind.KrxStock => new
            {
                kind = "krx-stock",
                market = target.Market,
                stockName = target.StockName,
                stockCode = target.StockCode
            },
            LegacyComparisonImportTargetKind.NxtStock => new
            {
                kind = "nxt-stock",
                market = target.Market,
                stockName = target.StockName,
                stockCode = target.StockCode
            },
            LegacyComparisonImportTargetKind.WorldStock => new
            {
                kind = "world-stock",
                inputName = target.InputName,
                symbol = target.Symbol,
                nation = target.Nation
            },
            _ => throw new InvalidOperationException(
                "The comparison import target kind is invalid.")
        };

    private void InvalidateLegacyComparisonImportRequest()
    {
        CancelRequest(Interlocked.Exchange(
            ref _legacyComparisonImportCancellation,
            null));
    }

    private void ShutdownLegacyComparisonImportRuntime()
    {
        InvalidateLegacyComparisonImportRequest();
        _legacyComparisonImportService = null;
    }
}
