using System.Globalization;

namespace Sunfish.Web;

/// <summary>Un rango de bytes pedido con "Range: bytes=..." (solo se admite uno).</summary>
public readonly record struct ByteRange(long Start, long End)
{
    public long Length => End - Start + 1;

    public enum ParseResult { None, Valid, Unsatisfiable }

    /// <summary>
    /// Interpreta la cabecera Range para un fichero de <paramref name="size"/> bytes.
    /// None: sin cabecera, mal formada o con varios rangos (se responde el fichero entero).
    /// </summary>
    public static ParseResult TryParse(string? header, long size, out ByteRange range)
    {
        range = default;
        if (string.IsNullOrWhiteSpace(header))
            return ParseResult.None;
        header = header.Trim();
        if (!header.StartsWith("bytes=", StringComparison.OrdinalIgnoreCase))
            return ParseResult.None;
        string spec = header[6..].Trim();
        if (spec.Contains(','))
            return ParseResult.None;
        int dash = spec.IndexOf('-');
        if (dash < 0)
            return ParseResult.None;
        string first = spec[..dash].Trim();
        string last = spec[(dash + 1)..].Trim();

        if (first.Length == 0)
        {
            // "-N": los ultimos N bytes
            if (!long.TryParse(last, NumberStyles.None, CultureInfo.InvariantCulture, out long suffix))
                return ParseResult.None;
            if (suffix == 0 || size == 0)
                return ParseResult.Unsatisfiable;
            long start = Math.Max(0, size - suffix);
            range = new ByteRange(start, size - 1);
            return ParseResult.Valid;
        }

        if (!long.TryParse(first, NumberStyles.None, CultureInfo.InvariantCulture, out long from))
            return ParseResult.None;
        long to = size - 1;
        if (last.Length > 0)
        {
            if (!long.TryParse(last, NumberStyles.None, CultureInfo.InvariantCulture, out to) || to < from)
                return ParseResult.None;
            to = Math.Min(to, size - 1);
        }
        if (from >= size)
            return ParseResult.Unsatisfiable;
        range = new ByteRange(from, to);
        return ParseResult.Valid;
    }
}
