//! Persona credential policy builder for WASM.

use wasm_bindgen::prelude::*;

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

/// Persona credential policy builder for WASM.
///
/// # JavaScript Usage
///
/// ```javascript
/// const policy = new PersonaPolicy()
///     .withFields(["display_name", "email"])
///     .withOptionalFields(["phone", "company"])
///     .withCallback("https://yourapp.com/callback");
/// ```
#[wasm_bindgen(js_name = PersonaPolicy)]
pub struct WasmPersonaPolicy {
    callback_url: Option<String>,
    fields: Vec<String>,
    optional_fields: Vec<String>,
}

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

    /// Add required fields that must be disclosed.
    /// Valid fields: display_name, email, phone, company, job_title,
    /// avatar_url, website, location, visibility
    #[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: display_name, email, phone, company, job_title,
    /// avatar_url, website, location, visibility
    #[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.Persona".to_string()
    }

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

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

/// Map field name from JS input to snake_case for the API.
fn map_persona_field(field: &str) -> &str {
    match field {
        // Support both PascalCase (legacy) and snake_case (new)
        "DisplayName" | "display_name" => "display_name",
        "Email" | "email" => "email",
        "Phone" | "phone" => "phone",
        "Company" | "company" => "company",
        "JobTitle" | "job_title" => "job_title",
        "AvatarUrl" | "avatar_url" => "avatar_url",
        "Website" | "website" => "website",
        "Location" | "location" => "location",
        "Visibility" | "visibility" => "visibility",
        other => other,
    }
}

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

    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.Persona");
        request.callback_url = Some(callback_url);

        // Set required fields (no optional flag in OpenID4VP)
        if !self.fields.is_empty() {
            request.required_fields = Some(
                self.fields
                    .iter()
                    .map(|f| map_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_persona_field(f).to_string())
                    .collect(),
            );
        }

        Ok(request)
    }
}
