//! Trust credential policy builder.
//!
//! Requests trust level credentials established through the `LastID` trust
//! network.

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

use serde::{Deserialize, Serialize};

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

/// Trust levels in the `LastID` trust network.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub enum TrustLevel {
    /// No established trust
    None = 0,
    /// Basic trust (mutual connection)
    Basic = 1,
    /// Verified trust (identity verified by trusted party)
    Verified = 2,
    /// High trust (multiple verifications, long history)
    High = 3,
    /// Ultimate trust (institutional verification)
    Ultimate = 4,
}

impl TrustLevel {
    /// Get the level as a string for the API.
    #[must_use]
    pub const fn as_str(&self) -> &'static str {
        match self {
            Self::None => "none",
            Self::Basic => "basic",
            Self::Verified => "verified",
            Self::High => "high",
            Self::Ultimate => "ultimate",
        }
    }

    /// Get the numeric value of this trust level.
    #[must_use]
    pub const fn value(&self) -> u8 {
        *self as u8
    }
}

/// Policy builder for trust credentials.
///
/// # Type-State Pattern
///
/// - `TrustPolicy<Incomplete>` - Initial state
/// - `TrustPolicy<Complete>` - Ready to build
///
/// # Example
///
/// ```rust,no_run
/// use lastid_sdk::{PolicyBuilder, TrustLevel, TrustPolicy};
///
/// let policy = TrustPolicy::new()
///     .with_minimum_level(TrustLevel::Verified)
///     .with_trusted_issuer("did:lastid:trusted-org")
///     .with_callback("https://example.com/callback");
///
/// let request = policy.to_request().unwrap();
/// ```
#[cfg(feature = "trust-policy")]
pub struct TrustPolicy<State = Incomplete> {
    callback_url: Option<String>,
    minimum_level: Option<TrustLevel>,
    trusted_issuers: Vec<String>,
    require_mutual: bool,
    max_age_days: Option<u32>,
    _marker: PhantomData<State>,
}

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

#[cfg(feature = "trust-policy")]
impl TrustPolicy<Incomplete> {
    /// Create a new trust credential policy builder.
    #[must_use]
    pub const fn new() -> Self {
        Self {
            callback_url: None,
            minimum_level: None,
            trusted_issuers: Vec::new(),
            require_mutual: false,
            max_age_days: None,
            _marker: PhantomData,
        }
    }

    /// Set the minimum required trust level using a `TrustLevel` enum.
    ///
    /// # Example
    ///
    /// ```rust,no_run
    /// use lastid_sdk::{TrustLevel, TrustPolicy};
    ///
    /// let policy = TrustPolicy::new()
    ///     .with_minimum_level(TrustLevel::Verified)
    ///     .with_callback("https://example.com/callback");
    /// ```
    #[must_use]
    pub const fn with_minimum_level(mut self, level: TrustLevel) -> Self {
        self.minimum_level = Some(level);
        self
    }

    /// Set the minimum required trust level using a numeric value.
    ///
    /// This is a convenience method for setting the trust level numerically:
    /// - 0 = None
    /// - 1 = Basic
    /// - 2 = Verified
    /// - 3 = High
    /// - 4 = Ultimate
    ///
    /// # Example
    ///
    /// ```rust,no_run
    /// use lastid_sdk::TrustPolicy;
    ///
    /// // Require at least trust level 3 (High)
    /// let policy = TrustPolicy::new()
    ///     .with_minimum_level_value(3)
    ///     .with_callback("https://example.com/callback");
    /// ```
    ///
    /// # Errors
    ///
    /// Returns `PolicyValidation` error at build time if the level is negative.
    #[must_use]
    pub const fn with_minimum_level_value(mut self, level: i64) -> Self {
        // Map numeric value to TrustLevel enum
        self.minimum_level = Some(match level {
            0 => TrustLevel::None,
            1 => TrustLevel::Basic,
            2 => TrustLevel::Verified,
            3 => TrustLevel::High,
            _ => TrustLevel::Ultimate, // 4+ maps to Ultimate
        });
        self
    }

    /// Add a trusted issuer DID that must have established trust.
    #[must_use]
    pub fn with_trusted_issuer(mut self, issuer_did: impl Into<String>) -> Self {
        self.trusted_issuers.push(issuer_did.into());
        self
    }

    /// Add multiple trusted issuer DIDs.
    #[must_use]
    pub fn with_trusted_issuers(mut self, issuer_dids: &[&str]) -> Self {
        self.trusted_issuers
            .extend(issuer_dids.iter().map(|s| (*s).to_string()));
        self
    }

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

    /// Set maximum age of the trust credential in days.
    #[must_use]
    pub const fn with_max_age_days(mut self, days: u32) -> Self {
        self.max_age_days = Some(days);
        self
    }

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

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

    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 minimum level if set
        if let Some(level) = self.minimum_level {
            CredentialConstraint::validate_minimum_level(i64::from(level.value()))?;
        }

        // Validate trusted issuers
        for issuer in &self.trusted_issuers {
            CredentialConstraint::validate_did(issuer)?;
        }

        // Build constraints
        let mut constraints_map = HashMap::new();

        if let Some(level) = self.minimum_level {
            constraints_map.insert(
                "minimumTrustLevel".to_string(),
                serde_json::json!(level.as_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)
        };

        // Trust requires Base -> Trust 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 = "trust-policy")]
impl Clone for TrustPolicy<Complete> {
    fn clone(&self) -> Self {
        Self {
            callback_url: self.callback_url.clone(),
            minimum_level: self.minimum_level,
            trusted_issuers: self.trusted_issuers.clone(),
            require_mutual: self.require_mutual,
            max_age_days: self.max_age_days,
            _marker: PhantomData,
        }
    }
}

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

    #[test]
    fn test_trust_policy_build() {
        let policy = TrustPolicy::new()
            .with_minimum_level(TrustLevel::Verified)
            .with_trusted_issuer("did:lastid:trusted-org")
            .require_mutual_trust()
            .with_max_age_days(30)
            .with_callback("https://example.com");

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

        let constraints = request.constraints.unwrap();
        assert!(constraints.get("minimumTrustLevel").is_some());
        assert!(constraints.get("trustedIssuers").is_some());
        assert!(constraints.get("requireMutual").is_some());
        assert!(constraints.get("maxAgeDays").is_some());
    }

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

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

    #[test]
    fn test_invalid_issuer_did() {
        let policy = TrustPolicy::new()
            .with_trusted_issuer("not-a-did") // Invalid
            .with_callback("https://example.com");

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

    #[test]
    fn test_trust_level_ordering() {
        assert!(TrustLevel::None < TrustLevel::Basic);
        assert!(TrustLevel::Basic < TrustLevel::Verified);
        assert!(TrustLevel::Verified < TrustLevel::High);
        assert!(TrustLevel::High < TrustLevel::Ultimate);
    }

    #[test]
    fn test_trust_level_as_str() {
        assert_eq!(TrustLevel::None.as_str(), "none");
        assert_eq!(TrustLevel::Basic.as_str(), "basic");
        assert_eq!(TrustLevel::Verified.as_str(), "verified");
        assert_eq!(TrustLevel::High.as_str(), "high");
        assert_eq!(TrustLevel::Ultimate.as_str(), "ultimate");
    }

    #[test]
    fn test_trust_level_value() {
        assert_eq!(TrustLevel::None.value(), 0);
        assert_eq!(TrustLevel::Basic.value(), 1);
        assert_eq!(TrustLevel::Ultimate.value(), 4);
    }

    #[test]
    fn test_with_minimum_level_value() {
        let policy = TrustPolicy::new()
            .with_minimum_level_value(3)
            .with_callback("https://example.com");

        let request = policy.to_request().unwrap();
        let constraints = request.constraints.unwrap();
        assert_eq!(constraints.get("minimumTrustLevel").unwrap(), "high");
    }

    #[test]
    fn test_with_minimum_level_value_mapping() {
        // Test all value mappings
        for (value, expected) in [
            (0, "none"),
            (1, "basic"),
            (2, "verified"),
            (3, "high"),
            (4, "ultimate"),
            (5, "ultimate"),
        ] {
            let policy = TrustPolicy::new()
                .with_minimum_level_value(value)
                .with_callback("https://example.com");

            let request = policy.to_request().unwrap();
            let constraints = request.constraints.unwrap();
            assert_eq!(
                constraints.get("minimumTrustLevel").unwrap(),
                expected,
                "Failed for value {}",
                value
            );
        }
    }
}
