package com.cameleer.server.app.agent;

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.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

/**
 * Signs SSE command payloads with Ed25519 before delivery.
 * <p>
 * The signature is computed over the original JSON payload string (without the
 * signature field). The resulting Base64-encoded signature is added as a
 * {@code "signature"} field to the JSON before returning.
 * <p>
 * Agents verify the signature by:
 * <ol>
 *   <li>Extracting and removing the {@code "signature"} field from the received JSON</li>
 *   <li>Serializing the remaining fields back to a JSON string</li>
 *   <li>Verifying the signature against that string using the server's Ed25519 public key</li>
 * </ol>
 * In practice, agents should verify against the original payload — the signature is
 * computed over the exact JSON string as received by the server.
 */
@Component
public class SsePayloadSigner {

    private static final Logger log = LoggerFactory.getLogger(SsePayloadSigner.class);

    private final Ed25519SigningService ed25519SigningService;
    private final ObjectMapper objectMapper;

    public SsePayloadSigner(Ed25519SigningService ed25519SigningService, ObjectMapper objectMapper) {
        this.ed25519SigningService = ed25519SigningService;
        this.objectMapper = objectMapper;
    }

    /**
     * Signs the given JSON payload and returns a new JSON string with a {@code "signature"} field added.
     * <p>
     * The signature is computed over the original payload string (before adding the signature field).
     *
     * @param jsonPayload the JSON string to sign
     * @return the signed JSON string with a "signature" field, or the original payload if null/empty/blank
     */
    public String signPayload(String jsonPayload) {
        if (jsonPayload == null) {
            log.warn("Attempted to sign null payload, returning null");
            return null;
        }
        if (jsonPayload.isEmpty() || jsonPayload.isBlank()) {
            log.warn("Attempted to sign empty/blank payload, returning as-is");
            return jsonPayload;
        }

        try {
            // 1. Sign the original payload string
            String signatureBase64 = ed25519SigningService.sign(jsonPayload);

            // 2. Parse payload, add signature field, serialize back
            JsonNode node = objectMapper.readTree(jsonPayload);
            if (node instanceof ObjectNode objectNode) {
                objectNode.put("signature", signatureBase64);
                return objectMapper.writeValueAsString(objectNode);
            } else {
                // Payload is not a JSON object (e.g., array or primitive) -- cannot add field
                log.warn("Payload is not a JSON object, returning unsigned: {}", jsonPayload);
                return jsonPayload;
            }
        } catch (Exception e) {
            log.error("Failed to sign payload, returning unsigned", e);
            return jsonPayload;
        }
    }
}
