package com.cameleer.server.app.agent;

import com.cameleer.server.app.security.Ed25519SigningServiceImpl;
import com.cameleer.server.core.security.Ed25519SigningService;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.Signature;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;

import static org.assertj.core.api.Assertions.assertThat;

/**
 * Unit tests for SsePayloadSigner.
 * Uses real Ed25519SigningServiceImpl (no Spring context needed).
 */
class SsePayloadSignerTest {

    private Ed25519SigningService signingService;
    private ObjectMapper objectMapper;
    private SsePayloadSigner signer;

    @BeforeEach
    void setUp() {
        signingService = Ed25519SigningServiceImpl.ephemeral();
        objectMapper = new ObjectMapper();
        signer = new SsePayloadSigner(signingService, objectMapper);
    }

    @Test
    void signPayload_addsSignatureFieldToJson() throws Exception {
        String payload = "{\"key\":\"value\",\"count\":42}";

        String signed = signer.signPayload(payload);

        JsonNode node = objectMapper.readTree(signed);
        assertThat(node.has("signature")).isTrue();
        assertThat(node.get("key").asText()).isEqualTo("value");
        assertThat(node.get("count").asInt()).isEqualTo(42);
    }

    @Test
    void signPayload_signatureIsBase64Encoded() throws Exception {
        String payload = "{\"key\":\"value\"}";

        String signed = signer.signPayload(payload);

        JsonNode node = objectMapper.readTree(signed);
        String signature = node.get("signature").asText();
        // Should not throw -- valid Base64
        byte[] decoded = Base64.getDecoder().decode(signature);
        assertThat(decoded).isNotEmpty();
    }

    @Test
    void signPayload_signatureVerifiesAgainstPublicKey() throws Exception {
        String payload = "{\"key\":\"value\",\"nested\":{\"a\":1}}";

        String signed = signer.signPayload(payload);

        JsonNode node = objectMapper.readTree(signed);
        String signatureBase64 = node.get("signature").asText();

        // Verify signature against original payload (before signature was added)
        byte[] signatureBytes = Base64.getDecoder().decode(signatureBase64);
        PublicKey publicKey = loadPublicKey(signingService.getPublicKeyBase64());

        Signature verifier = Signature.getInstance("Ed25519");
        verifier.initVerify(publicKey);
        verifier.update(payload.getBytes(StandardCharsets.UTF_8));
        assertThat(verifier.verify(signatureBytes)).isTrue();
    }

    @Test
    void signPayload_signatureIsOverOriginalPayloadWithoutSignatureField() throws Exception {
        String payload = "{\"data\":\"test\"}";

        String signed = signer.signPayload(payload);

        // Remove signature field and verify the original payload was what was signed
        JsonNode node = objectMapper.readTree(signed);
        String signatureBase64 = node.get("signature").asText();

        byte[] signatureBytes = Base64.getDecoder().decode(signatureBase64);
        PublicKey publicKey = loadPublicKey(signingService.getPublicKeyBase64());

        // Verify against original payload string (not reconstructed from parsed JSON)
        Signature verifier = Signature.getInstance("Ed25519");
        verifier.initVerify(publicKey);
        verifier.update(payload.getBytes(StandardCharsets.UTF_8));
        assertThat(verifier.verify(signatureBytes))
                .as("Signature should be computed over the original JSON string")
                .isTrue();
    }

    @Test
    void signPayload_nullPayloadReturnsNull() {
        String result = signer.signPayload(null);
        assertThat(result).isNull();
    }

    @Test
    void signPayload_emptyPayloadReturnsEmpty() {
        String result = signer.signPayload("");
        assertThat(result).isEmpty();
    }

    @Test
    void signPayload_blankPayloadReturnsAsIs() {
        String result = signer.signPayload("   ");
        assertThat(result).isEqualTo("   ");
    }

    private PublicKey loadPublicKey(String base64PublicKey) throws Exception {
        byte[] keyBytes = Base64.getDecoder().decode(base64PublicKey);
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance("Ed25519");
        return keyFactory.generatePublic(keySpec);
    }
}
