//! Credential fixture builder.

use serde_json::json;

/// Builder for creating test credential JWTs.
///
/// # Example
///
/// ```rust
/// use lastid_sdk::test_utils::fixtures::CredentialBuilder;
///
/// let credential_jwt = CredentialBuilder::new("LastID.Base")
///     .with_issuer("did:lastid:test-issuer")
///     .with_subject("did:lastid:test-subject")
///     .with_claim("email", "test@example.com")
///     .build_jwt();
/// ```
#[derive(Debug, Clone)]
pub struct CredentialBuilder {
    credential_type: String,
    issuer: String,
    subject: String,
    claims: serde_json::Map<String, serde_json::Value>,
    issued_at: Option<String>,
    expires_at: Option<String>,
}

impl CredentialBuilder {
    /// Create a new credential builder with the credential type.
    #[must_use]
    pub fn new(credential_type: impl Into<String>) -> Self {
        Self {
            credential_type: credential_type.into(),
            issuer: "did:lastid:default-issuer".to_string(),
            subject: "did:lastid:default-subject".to_string(),
            claims: serde_json::Map::new(),
            issued_at: None,
            expires_at: None,
        }
    }

    /// Set the issuer DID.
    #[must_use]
    pub fn with_issuer(mut self, issuer: impl Into<String>) -> Self {
        self.issuer = issuer.into();
        self
    }

    /// Set the subject DID.
    #[must_use]
    pub fn with_subject(mut self, subject: impl Into<String>) -> Self {
        self.subject = subject.into();
        self
    }

    /// Add a claim to the credential.
    #[must_use]
    pub fn with_claim(
        mut self,
        key: impl Into<String>,
        value: impl Into<serde_json::Value>,
    ) -> Self {
        self.claims.insert(key.into(), value.into());
        self
    }

    /// Set the issuance timestamp (ISO 8601 format).
    #[must_use]
    pub fn issued_at(mut self, timestamp: impl Into<String>) -> Self {
        self.issued_at = Some(timestamp.into());
        self
    }

    /// Set the expiration timestamp (ISO 8601 format).
    #[must_use]
    pub fn expires_at(mut self, timestamp: impl Into<String>) -> Self {
        self.expires_at = Some(timestamp.into());
        self
    }

    /// Build the credential as a JSON object.
    #[must_use]
    pub fn build_json(&self) -> serde_json::Value {
        let mut vc = json!({
            "@context": [
                "https://www.w3.org/2018/credentials/v1",
                "https://lastid.io/credentials/v1"
            ],
            "type": ["VerifiableCredential", &self.credential_type],
            "issuer": &self.issuer,
            "credentialSubject": {
                "id": &self.subject
            }
        });

        // Add claims to credentialSubject
        if let Some(subject) = vc["credentialSubject"].as_object_mut() {
            for (key, value) in &self.claims {
                subject.insert(key.clone(), value.clone());
            }
        }

        // Add timestamps
        if let Some(ref iat) = self.issued_at {
            vc["issuanceDate"] = json!(iat);
        } else {
            vc["issuanceDate"] = json!("2024-01-01T00:00:00Z");
        }

        if let Some(ref exp) = self.expires_at {
            vc["expirationDate"] = json!(exp);
        }

        vc
    }

    /// Build a mock JWT string (for testing - not cryptographically valid).
    ///
    /// The JWT has a valid structure but uses a placeholder signature.
    /// Use this for testing parsing and flow logic, not cryptographic
    /// verification.
    #[must_use]
    pub fn build_jwt(&self) -> String {
        use base64::Engine;
        use base64::engine::general_purpose::URL_SAFE_NO_PAD;

        let header = json!({
            "alg": "ES256",
            "typ": "JWT"
        });

        let payload = json!({
            "iss": &self.issuer,
            "sub": &self.subject,
            "iat": 1_704_067_200, // 2024-01-01
            "exp": 1_735_689_600, // 2025-01-01
            "vc": self.build_json()
        });

        let header_b64 = URL_SAFE_NO_PAD.encode(header.to_string().as_bytes());
        let payload_b64 = URL_SAFE_NO_PAD.encode(payload.to_string().as_bytes());
        // Placeholder signature (not cryptographically valid)
        let sig_b64 = URL_SAFE_NO_PAD.encode(b"test-signature-placeholder");

        format!("{header_b64}.{payload_b64}.{sig_b64}")
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_builder_basic() {
        let jwt = CredentialBuilder::new("LastID.Base")
            .with_issuer("did:lastid:issuer")
            .with_subject("did:lastid:subject")
            .build_jwt();

        // Verify JWT structure
        let parts: Vec<&str> = jwt.split('.').collect();
        assert_eq!(parts.len(), 3);
    }

    #[test]
    fn test_builder_with_claims() {
        let json = CredentialBuilder::new("LastID.VerifiedEmail")
            .with_issuer("did:lastid:issuer")
            .with_subject("did:lastid:subject")
            .with_claim("email", "test@example.com")
            .with_claim("verified", true)
            .build_json();

        assert_eq!(json["credentialSubject"]["email"], "test@example.com");
        assert_eq!(json["credentialSubject"]["verified"], true);
    }
}
