//! Minimal compact JWS verification for Ed25519. Mirrors the contract the
//! platform produces via `jose.CompactSign` + EdDSA.

use base64::Engine;
use ed25519_dalek::pkcs8::DecodePublicKey;
use ed25519_dalek::{Signature, Verifier, VerifyingKey};
use serde::Deserialize;

const B64URL: base64::engine::general_purpose::GeneralPurpose =
    base64::engine::general_purpose::URL_SAFE_NO_PAD;

#[derive(Debug)]
pub enum JwsError {
    Malformed,
    UnsupportedAlgorithm(String),
    SignatureMismatch,
    InvalidKey(String),
    InvalidPayload,
}

#[derive(Debug, Clone, Deserialize)]
pub struct JwsHeader {
    pub alg: String,
    pub kid: Option<String>,
    #[serde(default)]
    pub typ: Option<String>,
}

#[derive(Debug, Clone)]
pub struct VerifiedJws {
    pub header: JwsHeader,
    pub payload: Vec<u8>,
}

pub fn verify_ed25519_pem(compact: &str, public_key_pem: &str) -> Result<VerifiedJws, JwsError> {
    let verifying_key =
        VerifyingKey::from_public_key_pem(public_key_pem).map_err(|e| JwsError::InvalidKey(e.to_string()))?;
    verify_ed25519(compact, &verifying_key)
}

pub fn verify_ed25519(compact: &str, verifying_key: &VerifyingKey) -> Result<VerifiedJws, JwsError> {
    let mut parts = compact.splitn(3, '.');
    let header_b64 = parts.next().ok_or(JwsError::Malformed)?;
    let payload_b64 = parts.next().ok_or(JwsError::Malformed)?;
    let signature_b64 = parts.next().ok_or(JwsError::Malformed)?;
    if parts.next().is_some() {
        return Err(JwsError::Malformed);
    }

    let header_bytes = B64URL.decode(header_b64).map_err(|_| JwsError::Malformed)?;
    let header: JwsHeader =
        serde_json::from_slice(&header_bytes).map_err(|_| JwsError::Malformed)?;
    if header.alg != "EdDSA" {
        return Err(JwsError::UnsupportedAlgorithm(header.alg));
    }

    let payload_bytes = B64URL.decode(payload_b64).map_err(|_| JwsError::InvalidPayload)?;
    let signature_bytes = B64URL
        .decode(signature_b64)
        .map_err(|_| JwsError::SignatureMismatch)?;
    let signature_array: [u8; 64] = signature_bytes
        .as_slice()
        .try_into()
        .map_err(|_| JwsError::SignatureMismatch)?;
    let signature = Signature::from_bytes(&signature_array);

    let signing_input = format!("{header_b64}.{payload_b64}");
    verifying_key
        .verify(signing_input.as_bytes(), &signature)
        .map_err(|_| JwsError::SignatureMismatch)?;

    Ok(VerifiedJws {
        header,
        payload: payload_bytes,
    })
}
