using Microsoft.EntityFrameworkCore;
using ZB.MOM.WW.OtOpcUa.Configuration.Entities;

namespace ZB.MOM.WW.OtOpcUa.Configuration.Queries;

/// <summary>
/// Shared query for the cluster-scoped audit view. Audit rows reach <c>ConfigAuditLog</c> by two
/// paths that stamp different columns:
/// <list type="bullet">
/// <item>the bespoke stored-procedure path stamps <c>ClusterId</c> directly;</item>
/// <item>the structured <c>AuditWriterActor</c> path stamps <c>NodeId</c> (leaving
/// <c>ClusterId</c> null).</item>
/// </list>
/// A cluster-scoped view must surface both, so this query matches rows whose <c>ClusterId</c>
/// equals the cluster <em>or</em> whose <c>NodeId</c> belongs to a node in the cluster
/// (membership from <see cref="ClusterNode"/>: <c>NodeId → ClusterId</c>).
/// </summary>
public static class ClusterAuditQuery
{
    /// <summary>
    /// Returns the newest <paramref name="pageSize"/> audit rows visible for
    /// <paramref name="clusterId"/>, newest first. Executes one query to resolve the cluster's
    /// node IDs, then one filtered query against <c>ConfigAuditLog</c>.
    /// </summary>
    /// <param name="db">The config database context.</param>
    /// <param name="clusterId">The cluster whose audit rows to fetch.</param>
    /// <param name="pageSize">Maximum number of rows to return.</param>
    /// <param name="ct">Cancellation token.</param>
    /// <returns>The matching audit rows, newest first.</returns>
    public static async Task<List<ConfigAuditLog>> ForClusterAsync(
        OtOpcUaConfigDbContext db, string clusterId, int pageSize, CancellationToken ct = default)
    {
        var nodeIds = await db.ClusterNodes.AsNoTracking()
            .Where(n => n.ClusterId == clusterId)
            .Select(n => n.NodeId)
            .ToListAsync(ct);

        return await db.ConfigAuditLogs.AsNoTracking()
            .Where(a => a.ClusterId == clusterId
                || (a.ClusterId == null && a.NodeId != null && nodeIds.Contains(a.NodeId)))
            .OrderByDescending(a => a.Timestamp)
            .Take(pageSize)
            .ToListAsync(ct);
    }
}
