using System.IO.Compression;

namespace ZB.MOM.WW.CBDD.Core.Compression;

/// <summary>
/// Compression configuration for document payload processing.
/// </summary>
public sealed class CompressionOptions
{
    /// <summary>
    /// Default compression options (compression disabled).
    /// </summary>
    public static CompressionOptions Default { get; } = new();

    /// <summary>
    /// Enables payload compression for new writes.
    /// </summary>
    public bool EnableCompression { get; init; } = false;

    /// <summary>
    /// Minimum payload size (bytes) required before compression is attempted.
    /// </summary>
    public int MinSizeBytes { get; init; } = 1024;

    /// <summary>
    /// Minimum percentage of size reduction required to keep compressed output.
    /// </summary>
    public int MinSavingsPercent { get; init; } = 10;

    /// <summary>
    /// Preferred default codec for new writes.
    /// </summary>
    public CompressionCodec Codec { get; init; } = CompressionCodec.Brotli;

    /// <summary>
    /// Compression level passed to codec implementations.
    /// </summary>
    public CompressionLevel Level { get; init; } = CompressionLevel.Fastest;

    /// <summary>
    /// Maximum allowed decompressed payload size.
    /// </summary>
    public int MaxDecompressedSizeBytes { get; init; } = 16 * 1024 * 1024;

    /// <summary>
    /// Optional maximum input size allowed for compression attempts.
    /// </summary>
    public int? MaxCompressionInputBytes { get; init; }

    /// <summary>
    /// Normalizes and validates compression options.
    /// </summary>
    /// <param name="options">Optional user-provided options.</param>
    internal static CompressionOptions Normalize(CompressionOptions? options)
    {
        var candidate = options ?? Default;

        if (candidate.MinSizeBytes < 0)
            throw new ArgumentOutOfRangeException(nameof(MinSizeBytes), "MinSizeBytes must be non-negative.");

        if (candidate.MinSavingsPercent is < 0 or > 100)
            throw new ArgumentOutOfRangeException(nameof(MinSavingsPercent), "MinSavingsPercent must be between 0 and 100.");

        if (!Enum.IsDefined(candidate.Codec))
            throw new ArgumentOutOfRangeException(nameof(Codec), $"Unsupported codec: {candidate.Codec}.");

        if (candidate.MaxDecompressedSizeBytes <= 0)
            throw new ArgumentOutOfRangeException(nameof(MaxDecompressedSizeBytes), "MaxDecompressedSizeBytes must be greater than 0.");

        if (candidate.MaxCompressionInputBytes is <= 0)
            throw new ArgumentOutOfRangeException(nameof(MaxCompressionInputBytes), "MaxCompressionInputBytes must be greater than 0 when provided.");

        return candidate;
    }
}
