using Microsoft.EntityFrameworkCore;
namespace ZB.MOM.WW.OtOpcUa.Configuration.Validation;
///
/// Materialises a from the live config DB so
/// can run against the current edit state at deploy time.
///
///
///
/// This is a whole-DB ("global") snapshot — every cluster's rows in one pass — which is
/// what the deploy path needs: the admin-operations actor snapshots and flattens the
/// entire config, not one cluster. The validator's rules compare entity-level
/// ClusterId fields against each other (e.g. namespace binding), so the snapshot's
/// own is not read by any rule and is left empty.
///
///
/// is a placeholder (the generation model was
/// dropped); no rule reads it. is empty because
/// there is no prior-generation table to diff against.
/// / are left null so the path-length rule uses its
/// conservative upper bound.
///
///
public static class DraftSnapshotFactory
{
/// Builds a from the current config DB rows.
/// The config DB context to read from.
/// Cancellation token.
/// A snapshot populated from the live DB, ready for .
public static async Task FromConfigDbAsync(OtOpcUaConfigDbContext db, CancellationToken ct = default)
=> new DraftSnapshot
{
// GenerationId=0 and null Enterprise/Site are intentional conservative fallbacks at the
// global-factory level: the path-length validator uses its upper bound, and cross-gen
// EquipmentUuid checks run in separate validator passes — no rule here reads these fields.
GenerationId = 0, // generation model dropped; placeholder (no rule reads it)
ClusterId = string.Empty, // global snapshot; rules compare entity ClusterId fields, not this
// v3: Namespace entity retired — no namespace list to materialize. The Raw tree
// (RawFolders/TagGroups) + UnsTagReferences feed the v3 rules (charset, tagname length,
// effective-leaf uniqueness).
RawFolders = await db.RawFolders.AsNoTracking().ToListAsync(ct),
DriverInstances = await db.DriverInstances.AsNoTracking().ToListAsync(ct),
Devices = await db.Devices.AsNoTracking().ToListAsync(ct),
TagGroups = await db.TagGroups.AsNoTracking().ToListAsync(ct),
UnsAreas = await db.UnsAreas.AsNoTracking().ToListAsync(ct),
UnsLines = await db.UnsLines.AsNoTracking().ToListAsync(ct),
Equipment = await db.Equipment.AsNoTracking().ToListAsync(ct),
Tags = await db.Tags.AsNoTracking().ToListAsync(ct),
UnsTagReferences = await db.UnsTagReferences.AsNoTracking().ToListAsync(ct),
VirtualTags = await db.VirtualTags.AsNoTracking().ToListAsync(ct),
ScriptedAlarms = await db.ScriptedAlarms.AsNoTracking().ToListAsync(ct),
Scripts = await db.Scripts.AsNoTracking().ToListAsync(ct),
PollGroups = await db.PollGroups.AsNoTracking().ToListAsync(ct),
PriorEquipment = [], // intentional: no prior-generation table to diff against at this level
ActiveReservations = await db.ExternalIdReservations
.AsNoTracking()
.Where(r => r.ReleasedAt == null) // active only — matches DraftSnapshot.ActiveReservations semantics
.ToListAsync(ct),
};
}