using System.Collections.Immutable;

namespace ZB.MOM.WW.OtOpcUa.Core.ScriptedAlarms;

/// <summary>
///     Persistent per-alarm state tracked by the Part 9 state machine. Every field
///     carried here either participates in the state machine or contributes to the
///     audit trail required for GxP / 21 CFR Part 11 compliance.
/// </summary>
/// <remarks>
///     <para>
///         <see cref="Active"/> is re-derived from the predicate at startup — the engine
///         runs every alarm's predicate against current tag
///         values at <c>Load</c>, overriding whatever Active state is in the store.
///         Every other state field persists verbatim across server restarts so
///         operators don't re-ack active alarms after an outage + shelved alarms stay
///         shelved + audit history survives.
///     </para>
///     <para>
///         <see cref="Comments"/> is append-only; comments + ack/confirm user identities
///         are the audit surface regulators consume. The engine never rewrites past
///         entries. The runtime type is <see cref="ImmutableList{AlarmComment}"/> so
///         each append is O(log n) rather than the O(n) copy a plain
///         <c>IReadOnlyList&lt;AlarmComment&gt;</c> would force on every audit-producing
///         transition.
///     </para>
/// </remarks>
public sealed record AlarmConditionState(
    string AlarmId,
    AlarmEnabledState Enabled,
    AlarmActiveState Active,
    AlarmAckedState Acked,
    AlarmConfirmedState Confirmed,
    ShelvingState Shelving,
    DateTime LastTransitionUtc,
    DateTime? LastActiveUtc,
    DateTime? LastClearedUtc,
    DateTime? LastAckUtc,
    string? LastAckUser,
    string? LastAckComment,
    DateTime? LastConfirmUtc,
    string? LastConfirmUser,
    string? LastConfirmComment,
    ImmutableList<AlarmComment> Comments)
{
    /// <summary>Initial-load state for a newly registered alarm — everything in the "no-event" position.</summary>
    /// <param name="alarmId">The unique identifier for the alarm.</param>
    /// <param name="nowUtc">The current UTC timestamp.</param>
    /// <returns>A fresh AlarmConditionState with all fields initialized to default/inactive values.</returns>
    public static AlarmConditionState Fresh(string alarmId, DateTime nowUtc) => new(
        AlarmId: alarmId,
        Enabled: AlarmEnabledState.Enabled,
        Active: AlarmActiveState.Inactive,
        Acked: AlarmAckedState.Acknowledged,
        Confirmed: AlarmConfirmedState.Confirmed,
        Shelving: ShelvingState.Unshelved,
        LastTransitionUtc: nowUtc,
        LastActiveUtc: null,
        LastClearedUtc: null,
        LastAckUtc: null,
        LastAckUser: null,
        LastAckComment: null,
        LastConfirmUtc: null,
        LastConfirmUser: null,
        LastConfirmComment: null,
        Comments: ImmutableList<AlarmComment>.Empty);
}

/// <summary>
///     Shelving state — kind plus, for <see cref="ShelvingKind.Timed"/>, the UTC
///     timestamp at which the shelving auto-expires. The engine polls the timer on its
///     evaluation cadence; callers should not rely on millisecond-precision expiry.
/// </summary>
public sealed record ShelvingState(ShelvingKind Kind, DateTime? UnshelveAtUtc)
{
    public static readonly ShelvingState Unshelved = new(ShelvingKind.Unshelved, null);
}

/// <summary>
///     A single append-only audit record — acknowledgement / confirmation / explicit
///     comment / shelving action. Every entry carries a monotonic UTC timestamp plus the
///     user identity Phase 6.2 authenticated.
/// </summary>
/// <param name="TimestampUtc">When the action happened.</param>
/// <param name="User">OS / LDAP identity of the actor. For engine-internal events (shelving expiry, startup recovery) this is <c>"system"</c>.</param>
/// <param name="Kind">Human-readable classification — "Acknowledge", "Confirm", "ShelveOneShot", "ShelveTimed", "Unshelve", "AddComment", "Enable", "Disable", "AutoUnshelve".</param>
/// <param name="Text">Operator-supplied comment or engine-generated message.</param>
public sealed record AlarmComment(
    DateTime TimestampUtc,
    string User,
    string Kind,
    string Text);
