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

namespace MBN_STOCK_WEBVIEW.LegacyBridge.Tests;

public sealed class LegacyPlayoutBridgeTests
{
    private const string GateACapability =
        "0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF";

    [Fact]
    public void GateAPrepare_ParsesOnlyExactUppercaseCapabilityShape()
    {
        Assert.True(LegacyBridgeProtocol.TryParseIntent(
            "{\"type\":\"gate-a-prepare\",\"payload\":{\"capability\":\"" +
                GateACapability + "\"}}",
            out var intent,
            out var error), error);

        Assert.Equal(
            GateACapability,
            Assert.IsType<LegacyGateAPrepareIntent>(intent).Capability);
    }

    [Theory]
    [InlineData("")]
    [InlineData("0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef")]
    [InlineData("0123456789ABCDEF")]
    [InlineData("G123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF")]
    public void GateAPrepare_RejectsInvalidCapabilities(string capability)
    {
        Assert.False(LegacyBridgeProtocol.TryParseIntent(
            "{\"type\":\"gate-a-prepare\",\"payload\":{\"capability\":\"" +
                capability + "\"}}",
            out _,
            out _));
    }

    [Fact]
    public void GateAPrepare_RejectsExtraProperties()
    {
        Assert.False(LegacyBridgeProtocol.TryParseIntent(
            "{\"type\":\"gate-a-prepare\",\"payload\":{\"capability\":\"" +
                GateACapability + "\",\"retry\":false}}",
            out _,
            out _));
    }

    [Theory]
    [InlineData("prepare-playout", LegacyOperatorPlayoutCommand.Prepare)]
    [InlineData("take-in", LegacyOperatorPlayoutCommand.TakeIn)]
    [InlineData("next-playout", LegacyOperatorPlayoutCommand.Next)]
    [InlineData("take-out", LegacyOperatorPlayoutCommand.TakeOut)]
    public void CommandIntents_ParseClosedEmptyPayload(
        string type,
        LegacyOperatorPlayoutCommand expected)
    {
        var parsed = LegacyBridgeProtocol.TryParseIntent(
            "{\"type\":\"" + type + "\",\"payload\":{}}",
            out var intent,
            out var error);

        Assert.True(parsed, error);
        var command = Assert.IsType<LegacyExecutePlayoutIntent>(intent);
        Assert.Equal(expected, command.Command);
    }

    [Theory]
    [InlineData("prepare-playout")]
    [InlineData("take-in")]
    [InlineData("next-playout")]
    [InlineData("take-out")]
    [InlineData("toggle-background")]
    [InlineData("choose-background")]
    public void EmptyPlayoutIntents_RejectInjectedProperties(string type)
    {
        Assert.False(LegacyBridgeProtocol.TryParseIntent(
            "{\"type\":\"" + type +
                "\",\"payload\":{\"scene\":\"5001\"}}",
            out _,
            out _));
    }

    [Fact]
    public void CompositionIntents_ParseOnlyTrustedShape()
    {
        Assert.True(LegacyBridgeProtocol.TryParseIntent(
            """{"type":"set-fade-duration","payload":{"duration":6}}""",
            out var fade,
            out var fadeError), fadeError);
        Assert.Equal(6, Assert.IsType<LegacySetFadeDurationIntent>(fade).Duration);

        Assert.True(LegacyBridgeProtocol.TryParseIntent(
            """{"type":"toggle-background","payload":{}}""",
            out var toggle,
            out var toggleError), toggleError);
        Assert.IsType<LegacyToggleBackgroundIntent>(toggle);

        Assert.True(LegacyBridgeProtocol.TryParseIntent(
            """{"type":"choose-background","payload":{}}""",
            out var choose,
            out var chooseError), chooseError);
        Assert.IsType<LegacyChooseBackgroundIntent>(choose);
    }

    [Theory]
    [InlineData(0)]
    [InlineData(6)]
    [InlineData(19)]
    public void FadeDuration_AcceptsOriginalComboIndexRange(int duration)
    {
        Assert.True(LegacyBridgeProtocol.TryParseIntent(
            "{\"type\":\"set-fade-duration\",\"payload\":{\"duration\":" +
                duration + "}}",
            out var intent,
            out var error), error);
        Assert.Equal(duration,
            Assert.IsType<LegacySetFadeDurationIntent>(intent).Duration);
    }

    [Theory]
    [InlineData("-1")]
    [InlineData("20")]
    [InlineData("60")]
    [InlineData("1.5")]
    [InlineData("\"6\"")]
    [InlineData("null")]
    public void FadeDuration_RejectsNonCanonicalValues(string value)
    {
        Assert.False(LegacyBridgeProtocol.TryParseIntent(
            "{\"type\":\"set-fade-duration\",\"payload\":{\"duration\":" +
                value + "}}",
            out _,
            out _));
    }

    [Fact]
    public void SerializedState_ProjectsAuthoritativePlayoutState()
    {
        var snapshot = new LegacyOperatorSnapshot(
            1,
            string.Empty,
            [],
            null,
            [],
            [],
            null,
            string.Empty,
            LegacyOperatorStatusKind.Neutral);
        var playout = new LegacyOperatorPlayoutSnapshot(
            Mode: PlayoutMode.DryRun,
            ConnectionState: PlayoutConnectionState.DryRunReady,
            Phase: LegacyOperatorPlayoutPhase.Prepared,
            IsProcessRunning: true,
            IsConnected: true,
            IsCommandAvailable: true,
            LiveTakeInAllowed: true,
            IsPlayCompletionPending: false,
            OutcomeUnknown: false,
            PreparedCode: "5001",
            OnAirCode: null,
            CurrentCueIndexZeroBased: 0,
            CurrentEntryId: "entry-1",
            BuilderKey: "s5001",
            PageIndexZeroBased: 0,
            PageCount: 2,
            PageSize: 5,
            ItemCount: 8,
            CurrentPageItemCount: 5,
            IsLastPage: false,
            NextKind: LegacyWorkflowNextKind.PageNext,
            IsBusy: false,
            RefreshActive: false,
            RefreshCompletedCount: 0,
            RefreshMaximumCount: 1,
            RefreshLimitReached: false,
            FadeDuration: 6,
            BackgroundEnabled: true,
            BackgroundFileName: "기본.vrv",
            CanChangeBackground: false,
            Message: "준비 완료")
        {
            OnAirCueIndexZeroBased = -1,
            OnAirEntryId = null,
            IsTakeOutCompletionPending = true,
            RefreshNextAtUtc = new DateTimeOffset(
                2026, 7, 17, 2, 3, 4, TimeSpan.Zero),
            RefreshLastSuccessAtUtc = new DateTimeOffset(
                2026, 7, 17, 1, 2, 3, TimeSpan.Zero),
            RefreshFaultCode = "DATABASE_UNAVAILABLE"
        };

        using var document = JsonDocument.Parse(
            LegacyBridgeProtocol.SerializeState(snapshot, playout: playout));
        var projected = document.RootElement.GetProperty("payload").GetProperty("playout");

        Assert.Equal("dryRun", projected.GetProperty("mode").GetString());
        Assert.Equal("dryRunReady", projected.GetProperty("connectionState").GetString());
        Assert.Equal("prepared", projected.GetProperty("phase").GetString());
        Assert.Equal("5001", projected.GetProperty("preparedCode").GetString());
        Assert.Equal("pageNext", projected.GetProperty("nextKind").GetString());
        Assert.Equal(2, projected.GetProperty("pageCount").GetInt32());
        Assert.Equal("기본.vrv", projected.GetProperty("backgroundFileName").GetString());
        Assert.Equal("entry-1", projected.GetProperty("currentEntryId").GetString());
        Assert.Equal(-1, projected.GetProperty("onAirCueIndexZeroBased").GetInt32());
        Assert.Equal(JsonValueKind.Null, projected.GetProperty("onAirEntryId").ValueKind);
        Assert.True(projected.GetProperty("isTakeOutCompletionPending").GetBoolean());
        Assert.Equal(
            new DateTimeOffset(2026, 7, 17, 2, 3, 4, TimeSpan.Zero),
            projected.GetProperty("refreshNextAtUtc").GetDateTimeOffset());
        Assert.Equal(
            new DateTimeOffset(2026, 7, 17, 1, 2, 3, TimeSpan.Zero),
            projected.GetProperty("refreshLastSuccessAtUtc").GetDateTimeOffset());
        Assert.Equal(
            "DATABASE_UNAVAILABLE",
            projected.GetProperty("refreshFaultCode").GetString());
        Assert.False(projected.TryGetProperty("builderKey", out _));
    }
}
