//! Presentation fixture builder.

use serde_json::json;

/// Builder for creating test verifiable presentations.
///
/// # Example
///
/// ```rust
/// use lastid_sdk::test_utils::fixtures::{
///     CredentialBuilder, PresentationBuilder,
/// };
///
/// let credential = CredentialBuilder::new("LastID.Base")
///     .with_issuer("did:lastid:issuer")
///     .build_jwt();
///
/// let presentation = PresentationBuilder::new()
///     .with_holder("did:lastid:holder")
///     .with_credential(credential)
///     .build_json();
/// ```
#[derive(Debug, Clone)]
pub struct PresentationBuilder {
    holder: String,
    credentials: Vec<String>,
    challenge: Option<String>,
    domain: Option<String>,
}

impl Default for PresentationBuilder {
    fn default() -> Self {
        Self::new()
    }
}

impl PresentationBuilder {
    /// Create a new presentation builder.
    #[must_use]
    pub fn new() -> Self {
        Self {
            holder: "did:lastid:default-holder".to_string(),
            credentials: Vec::new(),
            challenge: None,
            domain: None,
        }
    }

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

    /// Add a credential JWT to the presentation.
    #[must_use]
    pub fn with_credential(mut self, credential_jwt: impl Into<String>) -> Self {
        self.credentials.push(credential_jwt.into());
        self
    }

    /// Add multiple credentials.
    #[must_use]
    pub fn with_credentials(mut self, credentials: Vec<String>) -> Self {
        self.credentials.extend(credentials);
        self
    }

    /// Set the challenge for the presentation (for replay protection).
    #[must_use]
    pub fn with_challenge(mut self, challenge: impl Into<String>) -> Self {
        self.challenge = Some(challenge.into());
        self
    }

    /// Set the domain for the presentation.
    #[must_use]
    pub fn with_domain(mut self, domain: impl Into<String>) -> Self {
        self.domain = Some(domain.into());
        self
    }

    /// Build the presentation as a JSON object.
    #[must_use]
    pub fn build_json(&self) -> serde_json::Value {
        let mut vp = json!({
            "@context": [
                "https://www.w3.org/2018/credentials/v1"
            ],
            "type": ["VerifiablePresentation"],
            "holder": &self.holder,
            "verifiableCredential": &self.credentials
        });

        if let Some(ref challenge) = self.challenge {
            vp["proof"] = json!({
                "type": "EcdsaSecp256r1Signature2019",
                "challenge": challenge,
                "domain": self.domain.as_deref().unwrap_or("https://example.com"),
                "created": "2024-01-01T00:00:00Z"
            });
        }

        vp
    }

    /// Build a mock JWT presentation (for testing - not cryptographically
    /// valid).
    #[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.holder,
            "aud": self.domain.as_deref().unwrap_or("https://example.com"),
            "nonce": self.challenge.as_deref().unwrap_or("default-nonce"),
            "iat": 1_704_067_200,
            "exp": 1_704_153_600, // 1 day later
            "vp": 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());
        let sig_b64 = URL_SAFE_NO_PAD.encode(b"test-presentation-sig");

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

#[cfg(test)]
mod tests {
    use super::*;
    use crate::test_utils::fixtures::CredentialBuilder;

    #[test]
    fn test_builder_basic() {
        let presentation = PresentationBuilder::new()
            .with_holder("did:lastid:holder")
            .build_json();

        assert_eq!(presentation["holder"], "did:lastid:holder");
        assert!(
            presentation["verifiableCredential"]
                .as_array()
                .unwrap()
                .is_empty()
        );
    }

    #[test]
    fn test_builder_with_credential() {
        let credential = CredentialBuilder::new("LastID.Base")
            .with_issuer("did:lastid:issuer")
            .build_jwt();

        let presentation = PresentationBuilder::new()
            .with_holder("did:lastid:holder")
            .with_credential(credential)
            .with_challenge("test-challenge")
            .with_domain("https://verifier.example.com")
            .build_json();

        assert_eq!(
            presentation["verifiableCredential"]
                .as_array()
                .unwrap()
                .len(),
            1
        );
        assert!(presentation["proof"].is_object());
    }

    #[test]
    fn test_builder_jwt() {
        let jwt = PresentationBuilder::new()
            .with_holder("did:lastid:holder")
            .build_jwt();

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