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

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

/// <summary>
/// Server-computed deployment counts per <see cref="DeploymentStatus"/>, backing the
/// Central UI deployment-status tiles.
///
/// <para>
/// The tiles used to be <c>_records.Count(r =&gt; r.Status == X)</c> over the
/// client-materialized full table — which meant the tiles were only correct because
/// the whole table was in memory, and paging the list would silently have reduced
/// them to "counts of the visible page". Computing them DB-side with one grouped
/// query decouples the tiles from the page.
/// </para>
/// </summary>
/// <param name="Pending">Rows in <see cref="DeploymentStatus.Pending"/>.</param>
/// <param name="InProgress">Rows in <see cref="DeploymentStatus.InProgress"/>.</param>
/// <param name="Success">Rows in <see cref="DeploymentStatus.Success"/>.</param>
/// <param name="Failed">Rows in <see cref="DeploymentStatus.Failed"/>.</param>
public sealed record DeploymentStatusCounts(
    int Pending,
    int InProgress,
    int Success,
    int Failed)
{
    /// <summary>All-zero counts — the shape returned for an empty or fully-excluded set.</summary>
    public static DeploymentStatusCounts Empty { get; } = new(0, 0, 0, 0);

    /// <summary>
    /// Total across every status. Summing is exact rather than approximate because
    /// <see cref="DeploymentStatus"/> is a closed set and every row carries one of
    /// its members.
    /// </summary>
    public int Total => Pending + InProgress + Success + Failed;

    /// <summary>Count for a single status.</summary>
    /// <param name="status">The status to read.</param>
    /// <returns>The count for <paramref name="status"/>; 0 for an unrecognised value.</returns>
    public int For(DeploymentStatus status) => status switch
    {
        DeploymentStatus.Pending => Pending,
        DeploymentStatus.InProgress => InProgress,
        DeploymentStatus.Success => Success,
        DeploymentStatus.Failed => Failed,
        _ => 0
    };

    /// <summary>
    /// Folds the <c>(status, count)</c> pairs a grouped database query returns into
    /// the fixed-shape record. Statuses absent from the grouping — no rows in that
    /// state — land as 0 rather than being missing.
    /// </summary>
    /// <param name="grouped">Per-status counts as returned by the aggregation query.</param>
    /// <returns>The folded counts.</returns>
    public static DeploymentStatusCounts FromGrouped(IEnumerable<(DeploymentStatus Status, int Count)> grouped)
    {
        ArgumentNullException.ThrowIfNull(grouped);

        var pending = 0;
        var inProgress = 0;
        var success = 0;
        var failed = 0;

        foreach (var (status, count) in grouped)
        {
            switch (status)
            {
                case DeploymentStatus.Pending: pending += count; break;
                case DeploymentStatus.InProgress: inProgress += count; break;
                case DeploymentStatus.Success: success += count; break;
                case DeploymentStatus.Failed: failed += count; break;
            }
        }

        return new DeploymentStatusCounts(pending, inProgress, success, failed);
    }
}
