//! Mock trust registry implementation for testing.
//!
//! Provides an in-memory trust registry that can be configured
//! with specific issuers and behaviors for testing.

use std::collections::HashMap;
use std::sync::Arc;

use async_trait::async_trait;
use tokio::sync::RwLock;

use crate::error::{HttpError, TrustRegistryError};
use crate::trust_registry::{IssuerInfo, IssuerStatus, TrustRegistry};

/// A mock trust registry for testing.
///
/// # Example
///
/// ```rust,no_run
/// use lastid_sdk::test_utils::MockTrustRegistry;
/// use lastid_sdk::test_utils::fixtures::IssuerInfoBuilder;
/// use lastid_sdk::trust_registry::TrustRegistry;
///
/// # async fn example() -> Result<(), lastid_sdk::LastIDError> {
/// let registry = MockTrustRegistry::new();
///
/// registry
///     .add_issuer(
///         IssuerInfoBuilder::new("did:lastid:test-issuer")
///             .with_name("Test Issuer")
///             .authorized()
///             .with_credential_type("LastID.Base")
///             .build(),
///     )
///     .await;
///
/// // Use the mock in tests
/// let issuer = registry.get_issuer("did:lastid:test-issuer").await?;
/// assert_eq!(issuer.organization_name, "Test Issuer");
/// # Ok(())
/// # }
/// ```
#[derive(Debug, Clone)]
pub struct MockTrustRegistry {
    issuers: Arc<RwLock<HashMap<String, IssuerInfo>>>,
    simulate_network_error: Arc<RwLock<bool>>,
}

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

impl MockTrustRegistry {
    /// Create a new empty mock trust registry.
    #[must_use]
    pub fn new() -> Self {
        Self {
            issuers: Arc::new(RwLock::new(HashMap::new())),
            simulate_network_error: Arc::new(RwLock::new(false)),
        }
    }

    /// Add an issuer asynchronously.
    pub async fn add_issuer(&self, issuer: IssuerInfo) {
        let mut map = self.issuers.write().await;
        map.insert(issuer.enterprise_did.clone(), issuer);
    }

    /// Remove an issuer from the registry.
    pub async fn remove_issuer(&self, did: &str) {
        let mut map = self.issuers.write().await;
        map.remove(did);
    }

    /// Update an issuer's status.
    pub async fn set_issuer_status(&self, did: &str, status: IssuerStatus) {
        let mut map = self.issuers.write().await;
        if let Some(issuer) = map.get_mut(did) {
            issuer.status = status;
        }
    }

    /// Enable or disable simulated network errors.
    pub async fn set_simulate_error(&self, simulate: bool) {
        let mut flag = self.simulate_network_error.write().await;
        *flag = simulate;
    }

    /// Check if an issuer exists in the registry.
    pub async fn has_issuer(&self, did: &str) -> bool {
        let map = self.issuers.read().await;
        map.contains_key(did)
    }

    /// Get the number of registered issuers.
    pub async fn issuer_count(&self) -> usize {
        let map = self.issuers.read().await;
        map.len()
    }

    /// Clear all issuers from the registry.
    pub async fn clear(&self) {
        let mut map = self.issuers.write().await;
        map.clear();
    }

    /// Check if the registry is empty.
    pub async fn is_empty(&self) -> bool {
        let map = self.issuers.read().await;
        map.is_empty()
    }
}

#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
impl TrustRegistry for MockTrustRegistry {
    async fn get_issuer(&self, did: &str) -> Result<IssuerInfo, TrustRegistryError> {
        // Check for simulated error
        if *self.simulate_network_error.read().await {
            return Err(TrustRegistryError::Http(HttpError::Status {
                status: 500,
                message: "Simulated network error".to_string(),
            }));
        }

        let map = self.issuers.read().await;
        map.get(did)
            .cloned()
            .ok_or_else(|| TrustRegistryError::IssuerNotFound(did.to_string()))
    }

    async fn invalidate_cache(&self, _did: &str) {
        // No-op for mock - no caching
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::test_utils::fixtures::{BASE_IDP_DID_DEV, BASE_IDP_DID_PROD, IssuerInfoBuilder};

    #[tokio::test]
    async fn test_mock_registry_basic() {
        let registry = MockTrustRegistry::new();

        registry
            .add_issuer(
                IssuerInfoBuilder::new("did:lastid:test")
                    .with_name("Test Org")
                    .authorized()
                    .with_credential_type("LastID.Base")
                    .build(),
            )
            .await;

        let issuer = registry.get_issuer("did:lastid:test").await.unwrap();
        assert_eq!(issuer.organization_name, "Test Org");
        assert!(issuer.can_issue("LastID.Base"));
    }

    #[tokio::test]
    async fn test_mock_registry_base_idp() {
        let registry = MockTrustRegistry::new();

        // Add base IDP using the convenience builder
        registry
            .add_issuer(IssuerInfoBuilder::base_idp().build())
            .await;

        let issuer = registry.get_issuer(BASE_IDP_DID_PROD).await.unwrap();
        assert_eq!(issuer.organization_name, "LastID");
        assert_eq!(issuer.endpoint_url, "https://human.lastid.co");
        assert!(matches!(issuer.status, IssuerStatus::Authorized));
        assert!(issuer.can_issue("LastID.Base"));
        assert!(issuer.can_issue("LastID.Persona"));
        assert!(issuer.can_issue("LastID.VerifiedEmail"));
    }

    #[tokio::test]
    async fn test_mock_registry_base_idp_dev() {
        let registry = MockTrustRegistry::new();

        registry
            .add_issuer(IssuerInfoBuilder::base_idp_dev().build())
            .await;

        let issuer = registry.get_issuer(BASE_IDP_DID_DEV).await.unwrap();
        assert_eq!(issuer.enterprise_did, "did:lastid:human.dev.lastid.co");
        assert_eq!(issuer.endpoint_url, "https://human.dev.lastid.co");
    }

    #[tokio::test]
    async fn test_validate_base_idp() {
        let registry = MockTrustRegistry::new();
        registry
            .add_issuer(IssuerInfoBuilder::base_idp().build())
            .await;

        // Base IDP should always validate (always authorized)
        let result = registry.validate_issuer(BASE_IDP_DID_PROD).await;
        assert!(result.is_ok());
        assert_eq!(result.unwrap().organization_name, "LastID");
    }

    #[tokio::test]
    async fn test_mock_registry_not_found() {
        let registry = MockTrustRegistry::new();

        let result = registry.get_issuer("did:lastid:unknown").await;
        assert!(matches!(result, Err(TrustRegistryError::IssuerNotFound(_))));
    }

    #[tokio::test]
    async fn test_mock_registry_simulated_error() {
        let registry = MockTrustRegistry::new();

        registry
            .add_issuer(IssuerInfoBuilder::new("did:lastid:test").build())
            .await;

        registry.set_simulate_error(true).await;

        let result = registry.get_issuer("did:lastid:test").await;
        assert!(matches!(result, Err(TrustRegistryError::Http(_))));
    }

    #[tokio::test]
    async fn test_validate_issuer_suspended() {
        let registry = MockTrustRegistry::new();

        registry
            .add_issuer(
                IssuerInfoBuilder::new("did:lastid:suspended")
                    .suspended()
                    .build(),
            )
            .await;

        let result = registry.validate_issuer("did:lastid:suspended").await;
        assert!(matches!(
            result,
            Err(TrustRegistryError::InvalidIssuerStatus {
                status: IssuerStatus::Suspended
            })
        ));
    }

    #[tokio::test]
    async fn test_set_issuer_status() {
        let registry = MockTrustRegistry::new();

        registry
            .add_issuer(
                IssuerInfoBuilder::new("did:lastid:test")
                    .authorized()
                    .build(),
            )
            .await;

        registry
            .set_issuer_status("did:lastid:test", IssuerStatus::Suspended)
            .await;

        let issuer = registry.get_issuer("did:lastid:test").await.unwrap();
        assert!(matches!(issuer.status, IssuerStatus::Suspended));
    }

    #[tokio::test]
    async fn test_empty_registry() {
        let registry = MockTrustRegistry::new();
        assert!(registry.is_empty().await);

        let result = registry.get_issuer("did:lastid:unknown").await;
        assert!(result.is_err());
    }
}
