package com.cameleer.server.app.security;

import com.cameleer.server.core.security.Ed25519SigningService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.Signature;
import java.security.spec.NamedParameterSpec;
import java.util.Base64;

/**
 * JDK 17 Ed25519 signing implementation.
 * <p>
 * Derives the keypair deterministically from the JWT secret via HMAC-SHA256,
 * so the same key pair is produced on every server startup. No database storage
 * of private key material is needed.
 */
public class Ed25519SigningServiceImpl implements Ed25519SigningService {

    private static final Logger log = LoggerFactory.getLogger(Ed25519SigningServiceImpl.class);
    private static final String DERIVATION_INFO = "cameleer-ed25519-signing";

    private final PrivateKey privateKey;
    private final PublicKey publicKey;

    public Ed25519SigningServiceImpl(String jwtSecret) {
        try {
            byte[] seed = deriveSeed(jwtSecret);
            KeyPair keyPair = generateFromSeed(seed);
            this.privateKey = keyPair.getPrivate();
            this.publicKey = keyPair.getPublic();
            log.info("Ed25519 signing key derived from JWT secret");
        } catch (GeneralSecurityException e) {
            throw new IllegalStateException("Failed to derive Ed25519 keypair", e);
        }
    }

    /** Ephemeral random key pair — for tests only. */
    public static Ed25519SigningServiceImpl ephemeral() {
        try {
            KeyPairGenerator generator = KeyPairGenerator.getInstance("Ed25519");
            KeyPair keyPair = generator.generateKeyPair();
            return new Ed25519SigningServiceImpl(keyPair);
        } catch (GeneralSecurityException e) {
            throw new IllegalStateException("Failed to generate Ed25519 keypair", e);
        }
    }

    private Ed25519SigningServiceImpl(KeyPair keyPair) {
        this.privateKey = keyPair.getPrivate();
        this.publicKey = keyPair.getPublic();
    }

    private static byte[] deriveSeed(String secret) throws GeneralSecurityException {
        Mac mac = Mac.getInstance("HmacSHA256");
        mac.init(new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), "HmacSHA256"));
        return mac.doFinal(DERIVATION_INFO.getBytes(StandardCharsets.UTF_8));
    }

    private static KeyPair generateFromSeed(byte[] seed) throws GeneralSecurityException {
        SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");
        sr.setSeed(seed);
        KeyPairGenerator kpg = KeyPairGenerator.getInstance("Ed25519");
        kpg.initialize(new NamedParameterSpec("Ed25519"), sr);
        return kpg.generateKeyPair();
    }

    @Override
    public String sign(String payload) {
        try {
            Signature signer = Signature.getInstance("Ed25519");
            signer.initSign(privateKey);
            signer.update(payload.getBytes(StandardCharsets.UTF_8));
            byte[] signatureBytes = signer.sign();
            return Base64.getEncoder().encodeToString(signatureBytes);
        } catch (GeneralSecurityException e) {
            throw new IllegalStateException("Failed to sign payload", e);
        }
    }

    @Override
    public String getPublicKeyBase64() {
        return Base64.getEncoder().encodeToString(publicKey.getEncoded());
    }
}
