using NATS.Server.Auth;
using NATS.Server.Protocol;
using NATS.Server.Subscriptions;

namespace NATS.Server;

/// <summary>
/// Lightweight socketless client for internal messaging (SYSTEM, ACCOUNT, JETSTREAM).
/// Maps to Go's internal client created by createInternalClient() in server.go:1910-1936.
/// No network I/O — messages are delivered via callback.
/// </summary>
public sealed class InternalClient : INatsClient
{
    public ulong Id { get; }
    public ClientKind Kind { get; }
    public bool IsInternal => Kind.IsInternal();
    public Account? Account { get; }
    public ClientOptions? ClientOpts => null;
    public ClientPermissions? Permissions => null;

    /// <summary>
    /// Callback invoked when a message is delivered to this internal client.
    /// Set by the event system or account import infrastructure.
    /// </summary>
    public Action<string, string, string?, ReadOnlyMemory<byte>, ReadOnlyMemory<byte>>? MessageCallback { get; set; }

    private readonly Dictionary<string, Subscription> _subs = new(StringComparer.Ordinal);

    public InternalClient(ulong id, ClientKind kind, Account account)
    {
        if (!kind.IsInternal())
            throw new ArgumentException($"InternalClient requires an internal ClientKind, got {kind}", nameof(kind));

        Id = id;
        Kind = kind;
        Account = account;
    }

    public void SendMessage(string subject, string sid, string? replyTo,
        ReadOnlyMemory<byte> headers, ReadOnlyMemory<byte> payload)
    {
        MessageCallback?.Invoke(subject, sid, replyTo, headers, payload);
    }

    public void SendMessageNoFlush(string subject, string sid, string? replyTo,
        ReadOnlyMemory<byte> headers, ReadOnlyMemory<byte> payload)
    {
        // Internal clients have no buffered I/O — same as SendMessage.
        MessageCallback?.Invoke(subject, sid, replyTo, headers, payload);
    }

    public void SignalFlush() { } // no-op for internal clients

    public bool QueueOutbound(ReadOnlyMemory<byte> data) => true; // no-op for internal clients

    public void RemoveSubscription(string sid)
    {
        if (_subs.Remove(sid))
            Account?.DecrementSubscriptions();
    }

    public void AddSubscription(Subscription sub)
    {
        _subs[sub.Sid] = sub;
    }

    public IReadOnlyDictionary<string, Subscription> Subscriptions => _subs;
}
