using ZB.MOM.WW.ScadaBridge.Commons.Messages.Health;
using ZB.MOM.WW.ScadaBridge.Commons.Types;
using ZB.MOM.WW.ScadaBridge.Commons.Types.Enums;

namespace ZB.MOM.WW.ScadaBridge.HealthMonitoring;

/// <summary>
/// Interface for site-side health metric collection.
/// Consumed by Site Runtime actors to report errors, and by DCL to report connection health.
/// </summary>
public interface ISiteHealthCollector
{
    /// <summary>
    /// Increments the script error count.
    /// </summary>
    void IncrementScriptError();

    /// <summary>
    /// Increments the alarm error count.
    /// </summary>
    void IncrementAlarmError();

    /// <summary>
    /// Increments the dead letter count.
    /// </summary>
    void IncrementDeadLetter();

    /// <summary>
    /// WP3.1: increments the per-interval count of script/alarm runs SHED because
    /// <c>MaxConcurrentRunsPerScript</c> runs were already in flight for that script. A raw
    /// per-interval count like the script/alarm error counters — a sustained non-zero value
    /// means a trigger is firing faster than its script can complete. Every shed is counted
    /// here even when its accompanying site event is rate-limited away.
    /// Default interface implementation is a no-op so existing test fakes continue to
    /// compile without per-fake updates.
    /// </summary>
    void IncrementScriptRunShed()
    {
        // Default no-op so test fakes do not need to be updated.
    }

    /// <summary>
    /// Increment the per-interval count of
    /// <c>FallbackAuditWriter</c> primary failures. Bridged from the
    /// <c>IAuditWriteFailureCounter</c> binding registered via
    /// <c>AddAuditLogHealthMetricsBridge()</c>.
    /// </summary>
    void IncrementSiteAuditWriteFailures();

    /// <summary>
    /// Increment the per-interval count of
    /// payload-filter redactor over-redactions (header / body / SQL
    /// parameter stage throws routed to the
    /// <c>&lt;redacted: redactor error&gt;</c> marker). Bridged from the
    /// <c>IAuditRedactionFailureCounter</c> binding registered via
    /// <c>AddAuditLogHealthMetricsBridge()</c>.
    /// </summary>
    void IncrementAuditRedactionFailure();

    /// <summary>
    /// Replace the latest site-local
    /// audit-queue backlog snapshot (pending count, oldest pending row,
    /// on-disk file bytes) used by the next <see cref="CollectReport"/> call.
    /// Refreshed periodically by the <c>SiteAuditBacklogReporter</c> hosted
    /// service so each report carries a recent point-in-time view of the
    /// site→central drain health.
    /// </summary>
    /// <param name="snapshot">The audit backlog snapshot.</param>
    void UpdateSiteAuditBacklog(SiteAuditBacklogSnapshot snapshot);

    /// <summary>
    /// Updates the health status for a data connection.
    /// </summary>
    /// <param name="connectionName">The name of the connection.</param>
    /// <param name="health">The connection health status.</param>
    void UpdateConnectionHealth(string connectionName, ConnectionHealth health);

    /// <summary>
    /// Removes a connection from health tracking.
    /// </summary>
    /// <param name="connectionName">The name of the connection.</param>
    void RemoveConnection(string connectionName);

    /// <summary>
    /// Updates tag resolution metrics for a connection.
    /// </summary>
    /// <param name="connectionName">The name of the connection.</param>
    /// <param name="totalSubscribed">Total number of subscribed tags.</param>
    /// <param name="successfullyResolved">Number of successfully resolved tags.</param>
    void UpdateTagResolution(string connectionName, int totalSubscribed, int successfullyResolved);

    /// <summary>
    /// Updates the endpoint for a connection.
    /// </summary>
    /// <param name="connectionName">The name of the connection.</param>
    /// <param name="endpoint">The connection endpoint.</param>
    void UpdateConnectionEndpoint(string connectionName, string endpoint);

    /// <summary>
    /// Updates tag quality metrics for a connection.
    /// </summary>
    /// <param name="connectionName">The name of the connection.</param>
    /// <param name="good">Number of good quality tags.</param>
    /// <param name="bad">Number of bad quality tags.</param>
    /// <param name="uncertain">Number of uncertain quality tags.</param>
    void UpdateTagQuality(string connectionName, int good, int bad, int uncertain);

    /// <summary>
    /// Sets the store-and-forward buffer depths for all categories.
    /// </summary>
    /// <param name="depths">Dictionary mapping category names to their buffer depths.</param>
    void SetStoreAndForwardDepths(IReadOnlyDictionary<string, int> depths);

    /// <summary>
    /// Sets the counts of instances in each state.
    /// </summary>
    /// <param name="deployed">Number of deployed instances.</param>
    /// <param name="enabled">Number of enabled instances.</param>
    /// <param name="disabled">Number of disabled instances.</param>
    void SetInstanceCounts(int deployed, int enabled, int disabled);

    /// <summary>
    /// Sets the count of parked messages.
    /// </summary>
    /// <param name="count">The number of parked messages.</param>
    void SetParkedMessageCount(int count);

    /// <summary>
    /// Sets the age (seconds) of the oldest parked store-and-forward message for the
    /// next <see cref="CollectReport"/> call; <c>null</c> when no rows are parked.
    /// Point-in-time (not reset on collect), refreshed by the parked-count poller in
    /// <c>HealthReportSender</c>. Default interface implementation is a no-op so
    /// existing test fakes continue to compile without per-fake updates.
    /// </summary>
    /// <param name="ageSeconds">Age in seconds of the oldest parked row, or <c>null</c> if none are parked.</param>
    void SetOldestParkedMessageAgeSeconds(double? ageSeconds)
    {
        // Default no-op so test fakes do not need to be updated.
    }

    /// <summary>
    /// Replace the latest cumulative
    /// site-event-log write-failure count (SQLite error, disk full,
    /// bounded-queue overflow drop) used by the next <see cref="CollectReport"/>
    /// call. Refreshed periodically by the <c>SiteEventLogFailureCountReporter</c>
    /// hosted service. Point-in-time: the value is NOT reset on
    /// <see cref="CollectReport"/>; it carries forward until the next poller
    /// refresh. Default interface implementation is a no-op so existing test
    /// fakes continue to compile without per-fake updates.
    /// </summary>
    /// <param name="count">The cumulative failed-write count from <c>ISiteEventLogger.FailedWriteCount</c>.</param>
    void SetSiteEventLogWriteFailures(long count)
    {
        // Default no-op so test fakes do not need to be updated. The real
        // SiteHealthCollector overrides this with the Interlocked.Exchange store.
    }

    /// <summary>
    /// Replace the latest LocalDb replication status (peer-session connectivity and
    /// unacked oplog backlog) used by the next <see cref="CollectReport"/> call.
    /// Refreshed periodically by the <c>LocalDbReplicationStatusReporter</c> hosted
    /// service. Point-in-time: values are NOT reset on <see cref="CollectReport"/>.
    /// </summary>
    /// <param name="connected">
    /// Whether a sync session is currently running. <see langword="false"/> is the
    /// normal state on a node with no peer configured — replication ships default-OFF.
    /// </param>
    /// <param name="oplogBacklog">
    /// Unacked oplog entries, or <see langword="null"/> when unknown. Pass null through
    /// unchanged: a failed poll rendered as 0 would report a broken pair as healthy.
    /// </param>
    void SetLocalDbReplicationStatus(bool connected, long? oplogBacklog)
    {
        // Default no-op so test fakes do not need to be updated.
    }

    /// <summary>
    /// Replace the latest script-execution-scheduler gauges (queue depth, busy
    /// thread count, and age in seconds of the oldest in-flight script) used by
    /// the next <see cref="CollectReport"/> call. Refreshed periodically by the
    /// <c>ScriptSchedulerStatsReporter</c> hosted service. Point-in-time: the
    /// values are NOT reset on <see cref="CollectReport"/>. S2/UA5 — surfaces a
    /// saturated or stuck script-execution pool on the site health report.
    /// Default interface implementation is a no-op so existing test fakes continue
    /// to compile without per-fake updates.
    /// </summary>
    /// <param name="queueDepth">Script tasks waiting to run.</param>
    /// <param name="busyThreads">Worker threads currently executing a script.</param>
    /// <param name="oldestBusyAgeSeconds">Age (seconds) of the oldest in-flight script, or <c>null</c> when idle.</param>
    /// <param name="detachedThreads">
    /// WP3.1: worker threads the stuck-script watchdog has detached and replaced because
    /// their script wedged past its deadline plus grace, and which have not yet returned.
    /// A non-zero, non-draining value means script bodies are permanently blocking threads.
    /// </param>
    void SetScriptSchedulerStats(
        int queueDepth, int busyThreads, double? oldestBusyAgeSeconds, int detachedThreads = 0)
    {
        // Default no-op so test fakes do not need to be updated.
    }

    /// <summary>
    /// WP2.6d (arch-review misc — site stream alarm-vs-attribute shared buffer):
    /// replace the latest cumulative count of alarm state changes dropped from
    /// <c>SiteStreamManager</c>'s dedicated alarm publish queue, used by the next
    /// <see cref="CollectReport"/> call. Refreshed periodically by the site-side
    /// <c>SiteStreamAlarmDropReporter</c> hosted service. Point-in-time: NOT reset on
    /// <see cref="CollectReport"/>. Default interface implementation is a no-op so
    /// existing test fakes continue to compile without per-fake updates.
    /// </summary>
    /// <param name="count">The cumulative dropped-alarm count from <c>SiteStreamManager.AlarmPublishDroppedCount</c>.</param>
    void SetSiteStreamAlarmDropCount(long count)
    {
        // Default no-op so test fakes do not need to be updated. The real
        // SiteHealthCollector overrides this with the Interlocked.Exchange store.
    }

    /// <summary>
    /// Sets the hostname of this node.
    /// </summary>
    /// <param name="hostname">The node hostname.</param>
    void SetNodeHostname(string hostname);

    /// <summary>
    /// Sets the list of cluster nodes.
    /// </summary>
    /// <param name="nodes">The list of cluster node statuses.</param>
    void SetClusterNodes(IReadOnlyList<Commons.Messages.Health.NodeStatus> nodes);

    /// <summary>
    /// Sets whether this node is the active node in the cluster.
    /// </summary>
    /// <param name="isActive">True if this node is active, false otherwise.</param>
    void SetActiveNode(bool isActive);

    /// <summary>
    /// Gets whether this node is the active node in the cluster.
    /// </summary>
    bool IsActiveNode { get; }

    /// <summary>
    /// Collects and returns a health report for a site.
    /// </summary>
    /// <param name="siteId">The site identifier.</param>
    /// <returns>A health report for the specified site.</returns>
    SiteHealthReport CollectReport(string siteId);

    /// <summary>
    /// Atomically add back the given per-interval error
    /// counts into the collector's accumulators. Called by the report sender
    /// when transport delivery of a freshly-collected report fails, so the
    /// counts that <see cref="CollectReport"/> already drained roll forward
    /// into the next report rather than being silently lost. Concurrent
    /// increments arriving between the failed Send and this restore are
    /// preserved — <c>Interlocked.Add</c> sums correctly with them. The
    /// default interface implementation is a no-op so existing test fakes
    /// (the only implementations outside <see cref="SiteHealthCollector"/>)
    /// continue to compile without per-fake updates; production callers see
    /// the real behaviour via the concrete class.
    /// </summary>
    /// <param name="scriptErrors">Script error count to add back.</param>
    /// <param name="alarmErrors">Alarm evaluation error count to add back.</param>
    /// <param name="deadLetters">Dead letter count to add back.</param>
    /// <param name="siteAuditWriteFailures">Site audit write failure count to add back.</param>
    /// <param name="auditRedactionFailures">Audit redaction failure count to add back.</param>
    void AddIntervalCounters(
        int scriptErrors,
        int alarmErrors,
        int deadLetters,
        int siteAuditWriteFailures,
        int auditRedactionFailures)
    {
        // Default no-op so test fakes do not need to be updated. The real
        // SiteHealthCollector overrides this with the Interlocked.Add restore.
    }
}
