#nullable enable

using System.Globalization;

namespace MBN_STOCK_WEBVIEW.Core.Playout.Scenes;

/// <summary>
/// Safe, read-only representation of a typed builder mutation for the operator preview.
/// Asset paths are deliberately replaced with a configured marker.
/// </summary>
public sealed record LegacyScenePreviewField(string Kind, string Name, string Value);

public static class LegacyScenePreviewFactory
{
    private const int MaximumFields = 4_096;
    private const int MaximumValueLength = 16_384;

    public static IReadOnlyList<LegacyScenePreviewField> Create(
        IReadOnlyList<PlayoutMutation> mutations)
    {
        ArgumentNullException.ThrowIfNull(mutations);
        if (mutations.Count > MaximumFields)
        {
            throw new LegacySceneDataException("The scene preview exceeds its bounded field count.");
        }

        var fields = new List<LegacyScenePreviewField>(mutations.Count);
        foreach (var mutation in mutations)
        {
            ArgumentNullException.ThrowIfNull(mutation);
            LegacyScenePreviewField field = mutation switch
            {
                PlayoutSetValue value => new("value", value.ObjectName, value.Value),
                PlayoutSetAssetValue asset => new("asset", asset.ObjectName, "configured"),
                PlayoutSetVisible visible => new(
                    "visible", visible.ObjectName, visible.IsVisible ? "true" : "false"),
                PlayoutSetFaceColor color => new(
                    "color", color.ObjectName,
                    $"{color.Red},{color.Green},{color.Blue},{color.Alpha}"),
                PlayoutSetPosition position => new(
                    "position", position.ObjectName,
                    $"{F(position.X)},{F(position.Y)},{F(position.Z)} [{position.Components}]"),
                PlayoutSetPositionKey position => new(
                    "position-key", position.ObjectName,
                    $"key={position.KeyIndex};{F(position.X)},{F(position.Y)},{F(position.Z)} [{position.Components}]"),
                PlayoutSetScale scale => new(
                    "scale", scale.ObjectName,
                    $"{F(scale.X)},{F(scale.Y)},{F(scale.Z)} [{scale.Components}]"),
                PlayoutSetCropKey crop => new(
                    "crop", crop.ObjectName,
                    $"key={crop.KeyIndex};{F(crop.Left)},{F(crop.Top)},{F(crop.Right)},{F(crop.Bottom)} [{crop.Edges}]"),
                PlayoutSetCircleAngleKey angle => new(
                    "angle", angle.ObjectName,
                    $"key={angle.KeyIndex};{F(angle.Start)},{F(angle.End)} [{angle.Components}]"),
                PlayoutSetPathPoints path => new(
                    "path", path.ObjectName, Points(path.Points)),
                PlayoutSetPathShapePoints path => new(
                    "path-shape", path.ObjectName, Points(path.Points)),
                PlayoutUseBackground background => new(
                    "background", "scene", background.IsEnabled ? "enabled" : "disabled"),
                PlayoutSetBackgroundTexture => new(
                    "background-texture", "scene", "configured"),
                PlayoutSetBackgroundVideo video => new(
                    "background-video", "scene",
                    $"configured;loop={video.LoopCount};loop-mode={video.LoopInfinite}"),
                _ => throw new LegacySceneDataException(
                    "The scene preview encountered an unsupported typed mutation.")
            };

            if (string.IsNullOrWhiteSpace(field.Name) || field.Name.Length > 128 ||
                field.Name.Any(char.IsControl) || field.Value.Length > MaximumValueLength ||
                field.Value.Any(character => character is '\0' or '\r' or '\n'))
            {
                throw new LegacySceneDataException("The scene preview contains an invalid field.");
            }

            fields.Add(field);
        }

        return Array.AsReadOnly(fields.ToArray());
    }

    private static string Points(IReadOnlyList<PlayoutPoint> points)
    {
        ArgumentNullException.ThrowIfNull(points);
        var value = string.Join(
            ";",
            points.Select(point => $"{F(point.X)},{F(point.Y)},{F(point.Z)}"));
        if (value.Length > MaximumValueLength)
        {
            throw new LegacySceneDataException("The scene preview path exceeds its bounded size.");
        }

        return $"count={points.Count};{value}";
    }

    private static string F(float value) =>
        value.ToString("0.###", CultureInfo.InvariantCulture);
}
