using System.Text.Json;
using MBN_STOCK_WEBVIEW.Core.Playout;
using MBN_STOCK_WEBVIEW.Core.Playout.Scenes;

namespace MBN_STOCK_WEBVIEW.Core.Tests;

public sealed class LegacyWebWorkflowIntegrationTests
{
    [Fact]
    public async Task WebPrepareThenNext_UsesNativeDtoBuilderAndSameScenePageDecision()
    {
        var engine = new RecordingEngine();
        var rows = Enumerable.Range(1, 6)
            .Select(index => new PagedQuoteRowData(
                "종목" + index,
                index * 100,
                index,
                index / 10d,
                ScenePriceDirection.Up))
            .ToArray();
        var dataSource = new LegacySceneDataSourceRouter(
        [
            new LegacySceneDataSourceRoute(["5074"], (entry, page, token) =>
            {
                token.ThrowIfCancellationRequested();
                var data = new PagedQuoteSceneData(
                    PagedQuoteBranch.Domestic,
                    PagedQuoteHeaderStyle.StandardWon,
                    PagedQuoteTitleStyle.Exact,
                    NxtMarketSession.NotApplicable,
                    "상승률",
                    rows,
                    page + 1);
                return Task.FromResult(new LegacySceneDataPage("s5074", data, rows.Length));
            })
        ]);
        var workflow = new LegacyPlayoutWorkflow(
            engine,
            new RegisteredLegacySceneCueProvider(dataSource));

        using var prepareJson = JsonDocument.Parse("""
            {
              "requestId":"REQ_PREPARE",
              "command":"prepare",
              "playlist":[{
                "id":"entry-1",
                "code":"5074",
                "enabled":true,
                "selection":{
                  "groupCode":"KOSPI_STOCK",
                  "subject":"테스트",
                  "graphicType":"",
                  "subtype":"",
                  "dataCode":"000001"
                }
              }],
              "selectedIndex":0
            }
            """);
        var prepare = PlayoutBridgeProtocol.ParseWorkflowCommand(prepareJson.RootElement);
        Assert.True(prepare.IsValid);

        Assert.True((await workflow.PrepareAsync(
            prepare.Request.Playlist!,
            prepare.Request.SelectedIndexZeroBased)).IsSuccess);
        Assert.Equal("entry-1", workflow.State.CurrentEntryId);
        Assert.Equal("s5074", workflow.State.BuilderKey);
        Assert.Equal(5, workflow.State.CurrentPageItemCount);
        Assert.NotEmpty(workflow.State.PreviewFields);
        Assert.True((await workflow.TakeInAsync()).IsSuccess);

        using var nextJson = JsonDocument.Parse(
            "{\"requestId\":\"REQ_NEXT\",\"command\":\"next\"}");
        var next = PlayoutBridgeProtocol.ParseWorkflowCommand(nextJson.RootElement);
        Assert.True(next.IsValid);
        Assert.Null(next.Request.Playlist);

        Assert.True((await workflow.NextAsync()).IsSuccess);

        Assert.Equal(1, workflow.State.PageIndexZeroBased);
        Assert.Equal(2, workflow.State.PageCount);
        Assert.True(workflow.State.IsLastPage);
        Assert.Equal(1, workflow.State.CurrentPageItemCount);
        Assert.Contains(workflow.State.PreviewFields, field =>
            field is { Kind: "value", Name: "title1", Value: "종목6" });
        Assert.Equal(LegacyWorkflowNextKind.EndOfPlaylist, workflow.State.NextKind);
        Assert.Equal(
            ["prepare:5074", "prepare:5074", "take-in", "load-next:5074"],
            engine.Calls);
        Assert.NotNull(engine.LastCue?.Mutations);
        Assert.Equal(new PlayoutUseBackground(false), engine.LastCue!.Mutations![0]);
        Assert.Contains(engine.LastCue.Mutations, mutation =>
            mutation is PlayoutSetValue { ObjectName: "title1", Value: "종목6" });
    }

    private sealed class RecordingEngine : IPlayoutEngine
    {
        public List<string> Calls { get; } = [];

        public PlayoutCue? LastCue { get; private set; }

        public PlayoutStatus Status { get; } = new(
            PlayoutMode.DryRun,
            PlayoutConnectionState.DryRunReady,
            true,
            false,
            true,
            false,
            null,
            null,
            "test",
            DateTimeOffset.UtcNow,
            0);

        public event EventHandler<PlayoutStatusChangedEventArgs>? StatusChanged
        {
            add { }
            remove { }
        }

        public Task<PlayoutResult> ConnectAsync(CancellationToken cancellationToken = default) =>
            Success(PlayoutOperation.Connect);

        public Task<PlayoutResult> DisconnectAsync(CancellationToken cancellationToken = default) =>
            Success(PlayoutOperation.Disconnect);

        public Task<PlayoutResult> PrepareAsync(
            PlayoutCue cue,
            CancellationToken cancellationToken = default)
        {
            LastCue = cue;
            Calls.Add("prepare:" + cue.SceneName);
            return Success(PlayoutOperation.Prepare);
        }

        public Task<PlayoutResult> TakeInAsync(CancellationToken cancellationToken = default)
        {
            Calls.Add("take-in");
            return Success(PlayoutOperation.TakeIn);
        }

        public Task<PlayoutResult> NextAsync(
            PlayoutCue cue,
            CancellationToken cancellationToken = default)
        {
            LastCue = cue;
            Calls.Add("load-next:" + cue.SceneName);
            return Success(PlayoutOperation.Next);
        }

        public Task<PlayoutResult> UpdateOnAirAsync(
            PlayoutCue cue,
            CancellationToken cancellationToken = default)
        {
            LastCue = cue;
            Calls.Add("page-next:" + cue.SceneName);
            return Success(PlayoutOperation.UpdateOnAir);
        }

        public Task<PlayoutResult> TakeOutAsync(
            PlayoutTakeOutScope scope,
            CancellationToken cancellationToken = default) =>
            Success(PlayoutOperation.TakeOut);

        public ValueTask QuarantineAsync() => ValueTask.CompletedTask;

        public ValueTask DisposeAsync() => ValueTask.CompletedTask;

        private static Task<PlayoutResult> Success(PlayoutOperation operation) =>
            Task.FromResult(new PlayoutResult(
                operation,
                PlayoutResultCode.Success,
                true,
                "ok",
                DateTimeOffset.UtcNow));
    }
}
