using ZB.MOM.WW.ScadaBridge.Commons.Types.Enums;

namespace ZB.MOM.WW.ScadaBridge.Commons.Types.Deployment;

/// <summary>
/// Filter contract for the Central UI deployment-status list. Every dimension is
/// applied IN THE DATABASE — the page that consumes it previously loaded the whole
/// <c>DeploymentRecords</c> table plus every <c>Instance</c> and filtered, sorted,
/// counted and paged the result in the Blazor circuit's memory.
///
/// <para>
/// Message-contract evolution rule: additive-only. New dimensions go on the end
/// with a default of <see langword="null"/> meaning "unconstrained".
/// </para>
/// </summary>
/// <param name="Status">
/// Restrict to one deployment status, or <see langword="null"/> for all.
/// Deliberately IGNORED by the status-count aggregation
/// (<c>IDeploymentManagerRepository.GetDeploymentStatusCountsAsync</c>): the tiles
/// are the status breakdown of the otherwise-identically-filtered set, so a tile
/// must keep showing its own total while that same status is the active filter.
/// </param>
/// <param name="Search">
/// Free-text term matched (case-insensitively, as a substring) against the
/// instance unique name, the deployment id, the revision hash and the initiating
/// user. Blank/whitespace is treated as unconstrained.
/// </param>
/// <param name="SiteIdScope">
/// When non-null, restrict to deployments whose instance belongs to one of these
/// sites — the site-scoped user's permitted-site grant, pushed into the query
/// rather than intersected in memory. An EMPTY collection is a real, meaningful
/// filter (a user permitted no sites sees nothing) and must not be treated as
/// "no filter"; <see langword="null"/> is the system-wide case.
/// </param>
/// <param name="InstanceId">Restrict to one instance's deployment history, or <see langword="null"/> for all.</param>
public sealed record DeploymentListFilter(
    DeploymentStatus? Status = null,
    string? Search = null,
    IReadOnlyCollection<int>? SiteIdScope = null,
    int? InstanceId = null)
{
    /// <summary>
    /// The trimmed search term, or <see langword="null"/> when the term is absent
    /// or whitespace-only. Repository implementations use this rather than
    /// <see cref="Search"/> so a stray space never becomes a LIKE pattern that
    /// matches nothing.
    /// </summary>
    public string? NormalizedSearch =>
        string.IsNullOrWhiteSpace(Search) ? null : Search.Trim();
}
