package server.logic.ws_protocol.JSON.messages;

import server.logic.ws_protocol.JSON.ConnectionContext;
import server.logic.ws_protocol.JSON.entyties.Net_Request;
import server.logic.ws_protocol.JSON.entyties.Net_Response;
import server.logic.ws_protocol.JSON.handlers.JsonMessageHandler;
import server.logic.ws_protocol.JSON.messages.entyties.Net_SendMessagePair_Request;
import server.logic.ws_protocol.JSON.messages.entyties.Net_SendMessagePair_Response;
import server.logic.ws_protocol.JSON.utils.NetExceptionResponseFactory;
import server.logic.ws_protocol.WireCodes;
import shine.db.dao.SignedMessagesV2DAO;
import shine.db.entities.SignedMessageV2Entry;

public class Net_SendMessagePair_Handler implements JsonMessageHandler {
    @Override
    public Net_Response handle(Net_Request baseRequest, ConnectionContext ctx) throws Exception {
        Net_SendMessagePair_Request req = (Net_SendMessagePair_Request) baseRequest;
        if (isBlank(req.getIncomingBlobB64()) || isBlank(req.getOutgoingBlobB64())) {
            return NetExceptionResponseFactory.error(req, WireCodes.Status.BAD_REQUEST, "BAD_FIELDS", "incomingBlobB64/outgoingBlobB64 обязательны");
        }

        final SignedMessageBlock incoming;
        final SignedMessageBlock outgoing;
        try {
            incoming = SignedMessagesCore.parseFromB64(req.getIncomingBlobB64());
            outgoing = SignedMessagesCore.parseFromB64(req.getOutgoingBlobB64());
            SignedMessagesCore.validatePair(incoming, outgoing);
        } catch (IllegalArgumentException ex) {
            return NetExceptionResponseFactory.error(req, WireCodes.Status.BAD_REQUEST, ex.getMessage(), "Некорректный формат пары сообщений");
        }

        try {
            SignedMessagesCore.verifyUsersAndSignature(incoming);
            SignedMessagesCore.verifyUsersAndSignature(outgoing);
        } catch (IllegalArgumentException ex) {
            String code = ex.getMessage();
            int status = "USER_NOT_FOUND".equals(code) ? 404 : WireCodes.Status.UNVERIFIED;
            return NetExceptionResponseFactory.error(req, status, code, "Сообщение не прошло проверку");
        }

        SignedMessageV2Entry incomingEntry;
        SignedMessageV2Entry outgoingEntry;
        try {
            String sourceApi = "ReceiveOutcomingMessage";
            String originSessionId = (ctx != null && !isBlank(ctx.getSessionId())) ? ctx.getSessionId() : null;
            incomingEntry = SignedMessagesCore.toEntry(incoming, sourceApi, originSessionId);
            outgoingEntry = SignedMessagesCore.toEntry(outgoing, sourceApi, originSessionId);
        } catch (IllegalArgumentException ex) {
            return NetExceptionResponseFactory.error(req, WireCodes.Status.BAD_REQUEST, ex.getMessage(), "Некорректный payload подтверждения");
        }

        boolean pairInserted = SignedMessagesV2DAO.getInstance().insertPairBothOrNothing(incomingEntry, outgoingEntry);

        SignedMessagesRealtime.DeliveryCounters inCounters = new SignedMessagesRealtime.DeliveryCounters();
        if (pairInserted) {
            inCounters = SignedMessagesRealtime.deliverToTargetSessions(incomingEntry, null);
        }

        String excludeSessionId = null;
        if (ctx != null && !isBlank(ctx.getLogin()) && outgoingEntry.getTargetLogin().equalsIgnoreCase(ctx.getLogin())) {
            excludeSessionId = ctx.getSessionId();
        }
        SignedMessagesRealtime.DeliveryCounters outCounters = new SignedMessagesRealtime.DeliveryCounters();
        if (pairInserted) {
            outCounters = SignedMessagesRealtime.deliverToTargetSessions(outgoingEntry, excludeSessionId);
        }

        Net_SendMessagePair_Response resp = new Net_SendMessagePair_Response();
        resp.setOp(req.getOp());
        resp.setRequestId(req.getRequestId());
        resp.setStatus(WireCodes.Status.OK);
        resp.setBaseKey(incomingEntry.getBaseKey());
        resp.setIncomingKey(incomingEntry.getMessageKey());
        resp.setOutgoingKey(outgoingEntry.getMessageKey());
        resp.setDeliveredWsSessions(inCounters.wsDelivered + outCounters.wsDelivered);
        resp.setDeliveredWebPushSessions(inCounters.pushDelivered + outCounters.pushDelivered);
        return resp;
    }

    private boolean isBlank(String s) {
        return s == null || s.isBlank();
    }
}
