//! Verified persona (KYC) credential policy builder for WASM.

use wasm_bindgen::prelude::*;

use crate::error::LastIDError;
use crate::policies::PolicyBuilder;
use crate::types::CredentialPolicyRequest;

/// Verified persona (KYC) credential policy builder for WASM.
///
/// # JavaScript Usage
///
/// ```javascript
/// const policy = new VerifiedPersonaPolicy()
///     .withVerificationLevel("Enhanced")
///     .withFields(["legal_name", "date_of_birth"])
///     .withOptionalFields(["nationality", "residential_address"])
///     .withCallback("https://yourapp.com/callback");
/// ```
#[wasm_bindgen(js_name = VerifiedPersonaPolicy)]
pub struct WasmVerifiedPersonaPolicy {
    callback_url: Option<String>,
    fields: Vec<String>,
    optional_fields: Vec<String>,
    verification_level: Option<String>,
}

#[wasm_bindgen(js_class = VerifiedPersonaPolicy)]
impl WasmVerifiedPersonaPolicy {
    #[wasm_bindgen(constructor)]
    pub fn new() -> Self {
        Self {
            callback_url: None,
            fields: Vec::new(),
            optional_fields: Vec::new(),
            verification_level: None,
        }
    }

    /// Set the required verification level: Basic, Standard, or Enhanced.
    #[wasm_bindgen(js_name = withVerificationLevel)]
    pub fn with_verification_level(mut self, level: String) -> Self {
        self.verification_level = Some(level);
        self
    }

    /// Add required fields that must be disclosed.
    /// Valid fields: legal_name, first_name, last_name, date_of_birth, age,
    /// nationality, residential_address, structured_address, government_id_type,
    /// document_number, liveness_score, face_match_score
    #[wasm_bindgen(js_name = withFields)]
    pub fn with_fields(mut self, fields: Vec<String>) -> Self {
        self.fields.extend(fields);
        self
    }

    #[wasm_bindgen(js_name = withField)]
    pub fn with_field(mut self, field: String) -> Self {
        self.fields.push(field);
        self
    }

    /// Add optional fields that the user can choose to disclose.
    /// Valid fields: legal_name, first_name, last_name, date_of_birth, age,
    /// nationality, residential_address, structured_address, government_id_type,
    /// document_number, liveness_score, face_match_score
    #[wasm_bindgen(js_name = withOptionalFields)]
    pub fn with_optional_fields(mut self, fields: Vec<String>) -> Self {
        self.optional_fields.extend(fields);
        self
    }

    #[wasm_bindgen(js_name = withOptionalField)]
    pub fn with_optional_field(mut self, field: String) -> Self {
        self.optional_fields.push(field);
        self
    }

    #[wasm_bindgen(js_name = withCallback)]
    pub fn with_callback(mut self, url: String) -> Self {
        self.callback_url = Some(url);
        self
    }

    #[wasm_bindgen(js_name = credentialType, getter)]
    pub fn credential_type(&self) -> String {
        "LastID.VerifiedPersona".to_string()
    }

    #[wasm_bindgen(js_name = isComplete, getter)]
    pub fn is_complete(&self) -> bool {
        self.callback_url.is_some()
    }
}

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

/// Map field name from JS input to snake_case for the API.
fn map_verified_persona_field(field: &str) -> &str {
    match field {
        // Support both PascalCase (legacy) and snake_case (new)
        "LegalName" | "legal_name" => "legal_name",
        "FirstName" | "first_name" => "first_name",
        "LastName" | "last_name" => "last_name",
        "DateOfBirth" | "date_of_birth" => "date_of_birth",
        "Age" | "age" => "age",
        "Nationality" | "nationality" => "nationality",
        "ResidentialAddress" | "residential_address" => "residential_address",
        "StructuredAddress" | "structured_address" => "structured_address",
        "GovernmentIdType" | "government_id_type" => "government_id_type",
        "DocumentNumber" | "document_number" => "document_number",
        "LivenessScore" | "liveness_score" => "liveness_score",
        "FaceMatchScore" | "face_match_score" => "face_match_score",
        // Legacy field names (map to closest equivalents)
        "VerifiedAddress" | "verifiedAddress" => "residential_address",
        "DocumentPhoto" | "documentPhoto" => "government_id_type",
        other => other,
    }
}

impl PolicyBuilder for WasmVerifiedPersonaPolicy {
    fn credential_type(&self) -> &'static str {
        "LastID.VerifiedPersona"
    }

    fn to_request(self) -> Result<CredentialPolicyRequest, LastIDError> {
        let callback_url = self
            .callback_url
            .ok_or_else(|| LastIDError::policy_validation("Callback URL is required"))?;

        crate::types::EndpointUrl::new(&callback_url)?;

        let mut request = CredentialPolicyRequest::with_chain("LastID.VerifiedPersona");
        request.callback_url = Some(callback_url);

        // Build constraints for verification level
        if let Some(level) = &self.verification_level {
            let level_str = match level.as_str() {
                "Basic" => "basic",
                "Standard" => "standard",
                "Enhanced" => "enhanced",
                other => other,
            };
            let mut constraints_map = std::collections::HashMap::new();
            constraints_map.insert(
                "verificationLevel".to_string(),
                serde_json::json!(level_str),
            );
            request.constraints = Some(constraints_map);
        }

        // Set required fields (no optional flag in OpenID4VP)
        if !self.fields.is_empty() {
            request.required_fields = Some(
                self.fields
                    .iter()
                    .map(|f| map_verified_persona_field(f).to_string())
                    .collect(),
            );
        }

        // Set optional fields (optional: true in OpenID4VP)
        if !self.optional_fields.is_empty() {
            request.optional_fields = Some(
                self.optional_fields
                    .iter()
                    .map(|f| map_verified_persona_field(f).to_string())
                    .collect(),
            );
        }

        Ok(request)
    }
}
