//! Trust credential policy builder for WASM.

use wasm_bindgen::prelude::*;

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

/// Trust credential policy builder for WASM.
///
/// # JavaScript Usage
///
/// ```javascript
/// const policy = new TrustPolicy()
///     .withMinimumLevel("Verified")
///     .withTrustedIssuer("did:lastid:trusted-org")
///     .requireMutualTrust()
///     .withMaxAgeDays(30)
///     .withCallback("https://yourapp.com/callback");
/// ```
#[wasm_bindgen(js_name = TrustPolicy)]
pub struct WasmTrustPolicy {
    callback_url: Option<String>,
    minimum_level: Option<String>,
    trusted_issuers: Vec<String>,
    require_mutual: bool,
    max_age_days: Option<u32>,
}

#[wasm_bindgen(js_class = TrustPolicy)]
impl WasmTrustPolicy {
    #[wasm_bindgen(constructor)]
    pub fn new() -> Self {
        Self {
            callback_url: None,
            minimum_level: None,
            trusted_issuers: Vec::new(),
            require_mutual: false,
            max_age_days: None,
        }
    }

    /// Set the minimum required trust level: None, Basic, Verified, High, or
    /// Ultimate.
    #[wasm_bindgen(js_name = withMinimumLevel)]
    pub fn with_minimum_level(mut self, level: String) -> Self {
        self.minimum_level = Some(level);
        self
    }

    /// Add a trusted issuer DID that must have established trust.
    #[wasm_bindgen(js_name = withTrustedIssuer)]
    pub fn with_trusted_issuer(mut self, issuer_did: String) -> Self {
        self.trusted_issuers.push(issuer_did);
        self
    }

    /// Require mutual trust (both parties have established trust).
    #[wasm_bindgen(js_name = requireMutualTrust)]
    pub fn require_mutual_trust(mut self) -> Self {
        self.require_mutual = true;
        self
    }

    /// Set maximum age of the trust credential in days.
    #[wasm_bindgen(js_name = withMaxAgeDays)]
    pub fn with_max_age_days(mut self, days: u32) -> Self {
        self.max_age_days = Some(days);
        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.Trust".to_string()
    }

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

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

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

    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)?;

        for issuer in &self.trusted_issuers {
            if !issuer.starts_with("did:") {
                return Err(LastIDError::policy_validation(format!(
                    "Trusted issuer must be a valid DID: {issuer}"
                )));
            }
        }

        let mut constraints_map = std::collections::HashMap::new();

        if let Some(level) = &self.minimum_level {
            let level_str = match level.as_str() {
                "None" => "none",
                "Basic" => "basic",
                "Verified" => "verified",
                "High" => "high",
                "Ultimate" => "ultimate",
                other => other,
            };
            constraints_map.insert(
                "minimumTrustLevel".to_string(),
                serde_json::json!(level_str),
            );
        }

        if !self.trusted_issuers.is_empty() {
            constraints_map.insert(
                "trustedIssuers".to_string(),
                serde_json::json!(self.trusted_issuers),
            );
        }

        if self.require_mutual {
            constraints_map.insert("requireMutual".to_string(), serde_json::json!(true));
        }

        if let Some(days) = self.max_age_days {
            constraints_map.insert("maxAgeDays".to_string(), serde_json::json!(days));
        }

        let constraints = if constraints_map.is_empty() {
            None
        } else {
            Some(constraints_map)
        };

        let mut request = CredentialPolicyRequest::with_chain("LastID.Trust");
        request.callback_url = Some(callback_url);
        request.constraints = constraints;
        Ok(request)
    }
}
