#nullable enable

using System.Collections.ObjectModel;

namespace MBN_STOCK_WEBVIEW.Core.Playout.Scenes;

/// <summary>
/// Fixed metadata for the 35 builders found in the read-only MBN_STOCK_N Scene folder.
/// Runtime implementations and their concrete DTO contracts are validated one-to-one by
/// <see cref="LegacySceneMutationBuilderRegistry"/>.
/// </summary>
public static class LegacySceneCatalog
{
    public const int ExpectedDefinitionCount = 35;

    private const LegacySceneMutationCapability Value =
        LegacySceneMutationCapability.Value;
    private const LegacySceneMutationCapability Asset =
        LegacySceneMutationCapability.AssetValue;
    private const LegacySceneMutationCapability Visible =
        LegacySceneMutationCapability.Visibility;
    private const LegacySceneMutationCapability Color =
        LegacySceneMutationCapability.FaceColor;
    private const LegacySceneMutationCapability Position =
        LegacySceneMutationCapability.Position;
    private const LegacySceneMutationCapability PositionKey =
        LegacySceneMutationCapability.PositionKey;
    private const LegacySceneMutationCapability Scale =
        LegacySceneMutationCapability.Scale;
    private const LegacySceneMutationCapability Crop =
        LegacySceneMutationCapability.Crop;
    private const LegacySceneMutationCapability CircleAngle =
        LegacySceneMutationCapability.CircleAngleKey;
    private const LegacySceneMutationCapability Path =
        LegacySceneMutationCapability.PathPoints;
    private const LegacySceneMutationCapability PathShape =
        LegacySceneMutationCapability.PathShapePoints;
    private const LegacySceneMutationCapability BackgroundVideo =
        LegacySceneMutationCapability.BackgroundVideo;

    private static readonly ReadOnlyCollection<LegacySceneDefinition> RegisteredDefinitions =
        Array.AsReadOnly(CreateDefinitions());

    private static readonly IReadOnlyDictionary<string, LegacySceneDefinition> BySceneCode =
        CreateSceneCodeIndex(RegisteredDefinitions);

    private static readonly IReadOnlyDictionary<string, IReadOnlyList<LegacySceneDefinition>>
        ByCutAlias = CreateCutAliasIndex(RegisteredDefinitions);

    static LegacySceneCatalog()
    {
        ValidateInvariants(RegisteredDefinitions);
    }

    public static IReadOnlyList<LegacySceneDefinition> Definitions => RegisteredDefinitions;

    /// <summary>
    /// Finds a definition by its builder-derived numeric scene code.
    /// </summary>
    public static LegacySceneDefinition? FindBySceneCode(string? sceneCode)
    {
        if (string.IsNullOrWhiteSpace(sceneCode))
        {
            return null;
        }

        return BySceneCode.TryGetValue(sceneCode, out var definition)
            ? definition
            : null;
    }

    /// <summary>
    /// Returns every builder eligible for an alias. The 8018/8032/5032 aliases intentionally
    /// return both conditional builders; the caller must use typed playlist context to choose.
    /// </summary>
    public static IReadOnlyList<LegacySceneDefinition> FindByCutAlias(string? cutAlias)
    {
        if (string.IsNullOrWhiteSpace(cutAlias))
        {
            return Array.Empty<LegacySceneDefinition>();
        }

        return ByCutAlias.TryGetValue(cutAlias, out var definitions)
            ? definitions
            : Array.Empty<LegacySceneDefinition>();
    }

    private static LegacySceneDefinition[] CreateDefinitions() =>
    [
        Define<S5001SceneDtoMarker>("s5001", "5001", ["5001", "N5001"],
            LegacyScenePageSize.None, LegacySceneReachability.MainFormDispatch,
            Value | Visible | Asset),
        Define<S5006SceneDtoMarker>("s5006", "5006", ["5006"],
            LegacyScenePageSize.None, LegacySceneReachability.MainFormDispatch,
            Value | Visible | Color | BackgroundVideo),
        Define<S5011SceneDtoMarker>("s5011", "5011", ["5011"],
            LegacyScenePageSize.None, LegacySceneReachability.MainFormDispatch,
            Value | Visible | Color),
        Define<S5016SceneDtoMarker>("s5016", "5016", ["5016"],
            LegacyScenePageSize.None, LegacySceneReachability.MainFormDispatch,
            Value | Visible),
        Define<S50160SceneDtoMarker>("s50160", "50160", ["50160"],
            LegacyScenePageSize.None, LegacySceneReachability.MainFormDispatch,
            Value | Visible),
        Define<S5023SceneDtoMarker>("s5023", "5023", ["5023"],
            LegacyScenePageSize.None, LegacySceneReachability.MainFormDispatch,
            Value | Color),
        Define<S5024SceneDtoMarker>("s5024", "5024", ["5024"],
            LegacyScenePageSize.None, LegacySceneReachability.MainFormDispatch,
            Value | Visible | Position | Scale),
        Define<S5025SceneDtoMarker>("s5025", "5025", ["5025"],
            LegacyScenePageSize.None, LegacySceneReachability.MainFormDispatch,
            Value),
        Define<S5026SceneDtoMarker>("s5026", "5026", ["5026"],
            LegacyScenePageSize.None, LegacySceneReachability.MainFormDispatch,
            Value | Visible | Color | Crop),
        Define<S5029SceneDtoMarker>("s5029", "5029", ["5029"],
            LegacyScenePageSize.None, LegacySceneReachability.MainFormDispatch,
            Value | Visible | Position | PathShape),
        Define<S5032SceneDtoMarker>("s5032", "5032", ["8018", "8032", "5032"],
            LegacyScenePageSize.None,
            LegacySceneReachability.ConditionalMainFormDispatch,
            Value | Visible | Asset),
        Define<S5037SceneDtoMarker>("s5037", "5037", ["5037"],
            LegacyScenePageSize.None, LegacySceneReachability.MainFormDispatch,
            Value | Visible),
        Define<S5074SceneDtoMarker>("s5074", "5074", ["5074"],
            LegacyScenePageSize.Five, LegacySceneReachability.MainFormDispatch,
            Value | Visible | Asset),
        Define<S5076SceneDtoMarker>("s5076", "5076", ["5076"],
            LegacyScenePageSize.None, LegacySceneReachability.MainFormDispatch,
            Value | Visible | CircleAngle),
        Define<S5077SceneDtoMarker>("s5077", "5077", ["5077"],
            LegacyScenePageSize.Six, LegacySceneReachability.MainFormDispatch,
            Value | Visible | Asset),
        Define<S5078SceneDtoMarker>("s5078", "5078", ["5078"],
            LegacyScenePageSize.None, LegacySceneReachability.MainFormDispatch,
            Value | Visible | Scale),
        Define<S5079SceneDtoMarker>("s5079", "5079", ["5079"],
            LegacyScenePageSize.None, LegacySceneReachability.MainFormDispatch,
            Value | Visible | Path),
        Define<S5080SceneDtoMarker>("s5080", "5080", ["5080"],
            LegacyScenePageSize.None, LegacySceneReachability.MainFormDispatch,
            Value | Visible | PositionKey | Scale),
        Define<S5081SceneDtoMarker>("s5081", "5081", ["5081"],
            LegacyScenePageSize.None, LegacySceneReachability.MainFormDispatch,
            Value | Visible | PositionKey | Scale),
        Define<S5082SceneDtoMarker>("s5082", "5082", ["5082"],
            LegacyScenePageSize.None, LegacySceneReachability.MainFormDispatch,
            Value | Visible),
        Define<S5083SceneDtoMarker>("s5083", "5083", ["5083"],
            LegacyScenePageSize.None, LegacySceneReachability.MainFormDispatch,
            Value | Position | Path),
        Define<S5084SceneDtoMarker>("s5084", "5084", ["5084"],
            LegacyScenePageSize.None, LegacySceneReachability.MainFormDispatch,
            Value | Position | Path),
        Define<S5085SceneDtoMarker>("s5085", "5085", ["5085"],
            LegacyScenePageSize.None, LegacySceneReachability.MainFormDispatch,
            Value | Color),
        Define<S5086SceneDtoMarker>("s5086", "5086", ["5086"],
            LegacyScenePageSize.None, LegacySceneReachability.MainFormDispatch,
            Value | Visible | Position | PathShape),
        Define<S50860SceneDtoMarker>("s50860", "50860", ["50860"],
            LegacyScenePageSize.None, LegacySceneReachability.MainFormDispatch,
            Value | Visible | Color | Position | PathShape),
        Define<S5087SceneDtoMarker>("s5087", "5087", ["5087"],
            LegacyScenePageSize.None, LegacySceneReachability.MainFormDispatch,
            Value | Visible | Position | PathShape),
        Define<S5088SceneDtoMarker>("s5088", "5088", ["5088"],
            LegacyScenePageSize.Twelve, LegacySceneReachability.MainFormDispatch,
            Value | Visible | Asset),
        Define<S6001SceneDtoMarker>("s6001", "6001", ["6001"],
            LegacyScenePageSize.None, LegacySceneReachability.MainFormDispatch,
            Value | Asset | Visible | Color),
        Define<S6067SceneDtoMarker>("s6067", "6067", ["6067"],
            LegacyScenePageSize.None, LegacySceneReachability.MainFormDispatch,
            Value | Color),
        Define<S8001SceneDtoMarker>("s8001", "8001", ["8001", "8002"],
            LegacyScenePageSize.None, LegacySceneReachability.MainFormDispatch,
            Value | Color),
        Define<S8003SceneDtoMarker>("s8003", "8003", ["8003"],
            LegacyScenePageSize.None, LegacySceneReachability.MainFormDispatch,
            Value | Visible | Color | Scale),
        Define<S8010SceneDtoMarker>(
            "s8010",
            "8010",
            ["8035", "8061", "8040", "8046", "8051", "8056"],
            LegacyScenePageSize.None,
            LegacySceneReachability.MainFormDispatch,
            Value | Visible | Color | Position | Crop | Path),
        Define<S8018SceneDtoMarker>("s8018", "8018", ["8018", "8032", "5032"],
            LegacyScenePageSize.None,
            LegacySceneReachability.ConditionalMainFormDispatch,
            Value | Visible | Asset),
        Define<S8067SceneDtoMarker>("s8067", "8067", ["8067", "5068", "5070", "5072"],
            LegacyScenePageSize.None, LegacySceneReachability.MainFormDispatch,
            Value | Visible | Color),
        Define<S8086SceneDtoMarker>("s8086", "8086", [],
            LegacyScenePageSize.None, LegacySceneReachability.NoMainFormDispatch,
            Value | Color)
    ];

    private static LegacySceneDefinition Define<TDtoMarker>(
        string builderKey,
        string sceneCode,
        IEnumerable<string> cutAliases,
        LegacyScenePageSize pageSize,
        LegacySceneReachability reachability,
        LegacySceneMutationCapability mutationCapabilities)
        where TDtoMarker : ILegacySceneDtoMarker =>
        new(
            builderKey,
            sceneCode,
            cutAliases,
            pageSize,
            reachability,
            typeof(TDtoMarker),
            builderKey,
            mutationCapabilities);

    private static IReadOnlyDictionary<string, LegacySceneDefinition> CreateSceneCodeIndex(
        IEnumerable<LegacySceneDefinition> definitions) =>
        new ReadOnlyDictionary<string, LegacySceneDefinition>(
            definitions.ToDictionary(
                definition => definition.SceneCode,
                StringComparer.Ordinal));

    private static IReadOnlyDictionary<string, IReadOnlyList<LegacySceneDefinition>>
        CreateCutAliasIndex(IEnumerable<LegacySceneDefinition> definitions)
    {
        var mutable = new Dictionary<string, List<LegacySceneDefinition>>(
            StringComparer.Ordinal);
        foreach (var definition in definitions)
        {
            foreach (var alias in definition.CutAliases)
            {
                if (!mutable.TryGetValue(alias, out var matches))
                {
                    matches = [];
                    mutable.Add(alias, matches);
                }

                matches.Add(definition);
            }
        }

        var readOnly = mutable.ToDictionary(
            pair => pair.Key,
            pair => (IReadOnlyList<LegacySceneDefinition>)Array.AsReadOnly(pair.Value.ToArray()),
            StringComparer.Ordinal);
        return new ReadOnlyDictionary<string, IReadOnlyList<LegacySceneDefinition>>(readOnly);
    }

    private static void ValidateInvariants(
        IReadOnlyCollection<LegacySceneDefinition> definitions)
    {
        if (definitions.Count != ExpectedDefinitionCount)
        {
            throw new InvalidOperationException(
                $"The legacy scene catalog must contain exactly {ExpectedDefinitionCount} definitions.");
        }

        RequireUnique(definitions, definition => definition.BuilderKey, "builder key");
        RequireUnique(definitions, definition => definition.SceneCode, "scene code");
        RequireUnique(definitions, definition => definition.SourceHashKey, "source hash key");
        RequireUnique(definitions, definition => definition.DtoTypeMarker.FullName!, "DTO marker");

        foreach (var aliasGroup in definitions
                     .SelectMany(definition => definition.CutAliases.Select(alias =>
                         (Alias: alias, Definition: definition)))
                     .GroupBy(pair => pair.Alias, StringComparer.Ordinal)
                     .Where(group => group.Count() > 1))
        {
            if (aliasGroup.Any(pair =>
                    pair.Definition.Reachability !=
                    LegacySceneReachability.ConditionalMainFormDispatch))
            {
                throw new InvalidOperationException(
                    $"Shared cut alias '{aliasGroup.Key}' must map only to conditional builders.");
            }
        }
    }

    private static void RequireUnique(
        IEnumerable<LegacySceneDefinition> definitions,
        Func<LegacySceneDefinition, string> selector,
        string label)
    {
        var duplicate = definitions
            .GroupBy(selector, StringComparer.Ordinal)
            .FirstOrDefault(group => group.Count() > 1);
        if (duplicate is not null)
        {
            throw new InvalidOperationException(
                $"Duplicate {label} '{duplicate.Key}' in the legacy scene catalog.");
        }
    }
}
