using System.Text;

namespace ZB.MOM.NatsNet.Server;

public sealed partial class NatsServer
{
    internal void SendDomainLeaderElectAdvisory()
    {
        var (_, cluster) = GetJetStreamCluster();
        var meta = cluster?.Meta;
        if (meta == null)
            return;

        Noticef(
            "JetStream domain leader elected advisory for leader {0} in cluster {1}",
            meta.GroupLeader(),
            CachedClusterName());
    }

    internal void SendConsumerLostQuorumAdvisory(NatsConsumer? consumer)
    {
        if (consumer == null || !consumer.ShouldSendLostQuorum())
            return;

        Noticef("JetStream consumer lost quorum advisory for consumer {0} on stream {1}", consumer.Name, consumer.Stream);
    }

    internal void SendConsumerLeaderElectAdvisory(NatsConsumer? consumer)
    {
        if (consumer == null)
            return;

        Noticef("JetStream consumer leader elected advisory for consumer {0} on stream {1}", consumer.Name, consumer.Stream);
    }

    internal void JsClusteredStreamRequest(
        ClientInfo clientInfo,
        Account account,
        string subject,
        string reply,
        byte[] rawMessage,
        StreamConfigRequest configRequest)
    {
        var (js, cluster) = GetJetStreamCluster();
        if (js == null || cluster == null)
            return;

        var cfg = configRequest.Config;
        var engine = new JetStreamEngine(js);
        var limitsError = engine.JsClusteredStreamLimitsCheck(account, cfg);
        if (limitsError != null)
        {
            var response = new ApiResponse
            {
                Type = JsApiSubjects.JsApiStreamCreateResponseType,
                Error = limitsError,
            };
            SendAPIErrResponse(clientInfo, account, subject, reply, string.Empty, JsonResponse(response));
            return;
        }

        var (group, createError) = engine.CreateGroupForStream(clientInfo, cfg);
        if (group == null || createError != null)
        {
            var response = new ApiResponse
            {
                Type = JsApiSubjects.JsApiStreamCreateResponseType,
                Error = JsApiErrors.NewJSClusterNoPeersError(createError ?? new SelectPeerError { Misc = true }),
            };
            SendAPIErrResponse(clientInfo, account, subject, reply, string.Empty, JsonResponse(response));
            return;
        }

        var assignment = new StreamAssignment
        {
            Group = group,
            Config = cfg,
            Subject = subject,
            Reply = reply,
            Client = clientInfo,
            Created = DateTime.UtcNow,
        };

        if (cluster.Meta != null)
        {
            cluster.Meta.Propose(Encoding.UTF8.GetBytes($"create-stream:{account.Name}:{cfg.Name}"));
            cluster.TrackInflightStreamProposal(account.Name, assignment, deleted: false);
        }
    }

    internal void JsClusteredStreamUpdateRequest(
        ClientInfo clientInfo,
        Account account,
        string subject,
        string reply,
        byte[] rawMessage,
        StreamConfig config)
    {
        _ = rawMessage;
        JsClusteredStreamRequest(clientInfo, account, subject, reply, rawMessage, new StreamConfigRequest { Config = config });
    }

    internal void JsClusteredStreamDeleteRequest(
        ClientInfo clientInfo,
        Account account,
        string stream,
        string subject,
        string reply,
        byte[] rawMessage)
    {
        _ = rawMessage;
        var (js, cluster) = GetJetStreamCluster();
        if (js == null || cluster?.Meta == null)
            return;

        var assignment = new StreamAssignment
        {
            Subject = subject,
            Reply = reply,
            Client = clientInfo,
            Config = new StreamConfig { Name = stream },
            Created = DateTime.UtcNow,
        };

        cluster.Meta.Propose(Encoding.UTF8.GetBytes($"delete-stream:{account.Name}:{stream}"));
        cluster.TrackInflightStreamProposal(account.Name, assignment, deleted: true);
    }

    internal void JsClusteredStreamPurgeRequest(
        ClientInfo clientInfo,
        Account account,
        NatsStream? stream,
        string streamName,
        string subject,
        string reply,
        byte[] rawMessage,
        StreamPurgeRequest request)
    {
        _ = stream;
        _ = streamName;
        _ = rawMessage;
        _ = request;

        var response = new ApiResponse { Type = JsApiSubjects.JsApiStreamPurgeResponseType };
        SendAPIResponse(clientInfo, account, subject, reply, string.Empty, JsonResponse(response));
    }

    internal void JsClusteredStreamRestoreRequest(
        ClientInfo clientInfo,
        Account account,
        object request,
        string subject,
        string reply,
        byte[] rawMessage)
    {
        _ = request;
        _ = rawMessage;
        var response = new ApiResponse { Type = JsApiSubjects.JsApiStreamRestoreResponseType };
        SendAPIResponse(clientInfo, account, subject, reply, string.Empty, JsonResponse(response));
    }

    internal bool AllPeersOffline(RaftGroup? group)
    {
        if (group == null || group.Peers.Length == 0)
            return false;

        foreach (var peer in group.Peers)
        {
            if (GetNodeInfo(peer) is { Offline: false })
                return false;
        }

        return true;
    }

    internal void JsClusteredStreamListRequest(Account account, ClientInfo clientInfo, string filter, int offset, string subject, string reply, byte[] rawMessage)
    {
        _ = filter;
        _ = offset;
        _ = rawMessage;
        var response = new ApiResponse { Type = JsApiSubjects.JsApiStreamListResponseType };
        SendAPIResponse(clientInfo, account, subject, reply, string.Empty, JsonResponse(response));
    }

    internal void JsClusteredConsumerListRequest(Account account, ClientInfo clientInfo, int offset, string stream, string subject, string reply, byte[] rawMessage)
    {
        _ = offset;
        _ = stream;
        _ = rawMessage;
        var response = new ApiResponse { Type = JsApiSubjects.JsApiConsumerListResponseType };
        SendAPIResponse(clientInfo, account, subject, reply, string.Empty, JsonResponse(response));
    }

    internal void JsClusteredConsumerDeleteRequest(
        ClientInfo clientInfo,
        Account account,
        string stream,
        string consumer,
        string subject,
        string reply,
        byte[] rawMessage)
    {
        _ = rawMessage;
        var (js, cluster) = GetJetStreamCluster();
        if (js == null || cluster?.Meta == null)
            return;

        cluster.Meta.Propose(Encoding.UTF8.GetBytes($"delete-consumer:{account.Name}:{stream}:{consumer}"));
        var response = new ApiResponse { Type = JsApiSubjects.JsApiConsumerDeleteResponseType };
        SendAPIResponse(clientInfo, account, subject, reply, string.Empty, JsonResponse(response));
    }

    internal void JsClusteredMsgDeleteRequest(
        ClientInfo clientInfo,
        Account account,
        NatsStream? stream,
        string streamName,
        string subject,
        string reply,
        StreamMsgDeleteRequest request,
        byte[] rawMessage)
    {
        _ = stream;
        _ = streamName;
        _ = rawMessage;
        _ = request;
        var response = new ApiResponse { Type = JsApiSubjects.JsApiMsgDeleteResponseType };
        SendAPIResponse(clientInfo, account, subject, reply, string.Empty, JsonResponse(response));
    }
}
