namespace ErsatzTV.Core.Images;

/// <summary>
///     The decode-budget policy for a remote image, as pure arithmetic so it can be enforced both at
///     render time (graphics engine) and at save time (logo download) without materializing
///     multi-gigabyte images. Extracted from ImageElementBase for reuse. (ersatztv#525, from #511.)
/// </summary>
public static class RemoteImageDecodeBudget
{
    /// <summary>
    ///     Ceiling on TOTAL decoded pixels — width x height x frames, as one product. Checking
    ///     dimensions and frame count independently does not bound the decode: a 60 KiB 2500x2500 x600
    ///     GIF passes both a 50 MP dimension check and a 600 frame check and costs ~14 GiB.
    /// </summary>
    public const long MaxRemoteDecodedPixels = 50_000_000;

    /// <summary>Frame ceiling, a cheap legible guard against absurd counts of tiny frames.</summary>
    public const int MaxRemoteFrames = 600;

    public static void EnsureDimensionsAffordable(int width, int height, Uri uri)
    {
        long pixels = (long)width * height;
        if (pixels > MaxRemoteDecodedPixels)
        {
            throw new InvalidOperationException(
                $"Remote image {uri} is {width}x{height} ({pixels} pixels), over the "
                + $"{MaxRemoteDecodedPixels} pixel limit");
        }
    }

    public static int AffordableFrames(int width, int height)
    {
        long perFrame = Math.Max((long)width * height, 1);
        return (int)Math.Clamp(MaxRemoteDecodedPixels / perFrame, 1, MaxRemoteFrames);
    }

    public static void EnsureDecodeAffordable(int width, int height, int frameCount, Uri uri)
    {
        int frames = Math.Max(frameCount, 1);
        if (frames > MaxRemoteFrames)
        {
            throw new InvalidOperationException(
                $"Remote image {uri} has {frames} frames, over the {MaxRemoteFrames} frame limit");
        }

        long totalPixels = (long)width * height * frames;
        if (totalPixels > MaxRemoteDecodedPixels)
        {
            throw new InvalidOperationException(
                $"Remote image {uri} decodes to {width}x{height} x{frames} frames "
                + $"({totalPixels} pixels), over the {MaxRemoteDecodedPixels} pixel limit");
        }
    }
}
