using ZB.MOM.WW.ScadaBridge.Commons.Entities.Templates;
using ZB.MOM.WW.ScadaBridge.Transport.Serialization;
namespace ZB.MOM.WW.ScadaBridge.Transport.Import;
///
/// The single source of truth for "has this template child changed?": the
/// field-for-field equality between a persisted template-child entity and its bundle
/// DTO. Every writable field is compared.
///
/// BOTH the Preview diff () and the
/// Overwrite child-sync (BundleImporter.SyncTemplate*Async) MUST route their
/// change detection through these methods so a Preview can never disagree with what
/// an Overwrite actually writes. Historically the two drifted — the diff omitted
/// ElementDataType, ExecutionTimeoutSeconds, the alarm on-trigger
/// script, and LockedInDerived — reporting Identical for artifacts an
/// Overwrite would in fact mutate. Adding a writable field to any template-child
/// entity means adding it here, once.
///
///
internal static class TemplateChildEquality
{
///
/// True when the persisted attribute equals its bundle DTO across every writable
/// field. The single source of truth for "attribute changed"; both ArtifactDiff
/// and SyncTemplateAttributesAsync call it.
///
/// Callers that normalise the DTO Value before comparing (the importer
/// normalises List values) pass a DTO whose Value is already normalised.
///
///
/// The persisted template attribute.
/// The bundle DTO to compare against.
/// true when every writable field matches.
public static bool AttributesEqual(TemplateAttribute e, TemplateAttributeDto i) =>
e.Value == i.Value
&& e.DataType == i.DataType
&& e.ElementDataType == i.ElementDataType
&& e.IsLocked == i.IsLocked
&& e.LockedInDerived == i.LockedInDerived
&& e.Description == i.Description
&& e.DataSourceReference == i.DataSourceReference;
///
/// True when the persisted alarm equals its bundle DTO across every writable
/// field, including the on-trigger script binding. Bundles reference the
/// on-trigger script by NAME (ids are environment-specific), so the caller
/// supplies — a resolver over the alarm's own
/// template scripts — to map the persisted OnTriggerScriptId FK back to a
/// name for comparison. The single source of truth for "alarm changed"; both
/// ArtifactDiff and SyncTemplateAlarmsAsync call it.
///
/// The persisted template alarm.
/// The bundle DTO to compare against.
/// Resolver mapping an on-trigger script id to its name.
/// true when every writable field matches.
public static bool AlarmsEqual(TemplateAlarm e, TemplateAlarmDto i, Func scriptNameById) =>
e.Description == i.Description
&& e.PriorityLevel == i.PriorityLevel
&& e.TriggerType == i.TriggerType
&& e.TriggerConfiguration == i.TriggerConfiguration
&& e.IsLocked == i.IsLocked
&& e.LockedInDerived == i.LockedInDerived
&& scriptNameById(e.OnTriggerScriptId) == i.OnTriggerScriptName;
///
/// True when the persisted script equals its bundle DTO across every writable
/// field. The single source of truth for "script changed"; both ArtifactDiff
/// (via DiffScriptChildren) and SyncTemplateScriptsAsync call it.
///
/// The persisted template script.
/// The bundle DTO to compare against.
/// true when every writable field matches.
public static bool ScriptsEqual(TemplateScript e, TemplateScriptDto i) =>
string.Equals(e.Code, i.Code, StringComparison.Ordinal)
&& e.TriggerType == i.TriggerType
&& e.TriggerConfiguration == i.TriggerConfiguration
&& e.ParameterDefinitions == i.ParameterDefinitions
&& e.ReturnDefinition == i.ReturnDefinition
&& e.IsLocked == i.IsLocked
&& e.LockedInDerived == i.LockedInDerived
&& e.MinTimeBetweenRuns == i.MinTimeBetweenRuns
&& e.ExecutionTimeoutSeconds == i.ExecutionTimeoutSeconds;
///
/// True when the persisted native alarm source equals its bundle DTO across every
/// writable field. The single source of truth for "native alarm source changed";
/// both ArtifactDiff and SyncTemplateNativeAlarmSourcesAsync call it.
///
/// The persisted native alarm source.
/// The bundle DTO to compare against.
/// true when every writable field matches.
public static bool NativeAlarmSourcesEqual(TemplateNativeAlarmSource e, TemplateNativeAlarmSourceDto i) =>
e.Description == i.Description
&& e.ConnectionName == i.ConnectionName
&& e.SourceReference == i.SourceReference
&& e.ConditionFilter == i.ConditionFilter
&& e.IsLocked == i.IsLocked
&& e.IsInherited == i.IsInherited
&& e.LockedInDerived == i.LockedInDerived;
///
/// Builds a resolver mapping a template script id → its name over the given
/// scripts, for the alarm on-trigger comparison. Returns null for a null id or an
/// id absent from the set.
///
/// The template scripts to index by id.
/// A resolver mapping a script id to its name, or null when unresolved.
public static Func ScriptNameResolver(IEnumerable scripts)
{
var byId = new Dictionary();
foreach (var s in scripts)
{
byId[s.Id] = s.Name;
}
return id => id is { } sid && byId.TryGetValue(sid, out var name) ? name : null;
}
}