//! Verified phone credential policy builder.

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

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

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

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

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

    /// Add a country code constraint (e.g., "+1" for US numbers).
    #[must_use]
    pub fn with_country_code(mut self, code: impl Into<String>) -> Self {
        self.country_code = Some(code.into());
        self
    }

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

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

    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 country code if present
        if let Some(ref code) = self.country_code
            && (!code.starts_with('+') || code.len() < 2)
        {
            return Err(LastIDError::policy_validation(
                "Country code must start with '+' and have at least one digit (e.g., '+1')",
            ));
        }

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

        // VerifiedPhone requires Base -> VerifiedPhone 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-phone-policy")]
impl Clone for VerifiedPhonePolicy<Complete> {
    fn clone(&self) -> Self {
        Self {
            callback_url: self.callback_url.clone(),
            country_code: self.country_code.clone(),
            _marker: PhantomData,
        }
    }
}

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

    #[test]
    fn test_verified_phone_policy_build() {
        let policy = VerifiedPhonePolicy::new()
            .with_country_code("+1")
            .with_callback("https://example.com");

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

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

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

    #[test]
    fn test_invalid_country_code() {
        let policy = VerifiedPhonePolicy::new()
            .with_country_code("1") // Missing +
            .with_callback("https://example.com");

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