package server.logic.ws_protocol.JSON.messages;

import shine.db.dao.SolanaUsersDAO;
import shine.db.entities.SignedMessageV2Entry;
import shine.db.entities.SolanaUserEntry;
import utils.crypto.Ed25519Util;

import java.nio.charset.StandardCharsets;
import java.util.Base64;

final class SignedMessagesCore {
    private static final int MAX_ENCRYPTED_BODY_BYTES = 16384;

    private SignedMessagesCore() {}

    static SignedMessageBlock parseFromB64(String blobB64) {
        try {
            byte[] raw = Base64.getDecoder().decode(blobB64.trim());
            return SignedMessageBlock.parse(raw, MAX_ENCRYPTED_BODY_BYTES);
        } catch (IllegalArgumentException e) {
            String code = e.getMessage();
            if (code == null || code.isBlank()) {
                throw new IllegalArgumentException("BAD_BLOCK_FORMAT");
            }
            switch (code) {
                case "ATTACHMENTS_DISABLED",
                        "BAD_PREFIX",
                        "BAD_LEN",
                        "BAD_TO_LOGIN",
                        "BAD_FROM_LOGIN",
                        "BAD_TIME",
                        "BAD_MESSAGE_TYPE",
                        "BAD_MESSAGE_LEN",
                        "BAD_FORMAT_VERSION",
                        "BAD_REVISION_TIME" -> throw e;
                default -> throw new IllegalArgumentException("BAD_BLOCK_FORMAT");
            }
        }
    }

    static void verifyUsersAndSignature(SignedMessageBlock block) throws Exception {
        SolanaUserEntry from = SolanaUsersDAO.getInstance().getByLogin(block.fromLogin);
        SolanaUserEntry to = SolanaUsersDAO.getInstance().getByLogin(block.toLogin);
        if (from == null || to == null) {
            throw new IllegalArgumentException("USER_NOT_FOUND");
        }
        byte[] pubKey32 = Ed25519Util.keyFromBase64(from.getClientKey());
        if (!Ed25519Util.verify(block.signedBody, block.signature64, pubKey32)) {
            throw new IllegalArgumentException("BAD_SIGNATURE");
        }
    }

    static void validatePair(SignedMessageBlock incoming, SignedMessageBlock outgoing) {
        if (incoming.messageType % 2 == 0) throw new IllegalArgumentException("BAD_PAIR_TYPES");
        if (outgoing.messageType != incoming.messageType + 1) throw new IllegalArgumentException("BAD_PAIR_TYPES");
        if (!incoming.toLogin.equalsIgnoreCase(outgoing.toLogin)) throw new IllegalArgumentException("BAD_PAIR_KEYS");
        if (!incoming.fromLogin.equalsIgnoreCase(outgoing.fromLogin)) throw new IllegalArgumentException("BAD_PAIR_KEYS");
        if (incoming.timeMs != outgoing.timeMs) throw new IllegalArgumentException("BAD_PAIR_KEYS");
        if (incoming.nonce != outgoing.nonce) throw new IllegalArgumentException("BAD_PAIR_KEYS");

        if (incoming.isReadReceiptType()) {
            ReadReceiptPayload inRef = ReadReceiptPayload.parse(incoming.payloadBytes);
            ReadReceiptPayload outRef = ReadReceiptPayload.parse(outgoing.payloadBytes);
            if (!inRef.refToLogin.equalsIgnoreCase(outRef.refToLogin)
                    || !inRef.refFromLogin.equalsIgnoreCase(outRef.refFromLogin)
                    || inRef.refTimeMs != outRef.refTimeMs
                    || inRef.refNonce != outRef.refNonce
                    || inRef.refType != outRef.refType) {
                throw new IllegalArgumentException("BAD_RECEIPT_REF");
            }
            return;
        }

        if (incoming.legacyFormat || outgoing.legacyFormat) {
            throw new IllegalArgumentException("BAD_CONTENT_FORMAT");
        }
        if (incoming.revisionTimeMs != outgoing.revisionTimeMs) {
            throw new IllegalArgumentException("BAD_REVISION_TIME");
        }
        if (incoming.formatVersionMajor != outgoing.formatVersionMajor
                || incoming.formatVersionMinor != outgoing.formatVersionMinor) {
            throw new IllegalArgumentException("BAD_FORMAT_VERSION");
        }

        if (incoming.encryptedBodyBytes.length != outgoing.encryptedBodyBytes.length) {
            throw new IllegalArgumentException("BAD_MESSAGE_LEN");
        }
        for (int i = 0; i < incoming.encryptedBodyBytes.length; i++) {
            if (incoming.encryptedBodyBytes[i] != outgoing.encryptedBodyBytes[i]) {
                throw new IllegalArgumentException("BAD_ENCRYPTED_BODY");
            }
        }
    }

    static SignedMessageV2Entry toEntry(SignedMessageBlock block, String sourceApi, String originSessionId) {
        String baseKey = SignedMessageKeys.baseKey(block.toLogin, block.fromLogin, block.timeMs, block.nonce);
        String messageKey = SignedMessageKeys.messageKey(block.toLogin, block.fromLogin, block.timeMs, block.nonce, block.messageType);

        SignedMessageV2Entry entry = new SignedMessageV2Entry();
        entry.setMessageKey(messageKey);
        entry.setBaseKey(baseKey);
        entry.setTargetLogin(block.targetLogin());
        entry.setFromLogin(block.fromLogin);
        entry.setToLogin(block.toLogin);
        entry.setTimeMs(block.timeMs);
        entry.setNonce(block.nonce);
        entry.setMessageType(block.messageType);
        entry.setRevisionTimeMs(block.revisionTimeMs);
        entry.setRawBlock(block.rawPacket);
        entry.setCreatedAtMs(System.currentTimeMillis());
        entry.setSourceApi(sourceApi);
        entry.setOriginSessionId(originSessionId);

        if (block.messageType == SignedMessageBlock.TYPE_READ_INCOMING
                || block.messageType == SignedMessageBlock.TYPE_READ_OUTGOING_COPY) {
            ReadReceiptPayload ref = ReadReceiptPayload.parse(block.payloadBytes);
            entry.setReceiptRefBaseKey(ref.refBaseKey());
            entry.setReceiptRefType(ref.refType);
        }

        return entry;
    }

    static String previewTextForPush(SignedMessageBlock block) {
        if (!block.isContentType() || block.encryptedBodyBytes == null || block.encryptedBodyBytes.length == 0) {
            return "";
        }
        return new String(block.encryptedBodyBytes, StandardCharsets.UTF_8);
    }
}
