using MBN_STOCK_WEBVIEW.Core.Playout.Scenes;

namespace MBN_STOCK_WEBVIEW.Core.Tests;

public sealed class LegacySceneRuntimeCoverageTests
{
    private static readonly string[] ExpectedAliases =
    [
        "5001", "5006", "5011", "5016", "50160", "5023", "5024", "5025",
        "5026", "5029", "5032", "5037", "5068", "5070", "5072", "5074",
        "5076", "5077", "5078", "5079", "5080", "5081", "5082", "5083",
        "5084", "5085", "5086", "50860", "5087", "5088", "6001", "6067",
        "8001", "8002", "8003", "8018", "8032", "8035", "8040", "8046",
        "8051", "8056", "8061", "8067", "N5001"
    ];

    [Fact]
    public void Complete_router_matches_the_independent_34_builder_45_alias_baseline()
    {
        var router = Router(ExpectedAliases);

        var report = LegacySceneRuntimeCoverage.Analyze(router);

        Assert.True(report.IsComplete);
        Assert.Equal(34, report.ReachableBuilderKeys.Count);
        Assert.Equal(45, report.ExpectedCutCodes.Count);
        Assert.Equal(ExpectedAliases, report.ExpectedCutCodes);
        Assert.Equal(ExpectedAliases, report.RegisteredCutCodes);
        Assert.Empty(report.MissingCutCodes);
        Assert.Empty(report.UnexpectedCutCodes);
        Assert.Empty(report.UncoveredBuilderKeys);
        Assert.Equal(["s8086"], report.UnreachableBuilderKeys);
        Assert.DoesNotContain("s8086", report.ReachableBuilderKeys);
        Assert.DoesNotContain("8086", report.ExpectedCutCodes);
        Assert.DoesNotContain("8010", report.ExpectedCutCodes);

        var validated = LegacySceneRuntimeCoverage.Validate(router);
        Assert.True(validated.IsComplete);
    }

    [Fact]
    public void Conditional_aliases_are_owned_only_by_s5032_and_s8018()
    {
        var report = LegacySceneRuntimeCoverage.Validate(Router(ExpectedAliases));

        Assert.Equal(
            new[] { "5032", "8018", "8032" },
            report.ConditionalAliasOwners.Keys.OrderBy(key => key, StringComparer.Ordinal));
        Assert.All(report.ConditionalAliasOwners.Values, owners =>
            Assert.Equal(new[] { "s5032", "s8018" }, owners));
        Assert.Contains("s5032", report.ReachableBuilderKeys);
        Assert.Contains("s8018", report.ReachableBuilderKeys);
    }

    [Fact]
    public void Missing_direct_alias_identifies_the_single_uncovered_builder()
    {
        var router = Router(ExpectedAliases.Where(alias => alias != "5078"));

        var report = LegacySceneRuntimeCoverage.Analyze(router);

        Assert.False(report.IsComplete);
        Assert.Equal(["5078"], report.MissingCutCodes);
        Assert.Empty(report.UnexpectedCutCodes);
        Assert.Equal(["s5078"], report.UncoveredBuilderKeys);
        Assert.Throws<InvalidOperationException>(() =>
            LegacySceneRuntimeCoverage.Validate(router));
    }

    [Fact]
    public void Missing_shared_alias_marks_both_conditional_builders_uncovered()
    {
        var router = Router(ExpectedAliases.Where(alias => alias != "8032"));

        var report = LegacySceneRuntimeCoverage.Analyze(router);

        Assert.False(report.IsComplete);
        Assert.Equal(["8032"], report.MissingCutCodes);
        Assert.Equal(new[] { "s5032", "s8018" }, report.UncoveredBuilderKeys);
        Assert.Equal(
            new[] { "s5032", "s8018" },
            report.ConditionalAliasOwners["8032"]);
    }

    [Fact]
    public void Unreachable_s8086_alias_is_rejected_as_an_unexpected_runtime_route()
    {
        var router = Router(ExpectedAliases.Append("8086"));

        var report = LegacySceneRuntimeCoverage.Analyze(router);

        Assert.False(report.IsComplete);
        Assert.Empty(report.MissingCutCodes);
        Assert.Equal(["8086"], report.UnexpectedCutCodes);
        Assert.Empty(report.UncoveredBuilderKeys);
        Assert.Equal(["s8086"], report.UnreachableBuilderKeys);
        Assert.Throws<InvalidOperationException>(() =>
            LegacySceneRuntimeCoverage.Validate(router));
    }

    [Fact]
    public void Unknown_extra_alias_is_rejected_even_when_all_expected_aliases_exist()
    {
        var router = Router(ExpectedAliases.Append("9999"));

        var report = LegacySceneRuntimeCoverage.Analyze(router);

        Assert.False(report.IsComplete);
        Assert.Equal(["9999"], report.UnexpectedCutCodes);
        Assert.Equal(46, report.RegisteredCutCodes.Count);
    }

    [Fact]
    public void Public_expected_alias_surface_is_sorted_stable_and_read_only()
    {
        Assert.Equal(45, LegacySceneRuntimeCoverage.ExpectedActiveAliasCount);
        Assert.Equal(34, LegacySceneRuntimeCoverage.ExpectedReachableBuilderCount);
        Assert.Equal(ExpectedAliases, LegacySceneRuntimeCoverage.ExpectedCutCodes);
        Assert.Equal(
            ExpectedAliases.OrderBy(alias => alias, StringComparer.Ordinal),
            LegacySceneRuntimeCoverage.ExpectedCutCodes);
        Assert.IsAssignableFrom<IReadOnlyList<string>>(
            LegacySceneRuntimeCoverage.ExpectedCutCodes);
    }

    [Fact]
    public void Null_router_is_rejected()
    {
        Assert.Throws<ArgumentNullException>(() =>
            LegacySceneRuntimeCoverage.Analyze(null!));
        Assert.Throws<ArgumentNullException>(() =>
            LegacySceneRuntimeCoverage.Validate(null!));
    }

    private static LegacySceneDataSourceRouter Router(IEnumerable<string> cutCodes) => new(
    [
        new LegacySceneDataSourceRoute(cutCodes, Load)
    ]);

    private static Task<LegacySceneDataPage> Load(
        LegacyPlaylistEntry entry,
        int page,
        CancellationToken cancellationToken) =>
        throw new NotSupportedException();
}
