// Go: gateway.go:100-150 (InterestMode enum)
// Go: gateway.go:1500-1600 (switchToInterestOnlyMode)
using System.Collections.Concurrent;
using NATS.Server.Subscriptions;

namespace NATS.Server.Gateways;

/// <summary>
/// Tracks the interest mode for each account on a gateway connection.
/// In Optimistic mode, all messages are forwarded unless a subject is in the
/// no-interest set. Once the no-interest set exceeds the threshold (1000),
/// the account switches to InterestOnly mode where only subjects with tracked
/// RS+ interest are forwarded.
/// </summary>
public enum GatewayInterestMode
{
    /// <summary>Forward everything (initial state). Track subjects with no interest.</summary>
    Optimistic,

    /// <summary>Mode transition in progress.</summary>
    Transitioning,

    /// <summary>Only forward subjects with known remote interest (RS+ received).</summary>
    InterestOnly,
}

/// <summary>
/// Per-account interest state machine for a gateway connection.
/// Go reference: gateway.go:100-150 (struct srvGateway, interestMode fields),
/// gateway.go:1500-1600 (switchToInterestOnlyMode, processGatewayAccountUnsub).
/// </summary>
public sealed class GatewayInterestTracker
{
    /// <summary>
    /// Number of no-interest subjects before switching to InterestOnly mode.
    /// Go: gateway.go:134 (defaultGatewayMaxRUnsubThreshold = 1000)
    /// </summary>
    public const int DefaultNoInterestThreshold = 1000;

    private readonly int _noInterestThreshold;

    // Per-account state: mode + no-interest set (Optimistic) or positive interest set (InterestOnly)
    private readonly ConcurrentDictionary<string, AccountState> _accounts = new(StringComparer.Ordinal);

    public GatewayInterestTracker(int noInterestThreshold = DefaultNoInterestThreshold)
    {
        _noInterestThreshold = noInterestThreshold;
    }

    /// <summary>
    /// Returns the current interest mode for the given account.
    /// Accounts default to Optimistic until the no-interest threshold is exceeded.
    /// </summary>
    public GatewayInterestMode GetMode(string account)
        => _accounts.TryGetValue(account, out var state) ? state.Mode : GatewayInterestMode.Optimistic;

    /// <summary>
    /// Track a positive interest (RS+ received from remote) for an account/subject.
    /// Go: gateway.go:1540 (processGatewayAccountSub — adds to interest set)
    /// </summary>
    public void TrackInterest(string account, string subject)
    {
        var state = GetOrCreateState(account);
        lock (state)
        {
            // In Optimistic mode, remove from no-interest set if present
            if (state.Mode == GatewayInterestMode.Optimistic)
            {
                state.NoInterestSet.Remove(subject);
                return;
            }

            // In InterestOnly mode, add to the positive interest set
            if (state.Mode == GatewayInterestMode.InterestOnly)
            {
                state.InterestSet.Add(subject);
            }
        }
    }

    /// <summary>
    /// Track a no-interest event (RS- received from remote) for an account/subject.
    /// When the no-interest set crosses the threshold, switches to InterestOnly mode.
    /// Go: gateway.go:1560 (processGatewayAccountUnsub — tracks no-interest, triggers switch)
    /// </summary>
    public void TrackNoInterest(string account, string subject)
    {
        var state = GetOrCreateState(account);
        lock (state)
        {
            if (state.Mode == GatewayInterestMode.InterestOnly)
            {
                // In InterestOnly mode, remove from positive interest set
                state.InterestSet.Remove(subject);
                return;
            }

            if (state.Mode == GatewayInterestMode.Optimistic)
            {
                state.NoInterestSet.Add(subject);

                if (state.NoInterestSet.Count >= _noInterestThreshold)
                    DoSwitchToInterestOnly(state);
            }
        }
    }

    /// <summary>
    /// Determines whether a message should be forwarded to the remote gateway
    /// for the given account and subject.
    /// Go: gateway.go:2900 (shouldForwardMsg — checks mode and interest)
    /// </summary>
    public bool ShouldForward(string account, string subject)
    {
        if (!_accounts.TryGetValue(account, out var state))
            return true; // Optimistic by default — no state yet means forward

        lock (state)
        {
            return state.Mode switch
            {
                GatewayInterestMode.Optimistic =>
                    // Forward unless subject is in no-interest set
                    !state.NoInterestSet.Contains(subject),

                GatewayInterestMode.Transitioning =>
                    // During transition, be conservative and forward
                    true,

                GatewayInterestMode.InterestOnly =>
                    // Only forward if at least one interest pattern matches
                    MatchesAnyInterest(state, subject),

                _ => true,
            };
        }
    }

    /// <summary>
    /// Explicitly switch an account to InterestOnly mode.
    /// Called when the remote signals it is in interest-only mode.
    /// Go: gateway.go:1500 (switchToInterestOnlyMode)
    /// </summary>
    public void SwitchToInterestOnly(string account)
    {
        var state = GetOrCreateState(account);
        lock (state)
        {
            if (state.Mode != GatewayInterestMode.InterestOnly)
                DoSwitchToInterestOnly(state);
        }
    }

    // ── Private helpers ────────────────────────────────────────────────

    private AccountState GetOrCreateState(string account)
        => _accounts.GetOrAdd(account, _ => new AccountState());

    private static void DoSwitchToInterestOnly(AccountState state)
    {
        // Go: gateway.go:1510-1530 — clear no-interest, build positive interest from what remains
        state.Mode = GatewayInterestMode.InterestOnly;
        state.NoInterestSet.Clear();
        // InterestSet starts empty; subsequent RS+ events will populate it
    }

    private static bool MatchesAnyInterest(AccountState state, string subject)
    {
        foreach (var pattern in state.InterestSet)
        {
            // Use SubjectMatch.MatchLiteral to support wildcard patterns in the interest set
            if (SubjectMatch.MatchLiteral(subject, pattern))
                return true;
        }

        return false;
    }

    /// <summary>Per-account mutable state. All access must be under the instance lock.</summary>
    private sealed class AccountState
    {
        public GatewayInterestMode Mode { get; set; } = GatewayInterestMode.Optimistic;

        /// <summary>Subjects with no remote interest (used in Optimistic mode).</summary>
        public HashSet<string> NoInterestSet { get; } = new(StringComparer.Ordinal);

        /// <summary>Subjects/patterns with positive remote interest (used in InterestOnly mode).</summary>
        public HashSet<string> InterestSet { get; } = new(StringComparer.Ordinal);
    }
}
