using System.Text.Json.Serialization;

namespace Mbproxy.Admin;

// ── Wire DTOs for GET /status.json ───────────────────────────────────────────
// Field names must match docs/Operations/StatusPage.md tables EXACTLY (camelCase via
// JsonKnownNamingPolicy.CamelCase on the source-gen context).

/// <summary>
/// Top-level response envelope for <c>GET /status.json</c>.
/// </summary>
public sealed record StatusResponse(
    ServiceFields Service,
    ListenersAggregate Listeners,
    IReadOnlyList<PlcStatus> Plcs);

/// <summary>Service-wide identity and reload counters.</summary>
public sealed record ServiceFields(
    long UptimeSeconds,
    string Version,
    DateTimeOffset? ConfigLastReloadUtc,
    int ConfigReloadCount,
    int ConfigReloadRejectedCount);

/// <summary>Aggregate listener state across all configured PLCs.</summary>
public sealed record ListenersAggregate(int Bound, int Configured);

/// <summary>Per-PLC status row.</summary>
public sealed record PlcStatus(
    string Name,
    string Host,
    int ListenPort,
    PlcListenerStatus Listener,
    PlcClientsStatus Clients,
    PlcPdusStatus Pdus,
    PlcBackendStatus Backend,
    PlcBytesStatus Bytes);

/// <summary>Listener state sub-object.</summary>
public sealed record PlcListenerStatus(
    string State,
    string? LastBindError,
    int RecoveryAttempts);

/// <summary>Connected-clients sub-object.</summary>
public sealed record PlcClientsStatus(
    int Connected,
    IReadOnlyList<ClientSnapshot> RemoteEndpoints);

/// <summary>Per-connection-pair snapshot for the status page.</summary>
public sealed record ClientSnapshot(
    string Remote,
    DateTimeOffset ConnectedAtUtc,
    long PdusForwarded);

/// <summary>PDU counters sub-object.</summary>
public sealed record PlcPdusStatus(
    long Forwarded,
    FcCounts ByFc,
    long RewrittenSlots,
    long PartialBcdWarnings,
    /// <summary>
    /// Count of BCD-rewriter slot decisions where the wire value was not a valid BCD
    /// nibble pattern (e.g. <c>0xABCD</c> at a tag address). The slot passes through
    /// unrewritten and this counter increments.
    /// </summary>
    long InvalidBcdWarnings);

/// <summary>Per-function-code request counts.</summary>
public sealed record FcCounts(
    long Fc03,
    long Fc04,
    long Fc06,
    long Fc16,
    long Other);

/// <summary>
/// Backend connect, exception, and multiplexer telemetry, including the in-flight
/// multiplexer fields (<c>InFlight</c>, <c>MaxInFlight</c>, <c>TxIdWraps</c>,
/// <c>DisconnectCascades</c>, <c>QueueDepth</c>), the read-coalescing counters
/// (<c>CoalescedHitCount</c>, <c>CoalescedMissCount</c>, <c>CoalescedResponseToDeadUpstream</c>),
/// and the response-cache counters (<c>CacheHitCount</c>, <c>CacheMissCount</c>,
/// <c>CacheInvalidations</c>, <c>CacheEntryCount</c>, <c>CacheBytes</c>).
///
/// <para>The dashboard-side derived ratios <c>coalescingRatio</c> and <c>cacheHitRatio</c>
/// are intentionally NOT carried on the wire — consumers compute <c>Hit / (Hit + Miss)</c>
/// from the raw counters.</para>
/// </summary>
public sealed record PlcBackendStatus(
    long ConnectsSuccess,
    long ConnectsFailed,
    ExceptionCounts ExceptionsByCode,
    double LastRoundTripMs,
    long InFlight,
    long MaxInFlight,
    long TxIdWraps,
    long DisconnectCascades,
    long QueueDepth,
    long CoalescedHitCount,
    long CoalescedMissCount,
    long CoalescedResponseToDeadUpstream,
    long CacheHitCount,
    long CacheMissCount,
    long CacheInvalidations,
    long CacheEntryCount,
    long CacheBytes,
    /// <summary>Backend keepalive heartbeat probes issued on idle backend sockets.</summary>
    long BackendHeartbeatsSent,
    /// <summary>Keepalive heartbeat probes that timed out (backend not answering).</summary>
    long BackendHeartbeatsFailed,
    /// <summary>Backend teardowns triggered by a failed keepalive heartbeat.</summary>
    long BackendIdleDisconnects);

/// <summary>Modbus exception counts by code.</summary>
public sealed record ExceptionCounts(
    long Code01,
    long Code02,
    long Code03,
    long Code04,
    /// <summary>
    /// Backend exceptions whose response code is not 01–04 (e.g. 0x06 Server Device
    /// Busy, 0x0B Gateway Target Failed To Respond, vendor-specific codes).
    /// </summary>
    long CodeOther);

/// <summary>Byte-transfer counters.</summary>
public sealed record PlcBytesStatus(
    long UpstreamIn,
    long UpstreamOut);

// ── Source-generation context ─────────────────────────────────────────────────
// TreatWarningsAsErrors is on, so the context must include every reachable type.

[JsonSerializable(typeof(StatusResponse))]
[JsonSourceGenerationOptions(
    WriteIndented = false,
    PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase)]
internal partial class StatusJsonContext : JsonSerializerContext;
