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

/// <summary>
///     The historian sink contract — where qualifying alarm events land. Phase 7 plan
///     decision #17: ingestion routes through Galaxy.Host's pipe so we reuse the
///     already-loaded <c>aahClientManaged</c> DLLs without loading 32-bit native code
///     in the main .NET 10 server. Tests use an in-memory fake; production uses
///     <see cref="SqliteStoreAndForwardSink"/>.
/// </summary>
/// <remarks>
///     <para>
///         <see cref="EnqueueAsync"/> is fire-and-forget from the engine's perspective —
///         the sink MUST NOT block the emitting thread. Production implementations
///         (<see cref="SqliteStoreAndForwardSink"/>) persist to a local SQLite queue
///         first, then drain asynchronously to the actual historian. Per Phase 7 plan
///         decision #16, failed downstream writes replay with exponential backoff;
///         operator actions are never blocked waiting on the historian.
///     </para>
///     <para>
///         <see cref="GetStatus"/> exposes queue depth + drain rate + last error
///         for the Admin UI <c>/alarms/historian</c> diagnostics page (Stream F).
///     </para>
/// </remarks>
public interface IAlarmHistorianSink
{
    /// <summary>Durably enqueue the event. Returns as soon as the queue row is committed.</summary>
    Task EnqueueAsync(AlarmHistorianEvent evt, CancellationToken cancellationToken);

    /// <summary>Snapshot of current queue depth + drain health.</summary>
    HistorianSinkStatus GetStatus();
}

/// <summary>No-op default for tests or deployments that don't historize alarms.</summary>
public sealed class NullAlarmHistorianSink : IAlarmHistorianSink
{
    public static readonly NullAlarmHistorianSink Instance = new();
    public Task EnqueueAsync(AlarmHistorianEvent evt, CancellationToken cancellationToken) => Task.CompletedTask;
    public HistorianSinkStatus GetStatus() => new(
        QueueDepth: 0,
        DeadLetterDepth: 0,
        LastDrainUtc: null,
        LastSuccessUtc: null,
        LastError: null,
        DrainState: HistorianDrainState.Disabled);
}

/// <summary>Diagnostic snapshot surfaced to the Admin UI + /healthz endpoints.</summary>
public sealed record HistorianSinkStatus(
    long QueueDepth,
    long DeadLetterDepth,
    DateTime? LastDrainUtc,
    DateTime? LastSuccessUtc,
    string? LastError,
    HistorianDrainState DrainState);

/// <summary>Where the drain worker is in its state machine.</summary>
public enum HistorianDrainState
{
    Disabled,
    Idle,
    Draining,
    BackingOff,
}

/// <summary>Signaled by the Galaxy.Host-side handler when it fails a batch — drain worker uses this to decide retry cadence.</summary>
public enum HistorianWriteOutcome
{
    /// <summary>Successfully persisted to the historian. Remove from queue.</summary>
    Ack,
    /// <summary>Transient failure (historian disconnected, timeout, busy). Leave queued; retry after backoff.</summary>
    RetryPlease,
    /// <summary>Permanent failure (malformed event, unrecoverable SDK error). Move to dead-letter table.</summary>
    PermanentFail,
}

/// <summary>What the drain worker delegates writes to — Stream G wires this to the Galaxy.Host IPC client.</summary>
public interface IAlarmHistorianWriter
{
    /// <summary>Push a batch of events to the historian. Returns one outcome per event, same order.</summary>
    Task<IReadOnlyList<HistorianWriteOutcome>> WriteBatchAsync(
        IReadOnlyList<AlarmHistorianEvent> batch, CancellationToken cancellationToken);
}
