using OpenNest;
namespace OpenNest.Data;
///
/// One bounded page request for browsing the drawing library
/// (GET /api/drawings/query). The server filters and pages in SQL; the
/// client and server share these bounds.
///
public sealed class DrawingLibraryQuery
{
public const int DefaultLimit = 100;
public const int MaxLimit = 500;
public const int MaxSearchLength = 200;
///
/// Case-insensitive (ASCII) substring of name, customer or notes. Blank means no filter.
///
public string Search { get; init; } = "";
/// Exact customer, compared by . Blank means any.
public string Customer { get; init; } = "";
/// Exact material name, compared by . Blank means any.
public string MaterialName { get; init; } = "";
/// Exact grade, compared by . Blank means any.
public string MaterialGrade { get; init; } = "";
/// Exact thickness after rounding to four decimals; null means any.
public double? Thickness { get; init; }
public CutType? CutType { get; init; }
/// Only drawings whose Cut first setting equals this; null means any.
public bool? CutFirst { get; init; }
public bool IncludeArchived { get; init; }
/// Column to order by; ties are broken by id in the same direction.
public DrawingSortField Sort { get; init; } = DrawingSortField.SavedAt;
/// Largest/newest/Z first when true (the default, newest saved first).
public bool Descending { get; init; } = true;
public int Offset { get; init; }
public int Limit { get; init; } = DefaultLimit;
/// The search text as matched: trimmed, never null.
public string NormalizedSearch => (Search ?? "").Trim();
/// The first violated bound, or null when the query may be sent.
public string? GetValidationError()
{
if (!Enum.IsDefined(Sort))
return "sort must be a supported column.";
if (CutType is { } cutType && !Enum.IsDefined(cutType))
return "cutType must be Laser, Waterjet or Plasma.";
if (Thickness is { } thickness && (!double.IsFinite(thickness) || thickness < 0))
return "thickness must be a finite number, zero or greater.";
if (Offset < 0)
return "offset must be zero or greater.";
if (Limit < 1 || Limit > MaxLimit)
return $"limit must be between 1 and {MaxLimit}.";
return DrawingLibraryRules.TextError("search", NormalizedSearch, MaxSearchLength)
?? DrawingLibraryRules.TextError("customer", Customer ?? "", DrawingLibraryRules.MaxTextLength)
?? DrawingLibraryRules.TextError("materialName", MaterialName ?? "", DrawingLibraryRules.MaxTextLength)
?? DrawingLibraryRules.TextError("materialGrade", MaterialGrade ?? "", DrawingLibraryRules.MaxTextLength);
}
}