//! Verified email credential policy builder.

use std::collections::HashMap;
use std::marker::PhantomData;

use super::{Complete, CredentialConstraint, Incomplete, PolicyBuilder};
use crate::error::LastIDError;
use crate::types::{CredentialPolicyRequest, EndpointUrl};

/// Policy builder for verified email credentials.
///
/// # Type-State Pattern
///
/// - `VerifiedEmailPolicy<Incomplete>` - Initial state
/// - `VerifiedEmailPolicy<Complete>` - Ready to build
///
/// # Example
///
/// ```rust,no_run
/// use lastid_sdk::{PolicyBuilder, VerifiedEmailPolicy};
///
/// let policy = VerifiedEmailPolicy::new()
///     .with_domain_constraint("example.com")
///     .with_callback("https://example.com/callback");
///
/// let request = policy.to_request().unwrap();
/// ```
#[cfg(feature = "verified-email-policy")]
pub struct VerifiedEmailPolicy<State = Incomplete> {
    callback_url: Option<String>,
    domain_constraint: Option<String>,
    _marker: PhantomData<State>,
}

#[cfg(feature = "verified-email-policy")]
impl Default for VerifiedEmailPolicy<Incomplete> {
    fn default() -> Self {
        Self::new()
    }
}

#[cfg(feature = "verified-email-policy")]
impl VerifiedEmailPolicy<Incomplete> {
    /// Create a new verified email credential policy builder.
    #[must_use]
    pub const fn new() -> Self {
        Self {
            callback_url: None,
            domain_constraint: None,
            _marker: PhantomData,
        }
    }

    /// Add a domain constraint (e.g., "example.com" requires @example.com
    /// emails).
    #[must_use]
    pub fn with_domain_constraint(mut self, domain: impl Into<String>) -> Self {
        self.domain_constraint = Some(domain.into());
        self
    }

    /// Set the callback URL and transition to Complete state.
    #[must_use]
    pub fn with_callback(self, url: impl Into<String>) -> VerifiedEmailPolicy<Complete> {
        VerifiedEmailPolicy {
            callback_url: Some(url.into()),
            domain_constraint: self.domain_constraint,
            _marker: PhantomData,
        }
    }
}

#[cfg(feature = "verified-email-policy")]
impl PolicyBuilder for VerifiedEmailPolicy<Complete> {
    fn credential_type(&self) -> &'static str {
        "LastID.VerifiedEmail"
    }

    fn to_request(self) -> Result<CredentialPolicyRequest, LastIDError> {
        // SEC-001: Validate callback URL with HTTPS enforcement
        // HTTP is only allowed for localhost (development)
        if let Some(ref url) = self.callback_url {
            EndpointUrl::new(url)?;
        }

        // Validate domain constraint if present
        if let Some(ref domain) = self.domain_constraint {
            CredentialConstraint::validate_domain(domain)?;
        }

        // Build constraints
        let constraints = self.domain_constraint.as_ref().map(|domain| {
            let mut map = HashMap::new();
            map.insert("domainConstraint".to_string(), serde_json::json!(domain));
            map
        });

        // VerifiedEmail requires Base -> VerifiedEmail chain (FR-007)
        let mut request = CredentialPolicyRequest::with_chain(self.credential_type());
        request.callback_url = self.callback_url;
        request.constraints = constraints;
        Ok(request)
    }
}

#[cfg(feature = "verified-email-policy")]
impl Clone for VerifiedEmailPolicy<Complete> {
    fn clone(&self) -> Self {
        Self {
            callback_url: self.callback_url.clone(),
            domain_constraint: self.domain_constraint.clone(),
            _marker: PhantomData,
        }
    }
}

#[cfg(all(test, feature = "verified-email-policy"))]
mod tests {
    use super::*;

    #[test]
    fn test_verified_email_policy_build() {
        let policy = VerifiedEmailPolicy::new()
            .with_domain_constraint("example.com")
            .with_callback("https://example.com");

        let request = policy.to_request().unwrap();
        assert_eq!(request.credential_type, "LastID.VerifiedEmail");
        assert!(request.constraints.is_some());
    }

    #[test]
    fn test_verified_email_no_constraint() {
        let policy = VerifiedEmailPolicy::new().with_callback("https://example.com");

        let request = policy.to_request().unwrap();
        assert!(request.constraints.is_none());
    }

    #[test]
    fn test_invalid_domain_constraint() {
        let policy = VerifiedEmailPolicy::new()
            .with_domain_constraint("user@example.com")
            .with_callback("https://example.com");

        let result = policy.to_request();
        assert!(result.is_err());
    }
}
