//! SD-JWT parsing and verification.

use p256::PublicKey;
use p256::ecdsa::signature::Verifier;
use p256::ecdsa::{Signature, VerifyingKey};
use serde::{Deserialize, Serialize};
#[cfg(feature = "tracing")]
use tracing::{debug, instrument, warn};

use crate::error::LastIDError;

/// Wrapper for SD-JWT presentations.
///
/// Parses SD-JWT format (JWS + disclosures separated by `~`).
#[derive(Debug, Clone)]
pub struct VerifiablePresentation {
    /// Raw SD-JWT string
    raw: String,
    /// Decoded JWT header
    header: serde_json::Value,
    /// Decoded JWT payload
    payload: serde_json::Value,
    /// Selective disclosure claims
    disclosures: Vec<Disclosure>,
}

impl VerifiablePresentation {
    /// Parse an SD-JWT presentation string.
    ///
    /// # Format
    ///
    /// SD-JWT format: `<jws>~<disclosure1>~<disclosure2>~...~<kb-jwt>`
    ///
    /// # Errors
    ///
    /// Returns `LastIDError::InvalidCredential` if parsing fails.
    #[cfg_attr(feature = "tracing", instrument(skip(sdjwt), fields(sdjwt_len = sdjwt.len())))]
    pub fn parse(sdjwt: &str) -> Result<Self, LastIDError> {
        #[cfg(feature = "tracing")]
        debug!("Parsing SD-JWT presentation");
        // Split SD-JWT into parts
        let parts: Vec<&str> = sdjwt.split('~').collect();
        if parts.is_empty() {
            return Err(LastIDError::invalid_credential("Empty SD-JWT"));
        }

        // Parse the main JWT (first part)
        let jwt = parts[0];
        let jwt_parts: Vec<&str> = jwt.split('.').collect();
        if jwt_parts.len() != 3 {
            return Err(LastIDError::invalid_credential(
                "Invalid JWT format: expected 3 parts",
            ));
        }

        // Decode header
        let header = base64_url_decode(jwt_parts[0])
            .and_then(|bytes| {
                serde_json::from_slice(&bytes)
                    .map_err(|e| LastIDError::invalid_credential(format!("Invalid header: {e}")))
            })
            .map_err(|e| {
                LastIDError::invalid_credential(format!("Failed to decode header: {e}"))
            })?;

        // Decode payload
        let payload = base64_url_decode(jwt_parts[1])
            .and_then(|bytes| {
                serde_json::from_slice(&bytes)
                    .map_err(|e| LastIDError::invalid_credential(format!("Invalid payload: {e}")))
            })
            .map_err(|e| {
                LastIDError::invalid_credential(format!("Failed to decode payload: {e}"))
            })?;

        // Parse disclosures (parts 1..n-1, last part may be key binding JWT)
        let disclosures = parts[1..]
            .iter()
            .filter(|s| !s.is_empty())
            .filter_map(|disclosure| Disclosure::parse(disclosure).ok())
            .collect();

        Ok(Self {
            raw: sdjwt.to_string(),
            header,
            payload,
            disclosures,
        })
    }

    /// Get the raw SD-JWT string.
    #[must_use]
    pub fn raw(&self) -> &str {
        &self.raw
    }

    /// Get the JWT header.
    #[must_use]
    pub const fn header(&self) -> &serde_json::Value {
        &self.header
    }

    /// Get the JWT payload.
    #[must_use]
    pub const fn payload(&self) -> &serde_json::Value {
        &self.payload
    }

    /// Get the disclosures.
    #[must_use]
    pub fn disclosures(&self) -> &[Disclosure] {
        &self.disclosures
    }

    /// Get the issuer DID from the payload.
    ///
    /// # Errors
    ///
    /// Returns error if `iss` claim is missing or not a string.
    pub fn issuer_did(&self) -> Result<&str, LastIDError> {
        self.payload
            .get("iss")
            .and_then(|v| v.as_str())
            .ok_or_else(|| LastIDError::invalid_credential("Missing or invalid 'iss' claim"))
    }

    /// Get the subject DID from the payload.
    ///
    /// # Errors
    ///
    /// Returns error if `sub` claim is missing or not a string.
    pub fn subject_did(&self) -> Result<&str, LastIDError> {
        self.payload
            .get("sub")
            .and_then(|v| v.as_str())
            .ok_or_else(|| LastIDError::invalid_credential("Missing or invalid 'sub' claim"))
    }

    /// Get the credential type from the payload.
    ///
    /// Looks for `vct` (verifiable credential type) claim.
    ///
    /// # Errors
    ///
    /// Returns error if credential type is missing.
    pub fn credential_type(&self) -> Result<&str, LastIDError> {
        self.payload
            .get("vct")
            .and_then(|v| v.as_str())
            .ok_or_else(|| LastIDError::invalid_credential("Missing or invalid 'vct' claim"))
    }

    /// Get the issued-at timestamp.
    ///
    /// # Errors
    ///
    /// Returns error if `iat` claim is missing or not a number.
    pub fn issued_at(&self) -> Result<i64, LastIDError> {
        self.payload
            .get("iat")
            .and_then(serde_json::Value::as_i64)
            .ok_or_else(|| LastIDError::invalid_credential("Missing or invalid 'iat' claim"))
    }

    /// Get the expiration timestamp.
    ///
    /// # Errors
    ///
    /// Returns error if `exp` claim is missing or not a number.
    pub fn expires_at(&self) -> Result<i64, LastIDError> {
        self.payload
            .get("exp")
            .and_then(serde_json::Value::as_i64)
            .ok_or_else(|| LastIDError::invalid_credential("Missing or invalid 'exp' claim"))
    }

    /// Get the not-before timestamp (SEC-009).
    ///
    /// The `nbf` claim specifies the time before which the credential
    /// must not be accepted for processing.
    ///
    /// # Returns
    ///
    /// - `Ok(Some(timestamp))` if `nbf` claim is present
    /// - `Ok(None)` if `nbf` claim is not present
    /// - `Err` if `nbf` claim is present but not a valid number
    pub fn not_before(&self) -> Result<Option<i64>, LastIDError> {
        self.payload.get("nbf").map_or(Ok(None), |v| {
            v.as_i64()
                .map(Some)
                .ok_or_else(|| LastIDError::invalid_credential("Invalid 'nbf' claim: not a number"))
        })
    }

    /// Check if the credential is currently valid based on time claims.
    ///
    /// Validates both `exp` (expiration) and `nbf` (not-before) claims
    /// against the provided current timestamp.
    ///
    /// # Arguments
    ///
    /// * `current_time` - Current Unix timestamp in seconds
    ///
    /// # Errors
    ///
    /// Returns error if:
    /// - The credential has expired (`exp` < `current_time`)
    /// - The credential is not yet valid (`nbf` > `current_time`)
    /// - Time claims are missing or invalid
    pub fn validate_time_claims(&self, current_time: i64) -> Result<(), LastIDError> {
        // Check expiration
        let exp = self.expires_at()?;
        if current_time > exp {
            return Err(LastIDError::invalid_credential(format!(
                "Credential expired at {exp}, current time is {current_time}"
            )));
        }

        // Check not-before if present
        if let Some(nbf) = self.not_before()?
            && current_time < nbf
        {
            return Err(LastIDError::invalid_credential(format!(
                "Credential not yet valid: valid from {nbf}, current time is {current_time}"
            )));
        }

        Ok(())
    }

    /// Verify the SD-JWT signature using the issuer's public key.
    ///
    /// This verifies that the JWT was signed by the expected issuer using ES256
    /// (ECDSA P-256).
    ///
    /// # Arguments
    ///
    /// * `public_key_jwk` - The issuer's public key in JWK format
    ///
    /// # Errors
    ///
    /// Returns `LastIDError::InvalidCredential` if:
    /// - The signature is invalid
    /// - The public key format is invalid
    /// - The algorithm is not ES256
    #[cfg_attr(feature = "tracing", instrument(skip(self, public_key_jwk)))]
    pub fn verify_signature(&self, public_key_jwk: &serde_json::Value) -> Result<(), LastIDError> {
        #[cfg(feature = "tracing")]
        debug!("Verifying SD-JWT signature");

        // Verify algorithm is ES256
        let alg = self
            .header
            .get("alg")
            .and_then(|v| v.as_str())
            .ok_or_else(|| LastIDError::invalid_credential("Missing 'alg' in JWT header"))?;

        if alg != "ES256" {
            #[cfg(feature = "tracing")]
            warn!(algorithm = alg, "Unsupported signature algorithm");
            return Err(LastIDError::invalid_credential(format!(
                "Unsupported algorithm: {alg}. Only ES256 is supported."
            )));
        }

        #[cfg(feature = "tracing")]
        debug!(algorithm = alg, "Algorithm validated");

        // Parse the public key from JWK
        let public_key = parse_jwk_to_public_key(public_key_jwk)?;
        let verifying_key = VerifyingKey::from(&public_key);

        #[cfg(feature = "tracing")]
        debug!("Public key parsed successfully");

        // Extract JWT parts from raw
        let jwt = self.raw.split('~').next().ok_or_else(|| {
            LastIDError::invalid_credential("Invalid SD-JWT format: missing JWT component")
        })?;

        let parts: Vec<&str> = jwt.split('.').collect();
        if parts.len() != 3 {
            return Err(LastIDError::invalid_credential(
                "Invalid JWT format: expected 3 parts",
            ));
        }

        let signing_input = format!("{}.{}", parts[0], parts[1]);
        let signature_bytes = base64_url_decode(parts[2])?;

        // Parse the signature (64 bytes for P-256)
        let signature = Signature::from_slice(&signature_bytes).map_err(|e| {
            LastIDError::invalid_credential(format!("Invalid signature format: {e}"))
        })?;

        // Verify the signature
        verifying_key
            .verify(signing_input.as_bytes(), &signature)
            .map_err(|e| {
                #[cfg(feature = "tracing")]
                warn!("Signature verification failed: {}", e);
                LastIDError::invalid_credential(format!("Signature verification failed: {e}"))
            })?;

        #[cfg(feature = "tracing")]
        debug!("Signature verified successfully");

        Ok(())
    }

    /// Reconstruct claims by applying disclosures to the payload.
    ///
    /// SD-JWT uses `_sd` arrays to reference selective disclosure claims.
    /// This method merges disclosed claims into the payload.
    #[must_use]
    pub fn reconstruct_claims(&self) -> serde_json::Value {
        #[cfg(feature = "tracing")]
        debug!(
            disclosure_count = self.disclosures.len(),
            "Reconstructing claims from disclosures"
        );

        let mut claims = self.payload.clone();

        // Add disclosed claims
        for disclosure in &self.disclosures {
            if let Some(claim_name) = &disclosure.claim {
                #[cfg(feature = "tracing")]
                debug!(claim = claim_name, "Adding disclosed claim");
                claims[claim_name] = disclosure.value.clone();
            }
        }

        // Remove SD-JWT metadata
        if let Some(obj) = claims.as_object_mut() {
            obj.remove("_sd");
            obj.remove("_sd_alg");
        }

        claims
    }
}

/// SD-JWT selective disclosure.
///
/// Format: `base64url([salt, claim_name, claim_value])`
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Disclosure {
    /// Random salt for privacy
    pub salt: String,
    /// Claim name (may be empty for array elements)
    pub claim: Option<String>,
    /// Claim value
    pub value: serde_json::Value,
}

impl Disclosure {
    /// Parse a disclosure string.
    ///
    /// # Errors
    ///
    /// Returns error if the disclosure format is invalid.
    #[cfg_attr(feature = "tracing", instrument(skip(disclosure), fields(disclosure_len = disclosure.len())))]
    pub fn parse(disclosure: &str) -> Result<Self, LastIDError> {
        let bytes = base64_url_decode(disclosure)?;
        let arr: Vec<serde_json::Value> = serde_json::from_slice(&bytes)
            .map_err(|e| LastIDError::invalid_credential(format!("Invalid disclosure: {e}")))?;

        if arr.len() < 2 {
            #[cfg(feature = "tracing")]
            warn!(elements = arr.len(), "Disclosure has insufficient elements");
            return Err(LastIDError::invalid_credential(
                "Disclosure must have at least 2 elements",
            ));
        }

        let salt = arr[0]
            .as_str()
            .ok_or_else(|| LastIDError::invalid_credential("Disclosure salt must be string"))?
            .to_string();

        // Two-element array: [salt, value] (for array items)
        // Three-element array: [salt, claim_name, value]
        let (claim, value) = if arr.len() == 2 {
            (None, arr[1].clone())
        } else {
            let claim_name = arr[1]
                .as_str()
                .ok_or_else(|| LastIDError::invalid_credential("Disclosure claim must be string"))?
                .to_string();
            (Some(claim_name), arr[2].clone())
        };

        #[cfg(feature = "tracing")]
        debug!(claim = ?claim, "Parsed disclosure successfully");

        Ok(Self { salt, claim, value })
    }
}

/// Base64 URL-safe decoding.
fn base64_url_decode(input: &str) -> Result<Vec<u8>, LastIDError> {
    use base64::Engine;
    use base64::engine::general_purpose::URL_SAFE_NO_PAD;

    URL_SAFE_NO_PAD
        .decode(input)
        .map_err(|e| LastIDError::invalid_credential(format!("Base64 decode error: {e}")))
}

/// Parse a JWK to a P-256 public key.
///
/// Expected JWK format:
/// ```json
/// {
///   "kty": "EC",
///   "crv": "P-256",
///   "x": "<base64url>",
///   "y": "<base64url>"
/// }
/// ```
fn parse_jwk_to_public_key(jwk: &serde_json::Value) -> Result<PublicKey, LastIDError> {
    use base64::Engine;
    use base64::engine::general_purpose::URL_SAFE_NO_PAD;

    // Validate key type
    let kty = jwk
        .get("kty")
        .and_then(|v| v.as_str())
        .ok_or_else(|| LastIDError::invalid_credential("JWK missing 'kty' field"))?;

    if kty != "EC" {
        return Err(LastIDError::invalid_credential(format!(
            "Invalid key type: {kty}. Expected 'EC'"
        )));
    }

    // Validate curve
    let crv = jwk
        .get("crv")
        .and_then(|v| v.as_str())
        .ok_or_else(|| LastIDError::invalid_credential("JWK missing 'crv' field"))?;

    if crv != "P-256" {
        return Err(LastIDError::invalid_credential(format!(
            "Invalid curve: {crv}. Expected 'P-256'"
        )));
    }

    // Extract x and y coordinates
    let x_b64 = jwk
        .get("x")
        .and_then(|v| v.as_str())
        .ok_or_else(|| LastIDError::invalid_credential("JWK missing 'x' coordinate"))?;

    let y_b64 = jwk
        .get("y")
        .and_then(|v| v.as_str())
        .ok_or_else(|| LastIDError::invalid_credential("JWK missing 'y' coordinate"))?;

    let x_bytes = URL_SAFE_NO_PAD
        .decode(x_b64)
        .map_err(|e| LastIDError::invalid_credential(format!("Invalid 'x' coordinate: {e}")))?;

    let y_bytes = URL_SAFE_NO_PAD
        .decode(y_b64)
        .map_err(|e| LastIDError::invalid_credential(format!("Invalid 'y' coordinate: {e}")))?;

    // Validate coordinate lengths (32 bytes each for P-256)
    if x_bytes.len() != 32 || y_bytes.len() != 32 {
        return Err(LastIDError::invalid_credential(
            "Invalid coordinate length for P-256",
        ));
    }

    // Construct uncompressed SEC1 point: 0x04 || x || y
    let mut sec1_point = vec![0x04];
    sec1_point.extend_from_slice(&x_bytes);
    sec1_point.extend_from_slice(&y_bytes);

    // Parse as P-256 public key
    PublicKey::from_sec1_bytes(&sec1_point)
        .map_err(|e| LastIDError::invalid_credential(format!("Invalid public key: {e}")))
}

#[cfg(test)]
mod tests {
    use base64::Engine;
    use base64::engine::general_purpose::URL_SAFE_NO_PAD;

    use super::*;

    fn create_test_jwt() -> String {
        let header = serde_json::json!({
            "alg": "ES256",
            "typ": "vc+sd-jwt"
        });
        let payload = serde_json::json!({
            "iss": "did:lastid:issuer",
            "sub": "did:lastid:subject",
            "vct": "LastID.Base",
            "iat": 1_700_000_000,
            "exp": 1_700_086_400,
            "_sd": ["hash1", "hash2"]
        });

        let header_b64 = URL_SAFE_NO_PAD.encode(serde_json::to_vec(&header).unwrap());
        let payload_b64 = URL_SAFE_NO_PAD.encode(serde_json::to_vec(&payload).unwrap());

        format!("{}.{}.fake_signature", header_b64, payload_b64)
    }

    fn create_disclosure(salt: &str, claim: &str, value: serde_json::Value) -> String {
        let arr = serde_json::json!([salt, claim, value]);
        URL_SAFE_NO_PAD.encode(serde_json::to_vec(&arr).unwrap())
    }

    #[test]
    fn test_parse_sdjwt() {
        let jwt = create_test_jwt();
        let sdjwt = format!("{}~", jwt);

        let vp = VerifiablePresentation::parse(&sdjwt).unwrap();
        assert_eq!(vp.issuer_did().unwrap(), "did:lastid:issuer");
        assert_eq!(vp.subject_did().unwrap(), "did:lastid:subject");
        assert_eq!(vp.credential_type().unwrap(), "LastID.Base");
    }

    #[test]
    fn test_parse_with_disclosures() {
        let jwt = create_test_jwt();
        let disclosure1 =
            create_disclosure("salt1", "email", serde_json::json!("user@example.com"));
        let disclosure2 = create_disclosure("salt2", "name", serde_json::json!("John Doe"));

        let sdjwt = format!("{}~{}~{}", jwt, disclosure1, disclosure2);
        let vp = VerifiablePresentation::parse(&sdjwt).unwrap();

        assert_eq!(vp.disclosures().len(), 2);
        assert_eq!(vp.disclosures()[0].claim, Some("email".into()));
        assert_eq!(vp.disclosures()[1].claim, Some("name".into()));
    }

    #[test]
    fn test_reconstruct_claims() {
        let jwt = create_test_jwt();
        let disclosure = create_disclosure("salt1", "email", serde_json::json!("user@example.com"));

        let sdjwt = format!("{}~{}", jwt, disclosure);
        let vp = VerifiablePresentation::parse(&sdjwt).unwrap();

        let claims = vp.reconstruct_claims();
        assert_eq!(claims["email"], "user@example.com");
        assert!(claims.get("_sd").is_none());
    }

    #[test]
    fn test_invalid_jwt() {
        let result = VerifiablePresentation::parse("invalid");
        assert!(result.is_err());
    }

    #[test]
    fn test_empty_sdjwt() {
        let result = VerifiablePresentation::parse("");
        assert!(result.is_err());
    }

    #[test]
    fn test_parse_real_disclosure() {
        // Real disclosure from production: ["xOt6WFHtw_3Hc4zqO7xegA","display_name","Matt Jezorek"]
        let disclosure =
            "WyJ4T3Q2V0ZIdHdfM0hjNHpxTzd4ZWdBIiwiZGlzcGxheV9uYW1lIiwiTWF0dCBKZXpvcmVrIl0";
        let parsed = Disclosure::parse(disclosure).unwrap();
        assert_eq!(parsed.claim, Some("display_name".to_string()));
        assert_eq!(parsed.value, serde_json::json!("Matt Jezorek"));
    }

    #[test]
    fn test_parse_real_disclosure_with_array_value() {
        // Real disclosure: ["UjBCzWLF1JEm1RlUdoARcQ","email",["matt@lastid.co"]]
        let disclosure = "WyJVakJDeldMRjFKRW0xUmxVZG9BUmNRIiwiZW1haWwiLFsibWF0dEBsYXN0aWQuY28iXV0";
        let parsed = Disclosure::parse(disclosure).unwrap();
        assert_eq!(parsed.claim, Some("email".to_string()));
        assert_eq!(parsed.value, serde_json::json!(["matt@lastid.co"]));
    }

    #[test]
    fn test_sdjwt_with_kb_jwt_at_end() {
        // SD-JWT format: issuer-jwt~disclosures~kb-jwt
        // KB-JWT should be ignored (not a valid disclosure)
        let jwt = create_test_jwt();
        let disclosure = create_disclosure("salt1", "email", serde_json::json!("user@example.com"));

        // Create a fake KB-JWT (just needs to look like a JWT)
        let kb_jwt = "eyJhbGciOiJFUzI1NiIsInR5cCI6ImtiK2p3dCJ9.eyJhdWQiOiJ0ZXN0In0.fake_sig";

        let sdjwt = format!("{}~{}~{}", jwt, disclosure, kb_jwt);
        let vp = VerifiablePresentation::parse(&sdjwt).unwrap();

        // Should only have 1 disclosure (KB-JWT filtered out)
        assert_eq!(vp.disclosures().len(), 1);
        assert_eq!(vp.disclosures()[0].claim, Some("email".to_string()));

        let claims = vp.reconstruct_claims();
        assert_eq!(claims["email"], "user@example.com");
    }

    #[test]
    fn test_full_sdjwt_with_multiple_disclosures() {
        // Simulates a Persona credential with 4 disclosures + KB-JWT
        let jwt = create_test_jwt();
        let d1 = create_disclosure("salt1", "display_name", serde_json::json!("Matt Jezorek"));
        let d2 = create_disclosure("salt2", "email", serde_json::json!(["matt@lastid.co"]));
        let d3 = create_disclosure("salt3", "phone", serde_json::json!(["5093091714"]));
        let d4 = create_disclosure("salt4", "job_title", serde_json::json!("CEO"));
        let kb_jwt = "eyJhbGciOiJFUzI1NiIsInR5cCI6ImtiK2p3dCJ9.eyJhdWQiOiJ0ZXN0In0.fake_sig";

        let sdjwt = format!("{}~{}~{}~{}~{}~{}", jwt, d1, d2, d3, d4, kb_jwt);
        let vp = VerifiablePresentation::parse(&sdjwt).unwrap();

        // Should have 4 disclosures (KB-JWT filtered out)
        assert_eq!(
            vp.disclosures().len(),
            4,
            "Expected 4 disclosures, got {:?}",
            vp.disclosures()
        );

        let claims = vp.reconstruct_claims();

        // Verify all disclosed claims are present
        assert_eq!(claims["display_name"], "Matt Jezorek");
        assert_eq!(claims["email"], serde_json::json!(["matt@lastid.co"]));
        assert_eq!(claims["phone"], serde_json::json!(["5093091714"]));
        assert_eq!(claims["job_title"], "CEO");

        // Original payload claims should still be there
        assert_eq!(claims["iss"], "did:lastid:issuer");
        assert_eq!(claims["vct"], "LastID.Base");

        // SD metadata should be removed
        assert!(claims.get("_sd").is_none());
        assert!(claims.get("_sd_alg").is_none());
    }

    // =========================================================================
    // Signature verification tests
    // =========================================================================

    #[test]
    fn test_parse_jwk_valid() {
        // Generate a valid P-256 key and convert to JWK
        use p256::ecdsa::SigningKey;
        use rand::rngs::OsRng;

        let signing_key = SigningKey::random(&mut OsRng);
        let verifying_key = signing_key.verifying_key();
        let public_key = verifying_key.to_encoded_point(false);

        // Extract x and y coordinates (skip first byte which is 0x04 for uncompressed)
        let x_bytes = &public_key.as_bytes()[1..33];
        let y_bytes = &public_key.as_bytes()[33..65];

        let jwk = serde_json::json!({
            "kty": "EC",
            "crv": "P-256",
            "x": URL_SAFE_NO_PAD.encode(x_bytes),
            "y": URL_SAFE_NO_PAD.encode(y_bytes)
        });

        let result = parse_jwk_to_public_key(&jwk);
        assert!(
            result.is_ok(),
            "Failed to parse valid JWK: {:?}",
            result.err()
        );
    }

    #[test]
    fn test_parse_jwk_wrong_kty() {
        let jwk = serde_json::json!({
            "kty": "RSA",
            "crv": "P-256",
            "x": "WbbXWlY0Sdw6eVKiJGnFFAJnGDHVjdVRLWbZGIgn3Go",
            "y": "bfIuXkJHLZvVMZ96ekEqhyuSoVVbWBzWqcAzSvhp2gs"
        });

        let result = parse_jwk_to_public_key(&jwk);
        assert!(result.is_err());
        assert!(result.unwrap_err().to_string().contains("Invalid key type"));
    }

    #[test]
    fn test_parse_jwk_wrong_crv() {
        let jwk = serde_json::json!({
            "kty": "EC",
            "crv": "P-384",
            "x": "WbbXWlY0Sdw6eVKiJGnFFAJnGDHVjdVRLWbZGIgn3Go",
            "y": "bfIuXkJHLZvVMZ96ekEqhyuSoVVbWBzWqcAzSvhp2gs"
        });

        let result = parse_jwk_to_public_key(&jwk);
        assert!(result.is_err());
        assert!(result.unwrap_err().to_string().contains("Invalid curve"));
    }

    #[test]
    fn test_parse_jwk_missing_x() {
        let jwk = serde_json::json!({
            "kty": "EC",
            "crv": "P-256",
            "y": "bfIuXkJHLZvVMZ96ekEqhyuSoVVbWBzWqcAzSvhp2gs"
        });

        let result = parse_jwk_to_public_key(&jwk);
        assert!(result.is_err());
        assert!(result.unwrap_err().to_string().contains("'x' coordinate"));
    }

    #[test]
    fn test_parse_jwk_missing_y() {
        let jwk = serde_json::json!({
            "kty": "EC",
            "crv": "P-256",
            "x": "WbbXWlY0Sdw6eVKiJGnFFAJnGDHVjdVRLWbZGIgn3Go"
        });

        let result = parse_jwk_to_public_key(&jwk);
        assert!(result.is_err());
        assert!(result.unwrap_err().to_string().contains("'y' coordinate"));
    }

    #[test]
    fn test_verify_signature_wrong_algorithm() {
        let header = serde_json::json!({
            "alg": "RS256",  // Wrong algorithm
            "typ": "vc+sd-jwt"
        });
        let payload = serde_json::json!({
            "iss": "did:lastid:issuer",
            "sub": "did:lastid:subject",
            "iat": 1_700_000_000,
            "exp": 1_700_086_400
        });

        let header_b64 = URL_SAFE_NO_PAD.encode(serde_json::to_vec(&header).unwrap());
        let payload_b64 = URL_SAFE_NO_PAD.encode(serde_json::to_vec(&payload).unwrap());
        let jwt = format!("{}.{}.fake_signature", header_b64, payload_b64);

        let vp = VerifiablePresentation::parse(&jwt).unwrap();

        let jwk = serde_json::json!({
            "kty": "EC",
            "crv": "P-256",
            "x": "WbbXWlY0Sdw6eVKiJGnFFAJnGDHVjdVRLWbZGIgn3Go",
            "y": "bfIuXkJHLZvVMZ96ekEqhyuSoVVbWBzWqcAzSvhp2gs"
        });

        let result = vp.verify_signature(&jwk);
        assert!(result.is_err());
        assert!(
            result
                .unwrap_err()
                .to_string()
                .contains("Unsupported algorithm")
        );
    }
}
