// Copyright 2018-2026 The NATS Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Adapted from server/accounts.go in the NATS server Go source.

using System.Globalization;
using System.Text.Json.Serialization;
using ZB.MOM.NatsNet.Server.Auth;
using ZB.MOM.NatsNet.Server.Internal;

namespace ZB.MOM.NatsNet.Server;

// ============================================================================
// AccountLimits — account-based limits
// Mirrors Go `limits` struct in server/accounts.go.
// ============================================================================

/// <summary>
/// Per-account connection and payload limits.
/// Mirrors Go <c>limits</c> struct in server/accounts.go.
/// </summary>
internal sealed class AccountLimits
{
    /// <summary>Maximum payload size (-1 = unlimited). Mirrors Go <c>mpay</c>.</summary>
    public int MaxPayload { get; set; } = -1;

    /// <summary>Maximum subscriptions (-1 = unlimited). Mirrors Go <c>msubs</c>.</summary>
    public int MaxSubscriptions { get; set; } = -1;

    /// <summary>Maximum connections (-1 = unlimited). Mirrors Go <c>mconns</c>.</summary>
    public int MaxConnections { get; set; } = -1;

    /// <summary>Maximum leaf nodes (-1 = unlimited). Mirrors Go <c>mleafs</c>.</summary>
    public int MaxLeafNodes { get; set; } = -1;

    /// <summary>When true, bearer tokens are not allowed. Mirrors Go <c>disallowBearer</c>.</summary>
    public bool DisallowBearer { get; set; }
}

// ============================================================================
// SConns — remote server connection/leafnode counters
// Mirrors Go `sconns` struct in server/accounts.go.
// ============================================================================

/// <summary>
/// Tracks the number of client connections and leaf nodes for a remote server.
/// Mirrors Go <c>sconns</c> struct in server/accounts.go.
/// </summary>
internal sealed class SConns
{
    /// <summary>Number of client connections from the remote server. Mirrors Go <c>conns</c>.</summary>
    public int Conns;

    /// <summary>Number of leaf nodes from the remote server. Mirrors Go <c>leafs</c>.</summary>
    public int Leafs;
}

// ============================================================================
// ServiceRespType — service response type enum
// Mirrors Go `ServiceRespType` and its iota constants in server/accounts.go.
// ============================================================================

/// <summary>
/// The response type for an exported service.
/// Mirrors Go <c>ServiceRespType</c> in server/accounts.go.
/// </summary>
public enum ServiceRespType : byte
{
    /// <summary>A single response is expected. Default. Mirrors Go <c>Singleton</c>.</summary>
    Singleton = 0,

    /// <summary>Multiple responses are streamed. Mirrors Go <c>Streamed</c>.</summary>
    Streamed  = 1,

    /// <summary>Responses are sent in chunks. Mirrors Go <c>Chunked</c>.</summary>
    Chunked   = 2,
}

/// <summary>
/// Extension methods for <see cref="ServiceRespType"/>.
/// </summary>
public static class ServiceRespTypeExtensions
{
    /// <summary>
    /// Returns the string representation of the response type.
    /// Mirrors Go <c>ServiceRespType.String()</c>.
    /// </summary>
    public static string ToNatsString(this ServiceRespType rt) => rt switch
    {
        ServiceRespType.Singleton => "Singleton",
        ServiceRespType.Streamed  => "Streamed",
        ServiceRespType.Chunked   => "Chunked",
        _                         => "Unknown ServiceResType",
    };
}

// ============================================================================
// ExportAuth — export authorization configuration
// Mirrors Go `exportAuth` struct in server/accounts.go.
// ============================================================================

/// <summary>
/// Holds configured approvals or a flag indicating that an auth token is
/// required for import.
/// Mirrors Go <c>exportAuth</c> struct in server/accounts.go.
/// </summary>
internal class ExportAuth
{
    /// <summary>When true, an auth token is required to import this export. Mirrors Go <c>tokenReq</c>.</summary>
    public bool TokenRequired { get; set; }

    /// <summary>
    /// Position in the subject token where the account name appears (for
    /// public exports that embed the importing account name).
    /// Mirrors Go <c>accountPos</c>.
    /// </summary>
    public uint AccountPosition { get; set; }

    /// <summary>
    /// Accounts explicitly approved to import this export.
    /// Key is the account name. Mirrors Go <c>approved</c>.
    /// </summary>
    public Dictionary<string, Account>? Approved { get; set; }

    /// <summary>
    /// Accounts whose activations have been revoked.
    /// Key is the account name, value is the revocation timestamp (Unix ns).
    /// Mirrors Go <c>actsRevoked</c>.
    /// </summary>
    public Dictionary<string, long>? ActivationsRevoked { get; set; }
}

// ============================================================================
// StreamExport — exported stream descriptor
// Mirrors Go `streamExport` struct in server/accounts.go.
// ============================================================================

/// <summary>
/// Describes a stream exported by an account.
/// Mirrors Go <c>streamExport</c> struct in server/accounts.go.
/// </summary>
internal sealed class StreamExport : ExportAuth
{
    // No additional fields beyond ExportAuth for now.
    // Full implementation in session 11 (accounts.go).
}

// ============================================================================
// InternalServiceLatency — service latency tracking configuration
// Mirrors Go `serviceLatency` struct in server/accounts.go.
// ============================================================================

/// <summary>
/// Configuration for service latency tracking on an exported service.
/// Mirrors Go <c>serviceLatency</c> struct in server/accounts.go.
/// </summary>
internal sealed class InternalServiceLatency
{
    /// <summary>
    /// Sampling percentage (1–100), or 0 to indicate triggered by header.
    /// Mirrors Go <c>sampling int8</c>.
    /// </summary>
    public int Sampling { get; set; }

    /// <summary>Subject to publish latency metrics to. Mirrors Go <c>subject</c>.</summary>
    public string Subject { get; set; } = string.Empty;
}

// ============================================================================
// ServiceExportEntry — exported service descriptor
// Mirrors Go `serviceExport` struct in server/accounts.go.
// ============================================================================

/// <summary>
/// Describes a service exported by an account with additional configuration
/// for response type, latency tracking, and timers.
/// Mirrors Go <c>serviceExport</c> struct in server/accounts.go.
/// </summary>
internal sealed class ServiceExportEntry : ExportAuth
{
    /// <summary>Account that owns this export. Mirrors Go <c>acc</c>.</summary>
    public Account? Account { get; set; }

    /// <summary>Response type (Singleton, Streamed, Chunked). Mirrors Go <c>respType</c>.</summary>
    public ServiceRespType ResponseType { get; set; } = ServiceRespType.Singleton;

    /// <summary>Latency tracking configuration, or null if disabled. Mirrors Go <c>latency</c>.</summary>
    public InternalServiceLatency? Latency { get; set; }

    /// <summary>
    /// Timer used to collect response-latency measurements.
    /// Mirrors Go <c>rtmr *time.Timer</c>.
    /// </summary>
    public Timer? ResponseTimer { get; set; }

    /// <summary>
    /// Threshold duration for service responses.
    /// Mirrors Go <c>respThresh time.Duration</c>.
    /// </summary>
    public TimeSpan ResponseThreshold { get; set; }

    /// <summary>
    /// When true, tracing is allowed past the account boundary for this export.
    /// Mirrors Go <c>atrc</c> (allow_trace).
    /// </summary>
    public bool AllowTrace { get; set; }
}

// ============================================================================
// ExportMap — tracks exported streams and services for an account
// Mirrors Go `exportMap` struct in server/accounts.go.
// ============================================================================

/// <summary>
/// Tracks all stream exports, service exports, and response mappings for an account.
/// Mirrors Go <c>exportMap</c> struct in server/accounts.go.
/// </summary>
internal sealed class ExportMap
{
    /// <summary>
    /// Exported streams keyed by subject pattern.
    /// Mirrors Go <c>streams map[string]*streamExport</c>.
    /// </summary>
    public Dictionary<string, StreamExport>? Streams { get; set; }

    /// <summary>
    /// Exported services keyed by subject pattern.
    /// Mirrors Go <c>services map[string]*serviceExport</c>.
    /// </summary>
    public Dictionary<string, ServiceExportEntry>? Services { get; set; }

    /// <summary>
    /// In-flight response service imports keyed by reply subject.
    /// Mirrors Go <c>responses map[string]*serviceImport</c>.
    /// </summary>
    public Dictionary<string, ServiceImportEntry>? Responses { get; set; }
}

// ============================================================================
// ImportMap — tracks imported streams and services for an account
// Mirrors Go `importMap` struct in server/accounts.go.
// ============================================================================

/// <summary>
/// Tracks all stream imports, service imports, and reverse-response maps.
/// Mirrors Go <c>importMap</c> struct in server/accounts.go.
/// </summary>
internal sealed class ImportMap
{
    /// <summary>
    /// Imported streams (ordered list).
    /// Mirrors Go <c>streams []*streamImport</c>.
    /// </summary>
    public List<StreamImportEntry>? Streams { get; set; }

    /// <summary>
    /// Imported services keyed by subject pattern; each key may have
    /// multiple import entries (e.g. fan-out imports).
    /// Mirrors Go <c>services map[string][]*serviceImport</c>.
    /// </summary>
    public Dictionary<string, List<ServiceImportEntry>>? Services { get; set; }

    /// <summary>
    /// Reverse-response map used to clean up singleton service imports.
    /// Mirrors Go <c>rrMap map[string][]*serviceRespEntry</c>.
    /// </summary>
    public Dictionary<string, List<ServiceRespEntry>>? ReverseResponseMap { get; set; }
}

// ============================================================================
// StreamImportEntry — an imported stream mapping
// Mirrors Go `streamImport` struct in server/accounts.go.
// ============================================================================

/// <summary>
/// An imported stream from another account, with optional subject remapping.
/// Mirrors Go <c>streamImport</c> struct in server/accounts.go.
/// </summary>
internal sealed class StreamImportEntry
{
    /// <summary>Account providing the stream. Mirrors Go <c>acc</c>.</summary>
    public Account? Account { get; set; }

    /// <summary>Source subject on the exporting account. Mirrors Go <c>from</c>.</summary>
    public string From { get; set; } = string.Empty;

    /// <summary>Destination subject on the importing account. Mirrors Go <c>to</c>.</summary>
    public string To { get; set; } = string.Empty;

    /// <summary>
    /// Subject transform applied to the source subject.
    /// Mirrors Go <c>tr *subjectTransform</c>.
    /// Stubbed as <see cref="ISubjectTransformer"/> until the transform
    /// engine is wired in.
    /// </summary>
    public ISubjectTransformer? Transform { get; set; }

    /// <summary>
    /// Reverse transform for reply subjects.
    /// Mirrors Go <c>rtr *subjectTransform</c>.
    /// </summary>
    public ISubjectTransformer? ReverseTransform { get; set; }

    /// <summary>
    /// JWT import claim that authorized this import.
    /// Mirrors Go <c>claim *jwt.Import</c>.
    /// Stubbed as <c>object?</c> until JWT integration is complete (session 11).
    /// </summary>
    public object? Claim { get; set; }

    /// <summary>
    /// When true, use the published subject instead of <see cref="To"/>.
    /// Mirrors Go <c>usePub</c>.
    /// </summary>
    public bool UsePublishedSubject { get; set; }

    /// <summary>Whether this import is considered invalid. Mirrors Go <c>invalid</c>.</summary>
    public bool Invalid { get; set; }

    /// <summary>
    /// When true, tracing is allowed past the account boundary.
    /// Mirrors Go <c>atrc</c> (allow_trace).
    /// </summary>
    public bool AllowTrace { get; set; }
}

// ============================================================================
// ServiceImportEntry — an imported service mapping
// Mirrors Go `serviceImport` struct in server/accounts.go.
// ============================================================================

/// <summary>
/// An imported service from another account, with response routing and
/// latency tracking state.
/// Mirrors Go <c>serviceImport</c> struct in server/accounts.go.
/// </summary>
internal sealed class ServiceImportEntry
{
    /// <summary>Account providing the service. Mirrors Go <c>acc</c>.</summary>
    public Account? Account { get; set; }

    /// <summary>
    /// JWT import claim that authorized this import.
    /// Mirrors Go <c>claim *jwt.Import</c>.
    /// Stubbed as <c>object?</c> until JWT integration is complete (session 11).
    /// </summary>
    public object? Claim { get; set; }

    /// <summary>Parent service export entry. Mirrors Go <c>se *serviceExport</c>.</summary>
    public ServiceExportEntry? ServiceExport { get; set; }

    /// <summary>
    /// Subscription ID byte slice for cleanup.
    /// Mirrors Go <c>sid []byte</c>.
    /// </summary>
    public byte[]? SubscriptionId { get; set; }

    /// <summary>Source subject on the importing account. Mirrors Go <c>from</c>.</summary>
    public string From { get; set; } = string.Empty;

    /// <summary>Destination subject on the exporting account. Mirrors Go <c>to</c>.</summary>
    public string To { get; set; } = string.Empty;

    /// <summary>
    /// Subject transform applied when routing requests.
    /// Mirrors Go <c>tr *subjectTransform</c>.
    /// Stubbed as <see cref="ISubjectTransformer"/> until transform engine is wired in.
    /// </summary>
    public ISubjectTransformer? Transform { get; set; }

    /// <summary>
    /// Timestamp (Unix nanoseconds) when the import request was created.
    /// Used for latency tracking. Mirrors Go <c>ts int64</c>.
    /// </summary>
    public long Timestamp { get; set; }

    /// <summary>Response type for this service import. Mirrors Go <c>rt ServiceRespType</c>.</summary>
    public ServiceRespType ResponseType { get; set; } = ServiceRespType.Singleton;

    /// <summary>Latency tracking configuration. Mirrors Go <c>latency *serviceLatency</c>.</summary>
    public InternalServiceLatency? Latency { get; set; }

    /// <summary>
    /// First-leg latency measurement (requestor side).
    /// Mirrors Go <c>m1 *ServiceLatency</c>.
    /// </summary>
    public ServiceLatency? M1 { get; set; }

    /// <summary>
    /// Client connection that sent the original request.
    /// Mirrors Go <c>rc *client</c>.
    /// </summary>
    public ClientConnection? RequestingClient { get; set; }

    /// <summary>
    /// When true, use the published subject instead of <see cref="To"/>.
    /// Mirrors Go <c>usePub</c>.
    /// </summary>
    public bool UsePublishedSubject { get; set; }

    /// <summary>
    /// When true, this import entry represents a pending response rather
    /// than an originating request.
    /// Mirrors Go <c>response</c>.
    /// </summary>
    public bool IsResponse { get; set; }

    /// <summary>Whether this import is considered invalid. Mirrors Go <c>invalid</c>.</summary>
    public bool Invalid { get; set; }

    /// <summary>
    /// When true, the requestor's <see cref="ClientInfo"/> is shared with
    /// the responder. Mirrors Go <c>share</c>.
    /// </summary>
    public bool Share { get; set; }

    /// <summary>Whether latency tracking is active. Mirrors Go <c>tracking</c>.</summary>
    public bool Tracking { get; set; }

    /// <summary>Whether a response was delivered to the requestor. Mirrors Go <c>didDeliver</c>.</summary>
    public bool DidDeliver { get; set; }

    /// <summary>
    /// When true, tracing is allowed past the account boundary (inherited
    /// from the service export). Mirrors Go <c>atrc</c>.
    /// </summary>
    public bool AllowTrace { get; set; }

    /// <summary>
    /// Headers from the original request, used when latency is triggered by
    /// a header. Mirrors Go <c>trackingHdr http.Header</c>.
    /// </summary>
    public Dictionary<string, string[]>? TrackingHeader { get; set; }
}

// ============================================================================
// ServiceRespEntry — reverse-response map entry
// Mirrors Go `serviceRespEntry` struct in server/accounts.go.
// ============================================================================

/// <summary>
/// Records a service import mapping for reverse-response-map cleanup.
/// Mirrors Go <c>serviceRespEntry</c> struct in server/accounts.go.
/// </summary>
internal sealed class ServiceRespEntry
{
    /// <summary>Account that owns the service import. Mirrors Go <c>acc</c>.</summary>
    public Account? Account { get; set; }

    /// <summary>
    /// The mapped subscription subject used for the response.
    /// Mirrors Go <c>msub</c>.
    /// </summary>
    public string MappedSubject { get; set; } = string.Empty;
}

// ============================================================================
// MapDest — public API for weighted subject mappings
// Mirrors Go `MapDest` struct in server/accounts.go.
// ============================================================================

/// <summary>
/// Describes a weighted mapping destination for published subjects.
/// Mirrors Go <c>MapDest</c> struct in server/accounts.go.
/// </summary>
public sealed class MapDest
{
    [JsonPropertyName("subject")]
    public string Subject { get; set; } = string.Empty;

    [JsonPropertyName("weight")]
    public byte Weight { get; set; }

    [JsonPropertyName("cluster")]
    public string Cluster { get; set; } = string.Empty;

    /// <summary>
    /// Creates a new <see cref="MapDest"/> with the given subject and weight.
    /// Mirrors Go <c>NewMapDest</c>.
    /// </summary>
    public static MapDest New(string subject, byte weight) =>
        new() { Subject = subject, Weight = weight };
}

// ============================================================================
// Destination — internal weighted mapped destination
// Mirrors Go `destination` struct in server/accounts.go.
// ============================================================================

/// <summary>
/// Internal representation of a weighted mapped destination, holding a
/// transform and a weight.
/// Mirrors Go <c>destination</c> struct in server/accounts.go.
/// </summary>
internal sealed class Destination
{
    /// <summary>
    /// Transform that converts the source subject to the destination subject.
    /// Mirrors Go <c>tr *subjectTransform</c>.
    /// </summary>
    public ISubjectTransformer? Transform { get; set; }

    /// <summary>
    /// Relative weight (0–100). Mirrors Go <c>weight uint8</c>.
    /// </summary>
    public byte Weight { get; set; }
}

// ============================================================================
// SubjectMapping — internal subject mapping entry
// Mirrors Go `mapping` struct in server/accounts.go.
// Renamed from `mapping` to avoid collision with the C# keyword context.
// ============================================================================

/// <summary>
/// An internal entry describing how a source subject is remapped to one or
/// more weighted destinations, optionally scoped to specific clusters.
/// Mirrors Go <c>mapping</c> struct in server/accounts.go.
/// </summary>
internal sealed class SubjectMapping
{
    /// <summary>Source subject pattern. Mirrors Go <c>src</c>.</summary>
    public string Source { get; set; } = string.Empty;

    /// <summary>
    /// Whether the source contains wildcards.
    /// Mirrors Go <c>wc</c>.
    /// </summary>
    public bool HasWildcard { get; set; }

    /// <summary>
    /// Weighted destinations with no cluster scope.
    /// Mirrors Go <c>dests []*destination</c>.
    /// </summary>
    public List<Destination> Destinations { get; set; } = [];

    /// <summary>
    /// Per-cluster weighted destinations.
    /// Key is the cluster name. Mirrors Go <c>cdests map[string][]*destination</c>.
    /// </summary>
    public Dictionary<string, List<Destination>>? ClusterDestinations { get; set; }
}

// ============================================================================
// TypedEvent — base for server advisory events
// Mirrors Go `TypedEvent` struct in server/events.go.
// Included here because ServiceLatency embeds it.
// ============================================================================

/// <summary>
/// Base fields for a NATS typed event or advisory.
/// Mirrors Go <c>TypedEvent</c> struct in server/events.go.
/// </summary>
public class TypedEvent
{
    [JsonPropertyName("type")]
    public string Type { get; set; } = string.Empty;

    [JsonPropertyName("id")]
    public string Id { get; set; } = string.Empty;

    [JsonPropertyName("timestamp")]
    public DateTime Time { get; set; }
}

// ============================================================================
// ServiceLatency — public latency measurement event
// Mirrors Go `ServiceLatency` struct in server/accounts.go.
// ============================================================================

/// <summary>
/// The JSON message published to a latency-tracking subject when a service
/// request completes. Includes requestor and responder timing breakdowns.
/// Mirrors Go <c>ServiceLatency</c> struct in server/accounts.go.
/// </summary>
public sealed class ServiceLatency : TypedEvent
{
    [JsonPropertyName("status")]
    public int Status { get; set; }

    [JsonPropertyName("description")]
    public string Error { get; set; } = string.Empty;

    [JsonPropertyName("requestor")]
    public ClientInfo? Requestor { get; set; }

    [JsonPropertyName("responder")]
    public ClientInfo? Responder { get; set; }

    /// <summary>
    /// Headers from the original request that triggered latency measurement.
    /// Mirrors Go <c>RequestHeader http.Header</c>.
    /// </summary>
    [JsonPropertyName("header")]
    public Dictionary<string, string[]>? RequestHeader { get; set; }

    [JsonPropertyName("start")]
    public DateTime RequestStart { get; set; }

    /// <summary>Mirrors Go <c>ServiceLatency time.Duration</c> (nanoseconds).</summary>
    [JsonPropertyName("service")]
    public TimeSpan ServiceLatencyDuration { get; set; }

    /// <summary>Mirrors Go <c>SystemLatency time.Duration</c> (nanoseconds).</summary>
    [JsonPropertyName("system")]
    public TimeSpan SystemLatency { get; set; }

    /// <summary>Mirrors Go <c>TotalLatency time.Duration</c> (nanoseconds).</summary>
    [JsonPropertyName("total")]
    public TimeSpan TotalLatency { get; set; }

    /// <summary>
    /// Returns the sum of requestor RTT, responder RTT, and system latency.
    /// Mirrors Go <c>ServiceLatency.NATSTotalTime()</c>.
    /// </summary>
    public TimeSpan NATSTotalTime()
    {
        var requestorRtt  = Requestor?.Rtt  ?? TimeSpan.Zero;
        var responderRtt  = Responder?.Rtt  ?? TimeSpan.Zero;
        return requestorRtt + responderRtt + SystemLatency;
    }
}

// ============================================================================
// RemoteLatency — cross-server latency transport message
// Mirrors Go `remoteLatency` struct in server/accounts.go.
// ============================================================================

/// <summary>
/// Used to transport a responder-side latency measurement to the
/// requestor's server so the two halves can be merged.
/// Mirrors Go <c>remoteLatency</c> struct in server/accounts.go.
/// </summary>
internal sealed class RemoteLatency
{
    [JsonPropertyName("account")]
    public string Account { get; set; } = string.Empty;

    [JsonPropertyName("req_id")]
    public string RequestId { get; set; } = string.Empty;

    [JsonPropertyName("m2")]
    public ServiceLatency M2 { get; set; } = new();

    /// <summary>
    /// Private: response latency threshold used when deciding whether to
    /// send the remote measurement.
    /// Mirrors Go <c>respThresh time.Duration</c>.
    /// </summary>
    public TimeSpan ResponseThreshold { get; set; }
}

// ============================================================================
// RsiReason — reason for removing a response service import
// Mirrors Go `rsiReason` and its iota constants in server/accounts.go.
// ============================================================================

/// <summary>
/// The reason a response service import entry is being removed.
/// Mirrors Go <c>rsiReason</c> and its iota constants in server/accounts.go.
/// </summary>
internal enum RsiReason
{
    /// <summary>Normal completion. Mirrors Go <c>rsiOk</c>.</summary>
    Ok          = 0,

    /// <summary>Response was never delivered. Mirrors Go <c>rsiNoDelivery</c>.</summary>
    NoDelivery  = 1,

    /// <summary>Response timed out. Mirrors Go <c>rsiTimeout</c>.</summary>
    Timeout     = 2,
}

// ============================================================================
// Account-level constants
// Mirrors the const blocks in server/accounts.go.
// ============================================================================

/// <summary>
/// Constants related to account route-pool indexing and search depth.
/// </summary>
internal static class AccountConstants
{
    /// <summary>
    /// Sentinel value indicating the account has a dedicated route connection.
    /// Mirrors Go <c>accDedicatedRoute = -1</c>.
    /// </summary>
    public const int DedicatedRoute = -1;

    /// <summary>
    /// Sentinel value indicating the account is in the process of transitioning
    /// to a dedicated route.
    /// Mirrors Go <c>accTransitioningToDedicatedRoute = -2</c>.
    /// </summary>
    public const int TransitioningToDedicatedRoute = -2;

    /// <summary>
    /// Maximum depth for account cycle detection when following import chains.
    /// Mirrors Go <c>MaxAccountCycleSearchDepth = 1024</c>.
    /// </summary>
    public const int MaxCycleSearchDepth = 1024;
}

/// <summary>
/// Well-known header names and event type identifiers used by the account
/// service-latency and client-info subsystems.
/// </summary>
public static class AccountEventConstants
{
    /// <summary>
    /// Header name used to pass client metadata into a service request.
    /// Mirrors Go <c>ClientInfoHdr = "Nats-Request-Info"</c>.
    /// </summary>
    public const string ClientInfoHeader = "Nats-Request-Info";

    /// <summary>
    /// The default threshold (in nanoseconds, as a <see cref="TimeSpan"/>) below
    /// which a subscription-limit report is suppressed.
    /// Mirrors Go <c>defaultMaxSubLimitReportThreshold = int64(2 * time.Second)</c>.
    /// </summary>
    public static readonly TimeSpan DefaultMaxSubLimitReportThreshold = TimeSpan.FromSeconds(2);

    /// <summary>
    /// NATS event type identifier for <see cref="ServiceLatency"/> messages.
    /// Mirrors Go <c>ServiceLatencyType = "io.nats.server.metric.v1.service_latency"</c>.
    /// </summary>
    public const string ServiceLatencyType = "io.nats.server.metric.v1.service_latency";
}

// ============================================================================
// ServiceImportEntry extension methods
// Mirrors Go methods on *serviceImport (accounts.go).
// ============================================================================

/// <summary>
/// Extension methods for <see cref="ServiceImportEntry"/>.
/// </summary>
internal static class ServiceImportEntryExtensions
{
    /// <summary>
    /// Returns true when this import entry represents a pending response service import
    /// (as opposed to an originating request import).
    /// Mirrors Go <c>(si *serviceImport) isRespServiceImport() bool</c>.
    /// </summary>
    public static bool IsRespServiceImport(this ServiceImportEntry? si)
        => si != null && si.IsResponse;
}

// ============================================================================
// ServiceExportEntry extension methods — response threshold timer
// Mirrors Go methods on *serviceExport (accounts.go ~lines 2446-2507).
// ============================================================================

/// <summary>
/// Extension methods for <see cref="ServiceExportEntry"/> providing response
/// threshold timer management and expired-response cleanup.
/// </summary>
internal static class ServiceExportEntryExtensions
{
    /// <summary>
    /// Sets the response threshold timer on <paramref name="se"/> if not already set.
    /// The timer fires <see cref="ServiceExportEntry.ResponseThreshold"/> after being set,
    /// calling <see cref="CheckExpiredResponses"/>.
    /// Account lock must be held on entry.
    /// Mirrors Go <c>(se *serviceExport) setResponseThresholdTimer()</c>.
    /// </summary>
    public static void SetResponseThresholdTimer(this ServiceExportEntry se)
    {
        if (se.ResponseTimer != null)
            return; // already set

        se.ResponseTimer = new Timer(
            _ => se.CheckExpiredResponses(),
            null,
            se.ResponseThreshold,
            Timeout.InfiniteTimeSpan);
    }

    /// <summary>
    /// Stops and clears the response threshold timer.
    /// Returns true when the timer was stopped before it fired (or was already null).
    /// Account lock must be held on entry.
    /// Mirrors Go <c>(se *serviceExport) clearResponseThresholdTimer() bool</c>.
    /// </summary>
    public static bool ClearResponseThresholdTimer(this ServiceExportEntry se)
    {
        if (se.ResponseTimer == null)
            return true;

        se.ResponseTimer.Dispose();
        se.ResponseTimer = null;
        return true;
    }

    /// <summary>
    /// Iterates the owning account's pending response map, removes any entries
    /// belonging to <paramref name="se"/> whose timestamp is older than
    /// <see cref="ServiceExportEntry.ResponseThreshold"/>, then resets or clears
    /// the timer depending on whether responses remain.
    /// Mirrors Go <c>(se *serviceExport) checkExpiredResponses()</c>.
    /// </summary>
    public static void CheckExpiredResponses(this ServiceExportEntry se)
    {
        var acc = se.Account;
        if (acc == null)
        {
            se.ClearResponseThresholdTimer();
            return;
        }

        var expired = new List<ServiceImportEntry>();
        var minTs = DateTime.UtcNow.Ticks - se.ResponseThreshold.Ticks;

        int totalResponses;
        // Read lock to collect expired entries.
        acc.ReadLock();
        try
        {
            totalResponses = acc.GetResponseCount(se);
            foreach (var (_, si) in acc.GetResponseEntries())
            {
                if (si.ServiceExport == se && si.Timestamp <= minTs)
                    expired.Add(si);
            }
        }
        finally
        {
            acc.ReadUnlock();
        }

        foreach (var si in expired)
            acc.RemoveRespServiceImport(si, RsiReason.Timeout);

        totalResponses -= expired.Count;

        // Reset or clear the timer under a write lock.
        acc.WriteLock();
        try
        {
            if (totalResponses > 0 && se.ResponseTimer != null)
            {
                se.ResponseTimer.Change(se.ResponseThreshold, Timeout.InfiniteTimeSpan);
            }
            else
            {
                se.ClearResponseThresholdTimer();
            }
        }
        finally
        {
            acc.WriteUnlock();
        }
    }
}

// ============================================================================
// ServiceLatency extensions — merge and sanitize
// Mirrors Go functions in accounts.go ~lines 1354-1377.
// ============================================================================

/// <summary>
/// Extension and static helper methods for <see cref="ServiceLatency"/>.
/// </summary>
public static class ServiceLatencyExtensions
{
    /// <summary>
    /// Merges responder-side latency measurement <paramref name="m2"/> into the
    /// requestor-side measurement <paramref name="m1"/>, computing system overhead.
    /// Mirrors Go <c>(m1 *ServiceLatency) merge(m2 *ServiceLatency)</c>.
    /// </summary>
    public static void Merge(this ServiceLatency m1, ServiceLatency m2)
    {
        var rtt = m2.Responder?.Rtt ?? TimeSpan.Zero;
        m1.SystemLatency      = m1.ServiceLatencyDuration - (m2.ServiceLatencyDuration + rtt);
        m1.ServiceLatencyDuration = m2.ServiceLatencyDuration;
        m1.Responder          = m2.Responder;
        m1.Sanitize();
    }

    /// <summary>
    /// Clamps any negative latency durations to zero.
    /// Mirrors Go <c>sanitizeLatencyMetric(sl *ServiceLatency)</c>.
    /// </summary>
    public static void Sanitize(this ServiceLatency sl)
    {
        if (sl.ServiceLatencyDuration < TimeSpan.Zero)
            sl.ServiceLatencyDuration = TimeSpan.Zero;
        if (sl.SystemLatency < TimeSpan.Zero)
            sl.SystemLatency = TimeSpan.Zero;
    }

    /// <summary>
    /// Clamps any negative latency durations on <paramref name="sl"/> to zero.
    /// Static helper that mirrors Go <c>sanitizeLatencyMetric</c> as a standalone function.
    /// </summary>
    public static void SanitizeLatencyMetric(ServiceLatency sl) => sl.Sanitize();
}

// ============================================================================
// Tracing header builders
// Mirrors Go functions in accounts.go ~lines 2228-2276.
// ============================================================================

/// <summary>
/// Well-known distributed-tracing header keys used for sampling propagation.
/// Mirrors Go package-level vars <c>trcUber, trcCtx, trcB3, ...</c> in accounts.go.
/// </summary>
internal static class TracingHeaderKeys
{
    // Uber/Jaeger
    public const string UberTraceId    = "Uber-Trace-Id";
    // W3C TraceContext
    public const string TraceParent    = "Traceparent";
    public const string TraceState     = "Tracestate";
    // Zipkin B3 single-header
    public const string B3Single       = "B3";
    // Zipkin B3 multi-header
    public const string B3Sampled      = "X-B3-Sampled";
    public const string B3TraceId      = "X-B3-TraceId";
    public const string B3ParentSpanId = "X-B3-ParentSpanId";
    public const string B3SpanId       = "X-B3-SpanId";
    // Uber context baggage prefix
    public const string UberCtxPrefix  = "Uberctx-";
}

/// <summary>
/// Factory helpers for distributed-tracing header propagation dictionaries.
/// Mirrors Go <c>newB3Header</c>, <c>newUberHeader</c>, <c>newTraceCtxHeader</c>
/// and <c>shouldSample</c> in accounts.go.
/// </summary>
internal static class TracingHeaders
{
    /// <summary>
    /// Copies the Zipkin B3 multi-header keys from <paramref name="source"/> into a new header map.
    /// Mirrors Go <c>newB3Header(h http.Header) http.Header</c>.
    /// </summary>
    public static Dictionary<string, string[]> NewB3Header(Dictionary<string, string[]> source)
    {
        var result = new Dictionary<string, string[]>(StringComparer.OrdinalIgnoreCase);
        TryCopy(source, result, TracingHeaderKeys.B3Sampled);
        TryCopy(source, result, TracingHeaderKeys.B3TraceId);
        TryCopy(source, result, TracingHeaderKeys.B3ParentSpanId);
        TryCopy(source, result, TracingHeaderKeys.B3SpanId);
        return result;
    }

    /// <summary>
    /// Builds a new Uber/Jaeger header map with <paramref name="traceId"/> plus any
    /// <c>Uberctx-*</c> baggage headers copied from <paramref name="source"/>.
    /// Mirrors Go <c>newUberHeader(h http.Header, tId []string) http.Header</c>.
    /// </summary>
    public static Dictionary<string, string[]> NewUberHeader(
        Dictionary<string, string[]> source,
        string[] traceId)
    {
        var result = new Dictionary<string, string[]>(StringComparer.OrdinalIgnoreCase)
        {
            [TracingHeaderKeys.UberTraceId] = traceId,
        };

        foreach (var (k, v) in source)
        {
            if (k.StartsWith(TracingHeaderKeys.UberCtxPrefix, StringComparison.OrdinalIgnoreCase))
                result[k] = v;
        }

        return result;
    }

    /// <summary>
    /// Builds a new W3C TraceContext header map with <paramref name="traceId"/> plus
    /// optional <c>Tracestate</c> copied from <paramref name="source"/>.
    /// Mirrors Go <c>newTraceCtxHeader(h http.Header, tId []string) http.Header</c>.
    /// </summary>
    public static Dictionary<string, string[]> NewTraceCtxHeader(
        Dictionary<string, string[]> source,
        string[] traceId)
    {
        var result = new Dictionary<string, string[]>(StringComparer.OrdinalIgnoreCase)
        {
            [TracingHeaderKeys.TraceParent] = traceId,
        };

        TryCopy(source, result, TracingHeaderKeys.TraceState);
        return result;
    }

    /// <summary>
    /// Determines whether a service-latency sample should be taken for a given
    /// <paramref name="latencyConfig"/> and <paramref name="headers"/>.
    /// Returns the sampling decision and an optional propagation header map.
    /// Mirrors Go <c>shouldSample(l *serviceLatency, c *client) (bool, http.Header)</c>.
    /// </summary>
    public static (bool Sample, Dictionary<string, string[]>? Header) ShouldSample(
        InternalServiceLatency? latencyConfig,
        Dictionary<string, string[]>? headers)
    {
        if (latencyConfig == null)
            return (false, null);

        if (latencyConfig.Sampling < 0)
            return (false, null);

        if (latencyConfig.Sampling >= 100)
            return (true, null);

        if (latencyConfig.Sampling > 0 && Random.Shared.Next(100) < latencyConfig.Sampling)
            return (true, null);

        if (headers == null || headers.Count == 0)
            return (false, null);

        // Uber/Jaeger: check flag bit in last token of trace-id.
        if (headers.TryGetValue(TracingHeaderKeys.UberTraceId, out var uberIds) && uberIds.Length > 0)
        {
            var tk = uberIds[0].Split(':');
            if (tk.Length == 4 && tk[3].Length is > 0 and <= 2)
            {
                var src = tk[3].Length == 1 ? $"0{tk[3]}" : tk[3];
                if (byte.TryParse(src, NumberStyles.HexNumber, null, out var flag) && (flag & 1) == 1)
                    return (true, NewUberHeader(headers, uberIds));
            }
            return (false, null);
        }

        // Zipkin B3 multi-header — sampled flag.
        if (headers.TryGetValue(TracingHeaderKeys.B3Sampled, out var sampled) && sampled.Length > 0)
        {
            if (sampled[0] == "1") return (true, NewB3Header(headers));
            if (sampled[0] == "0") return (false, null);
        }

        // Zipkin B3 multi-header — presence of trace-id means recipient decides.
        if (headers.ContainsKey(TracingHeaderKeys.B3TraceId))
            return (true, NewB3Header(headers));

        // Zipkin B3 single-header.
        if (headers.TryGetValue(TracingHeaderKeys.B3Single, out var b3) && b3.Length > 0)
        {
            var tk = b3[0].Split('-');
            if (tk.Length > 2 && tk[2] == "0")  return (false, null);
            if (tk.Length == 1 && tk[0] == "0") return (false, null);
            return (true, new Dictionary<string, string[]>(StringComparer.OrdinalIgnoreCase)
            {
                [TracingHeaderKeys.B3Single] = b3,
            });
        }

        // W3C TraceContext.
        if (headers.TryGetValue(TracingHeaderKeys.TraceParent, out var tpIds) && tpIds.Length > 0)
        {
            var tk = tpIds[0].Split('-');
            bool sample = false;
            if (tk.Length == 4 && tk[3].Length == 2)
            {
                if (byte.TryParse(tk[3], NumberStyles.HexNumber, null, out var flags))
                    sample = (flags & 0x1) == 0x1;
            }
            if (sample)
                return (true, NewTraceCtxHeader(headers, tpIds));
            return (false, null);
        }

        return (false, null);
    }

    // -------------------------------------------------------------------------
    // Private helpers
    // -------------------------------------------------------------------------

    private static void TryCopy(
        Dictionary<string, string[]> src,
        Dictionary<string, string[]> dst,
        string key)
    {
        if (src.TryGetValue(key, out var v))
            dst[key] = v;
    }
}

// ============================================================================
// UpdateAllClientsServiceExportResponseTime
// Mirrors Go updateAllClientsServiceExportResponseTime (accounts.go ~line 1527).
// ============================================================================

/// <summary>
/// Static helper that updates the response-time tracking threshold on all clients
/// registered for a service export.
/// Mirrors Go <c>updateAllClientsServiceExportResponseTime(clients []*client, lrt time.Duration)</c>.
/// </summary>
internal static class ServiceExportClientHelpers
{
    /// <summary>
    /// Iterates <paramref name="clients"/> and, for each one whose <c>RrTracking.Lrt</c>
    /// differs from <paramref name="lowestResponseThreshold"/>, updates the threshold
    /// and resets the pending-timer.
    /// Mirrors Go <c>updateAllClientsServiceExportResponseTime</c>.
    /// </summary>
    public static void UpdateAllClientsServiceExportResponseTime(
        IEnumerable<ClientConnection> clients,
        TimeSpan lowestResponseThreshold)
    {
        foreach (var c in clients)
        {
            c.UpdateRrTrackingThreshold(lowestResponseThreshold);
        }
    }
}
