using System.Collections.Concurrent;

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

/// <summary>
///     Process-singleton tracker of live resilience counters per
///     <c>(DriverInstanceId, HostName)</c>. Populated by the CapabilityInvoker and the
///     MemoryTracking layer; consumed by a HostedService that periodically persists a
///     snapshot to the <c>DriverInstanceResilienceStatus</c> table for Admin <c>/hosts</c>.
/// </summary>
/// <remarks>
///     Per Phase 6.1 Stream E. No DB dependency here — the tracker is pure in-memory so
///     tests can exercise it without EF Core or SQL Server. The HostedService that writes
///     snapshots lives in the Server project (Stream E.2); the actual SignalR push + Blazor
///     page refresh (E.3) lands in a follow-up visual-review PR.
/// </remarks>
public sealed class DriverResilienceStatusTracker
{
    private readonly ConcurrentDictionary<StatusKey, ResilienceStatusSnapshot> _status = new();

    /// <summary>Record a Polly pipeline failure for <paramref name="hostName"/>.</summary>
    /// <param name="driverInstanceId">The driver instance identifier.</param>
    /// <param name="hostName">The host name.</param>
    /// <param name="utcNow">The UTC timestamp of the failure event.</param>
    public void RecordFailure(string driverInstanceId, string hostName, DateTime utcNow)
    {
        var key = new StatusKey(driverInstanceId, hostName);
        _status.AddOrUpdate(key,
            _ => new ResilienceStatusSnapshot { ConsecutiveFailures = 1, LastSampledUtc = utcNow },
            (_, existing) => existing with
            {
                ConsecutiveFailures = existing.ConsecutiveFailures + 1,
                LastSampledUtc = utcNow,
            });
    }

    /// <summary>Reset the consecutive-failure count on a successful pipeline execution.</summary>
    /// <param name="driverInstanceId">The driver instance identifier.</param>
    /// <param name="hostName">The host name.</param>
    /// <param name="utcNow">The UTC timestamp of the success event.</param>
    public void RecordSuccess(string driverInstanceId, string hostName, DateTime utcNow)
    {
        var key = new StatusKey(driverInstanceId, hostName);
        _status.AddOrUpdate(key,
            _ => new ResilienceStatusSnapshot { ConsecutiveFailures = 0, LastSampledUtc = utcNow },
            (_, existing) => existing with
            {
                ConsecutiveFailures = 0,
                LastSampledUtc = utcNow,
            });
    }

    /// <summary>Record a circuit-breaker open event.</summary>
    /// <param name="driverInstanceId">The driver instance identifier.</param>
    /// <param name="hostName">The host name.</param>
    /// <param name="utcNow">The UTC timestamp of the breaker open event.</param>
    public void RecordBreakerOpen(string driverInstanceId, string hostName, DateTime utcNow)
    {
        var key = new StatusKey(driverInstanceId, hostName);
        _status.AddOrUpdate(key,
            _ => new ResilienceStatusSnapshot { LastBreakerOpenUtc = utcNow, LastSampledUtc = utcNow },
            (_, existing) => existing with { LastBreakerOpenUtc = utcNow, LastSampledUtc = utcNow });
    }

    /// <summary>Record a process recycle event (Tier C only).</summary>
    /// <param name="driverInstanceId">The driver instance identifier.</param>
    /// <param name="hostName">The host name.</param>
    /// <param name="utcNow">The UTC timestamp of the recycle event.</param>
    public void RecordRecycle(string driverInstanceId, string hostName, DateTime utcNow)
    {
        var key = new StatusKey(driverInstanceId, hostName);
        _status.AddOrUpdate(key,
            _ => new ResilienceStatusSnapshot { LastRecycleUtc = utcNow, LastSampledUtc = utcNow },
            (_, existing) => existing with { LastRecycleUtc = utcNow, LastSampledUtc = utcNow });
    }

    /// <summary>Capture / update the MemoryTracking-supplied baseline + current footprint.</summary>
    /// <param name="driverInstanceId">The driver instance identifier.</param>
    /// <param name="hostName">The host name.</param>
    /// <param name="baselineBytes">The baseline footprint in bytes.</param>
    /// <param name="currentBytes">The current footprint in bytes.</param>
    /// <param name="utcNow">The UTC timestamp of the footprint update.</param>
    public void RecordFootprint(string driverInstanceId, string hostName, long baselineBytes, long currentBytes, DateTime utcNow)
    {
        var key = new StatusKey(driverInstanceId, hostName);
        _status.AddOrUpdate(key,
            _ => new ResilienceStatusSnapshot
            {
                BaselineFootprintBytes = baselineBytes,
                CurrentFootprintBytes = currentBytes,
                LastSampledUtc = utcNow,
            },
            (_, existing) => existing with
            {
                BaselineFootprintBytes = baselineBytes,
                CurrentFootprintBytes = currentBytes,
                LastSampledUtc = utcNow,
            });
    }

    /// <summary>
    ///     Record the entry of a capability call for this (instance, host). Increments the
    ///     in-flight counter used as the <see cref="ResilienceStatusSnapshot.CurrentInFlight"/>
    ///     surface (a cheap stand-in for Polly bulkhead depth). Paired with
    ///     <see cref="RecordCallComplete"/>; callers use try/finally.
    /// </summary>
    /// <param name="driverInstanceId">The driver instance identifier.</param>
    /// <param name="hostName">The host name.</param>
    public void RecordCallStart(string driverInstanceId, string hostName)
    {
        var key = new StatusKey(driverInstanceId, hostName);
        _status.AddOrUpdate(key,
            _ => new ResilienceStatusSnapshot { CurrentInFlight = 1 },
            (_, existing) => existing with { CurrentInFlight = existing.CurrentInFlight + 1 });
    }

    /// <summary>Paired with <see cref="RecordCallStart"/> — decrements the in-flight counter.</summary>
    /// <param name="driverInstanceId">The driver instance identifier.</param>
    /// <param name="hostName">The host name.</param>
    public void RecordCallComplete(string driverInstanceId, string hostName)
    {
        var key = new StatusKey(driverInstanceId, hostName);
        _status.AddOrUpdate(key,
            _ => new ResilienceStatusSnapshot { CurrentInFlight = 0 }, // start-without-complete shouldn't happen; clamp to 0
            (_, existing) => existing with { CurrentInFlight = Math.Max(0, existing.CurrentInFlight - 1) });
    }

    /// <summary>Snapshot of a specific (instance, host) pair; null if no counters recorded yet.</summary>
    /// <param name="driverInstanceId">The driver instance identifier.</param>
    /// <param name="hostName">The host name.</param>
    public ResilienceStatusSnapshot? TryGet(string driverInstanceId, string hostName) =>
        _status.TryGetValue(new StatusKey(driverInstanceId, hostName), out var snapshot) ? snapshot : null;

    /// <summary>Copy of every currently-tracked (instance, host, snapshot) triple. Safe under concurrent writes.</summary>
    public IReadOnlyList<(string DriverInstanceId, string HostName, ResilienceStatusSnapshot Snapshot)> Snapshot() =>
        _status.Select(kvp => (kvp.Key.DriverInstanceId, kvp.Key.HostName, kvp.Value)).ToList();

    private readonly record struct StatusKey(string DriverInstanceId, string HostName);
}

/// <summary>Snapshot of the resilience counters for one <c>(DriverInstanceId, HostName)</c> pair.</summary>
public sealed record ResilienceStatusSnapshot
{
    /// <summary>Gets the number of consecutive pipeline failures.</summary>
    public int ConsecutiveFailures { get; init; }
    /// <summary>Gets the UTC timestamp of the last circuit-breaker open event, if any.</summary>
    public DateTime? LastBreakerOpenUtc { get; init; }
    /// <summary>Gets the UTC timestamp of the last recycle event, if any.</summary>
    public DateTime? LastRecycleUtc { get; init; }
    /// <summary>Gets the baseline process footprint in bytes.</summary>
    public long BaselineFootprintBytes { get; init; }
    /// <summary>Gets the current process footprint in bytes.</summary>
    public long CurrentFootprintBytes { get; init; }
    /// <summary>Gets the UTC timestamp when the counters were last updated.</summary>
    public DateTime LastSampledUtc { get; init; }

    /// <summary>
    ///     In-flight capability calls against this (instance, host). Bumped on call entry +
    ///     decremented on completion. Feeds <c>DriverInstanceResilienceStatus.CurrentBulkheadDepth</c>
    ///     for Admin <c>/hosts</c> — a cheap proxy for the Polly bulkhead depth until the full
    ///     telemetry observer lands.
    /// </summary>
    public int CurrentInFlight { get; init; }
}
