//! Age proof credential policy builder for WASM.

use wasm_bindgen::prelude::*;

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

/// Age proof credential policy builder for WASM.
///
/// # JavaScript Usage
///
/// ```javascript
/// // Verify user is at least 21
/// const policy = new AgeProofPolicy()
///     .atLeast(21)
///     .withCallback("https://yourapp.com/callback");
///
/// // Verify user is between 18 and 65
/// const policy2 = new AgeProofPolicy()
///     .between(18, 65)
///     .withCallback("https://yourapp.com/callback");
/// ```
#[wasm_bindgen(js_name = AgeProofPolicy)]
pub struct WasmAgeProofPolicy {
    callback_url: Option<String>,
    comparison: Option<String>,
    age: Option<u8>,
    max_age: Option<u8>,
}

#[wasm_bindgen(js_class = AgeProofPolicy)]
impl WasmAgeProofPolicy {
    #[wasm_bindgen(constructor)]
    pub fn new() -> Self {
        Self {
            callback_url: None,
            comparison: None,
            age: None,
            max_age: None,
        }
    }

    /// Require user to be at least the specified age (>=).
    #[wasm_bindgen(js_name = atLeast)]
    pub fn at_least(mut self, age: u8) -> Self {
        self.comparison = Some("atLeast".to_string());
        self.age = Some(age);
        self
    }

    /// Require user to be over the specified age (>).
    #[wasm_bindgen]
    pub fn over(mut self, age: u8) -> Self {
        self.comparison = Some("over".to_string());
        self.age = Some(age);
        self
    }

    /// Require user to be under the specified age (<).
    #[wasm_bindgen]
    pub fn under(mut self, age: u8) -> Self {
        self.comparison = Some("under".to_string());
        self.age = Some(age);
        self
    }

    /// Require user to be at most the specified age (<=).
    #[wasm_bindgen(js_name = atMost)]
    pub fn at_most(mut self, age: u8) -> Self {
        self.comparison = Some("atMost".to_string());
        self.age = Some(age);
        self
    }

    /// Require user to be exactly the specified age.
    #[wasm_bindgen]
    pub fn exactly(mut self, age: u8) -> Self {
        self.comparison = Some("exactly".to_string());
        self.age = Some(age);
        self
    }

    /// Require user to be within the specified age range (inclusive).
    #[wasm_bindgen]
    pub fn between(mut self, min_age: u8, max_age: u8) -> Self {
        self.comparison = Some("between".to_string());
        self.age = Some(min_age);
        self.max_age = Some(max_age);
        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.AgeProof".to_string()
    }

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

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

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

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

        if self.comparison.is_none() || self.age.is_none() {
            return Err(LastIDError::policy_validation(
                "Age proof policy requires an age constraint (use atLeast(), over(), etc.)",
            ));
        }

        if self.comparison.as_deref() == Some("between") {
            match (self.age, self.max_age) {
                (Some(min), Some(max)) if min >= max => {
                    return Err(LastIDError::policy_validation(
                        "Age range minimum must be less than maximum",
                    ));
                }
                (_, None) => {
                    return Err(LastIDError::policy_validation(
                        "Age range requires both minimum and maximum age",
                    ));
                }
                _ => {}
            }
        }

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

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

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

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

        let constraints = Some(constraints_map);

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