use crate::domain::error::{DomainError, Result};
use crate::domain::resource::{Resource, ResourceProvider, ResourceState};
use async_trait::async_trait;
use std::sync::Arc;

pub struct ProviderRegistry {
    providers: Vec<Arc<dyn ResourceProvider>>,
}

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

impl ProviderRegistry {
    pub fn new() -> Self {
        Self {
            providers: Vec::new(),
        }
    }

    pub fn register(&mut self, provider: Arc<dyn ResourceProvider>) {
        self.providers.push(provider);
    }

    fn find_provider(&self, resource: &Resource) -> Result<&Arc<dyn ResourceProvider>> {
        self.providers
            .iter()
            .find(|p| p.can_handle(resource))
            .ok_or_else(|| {
                DomainError::Internal(format!(
                    "No provider found for resource: {}",
                    resource.id()
                ))
            })
    }
}

#[async_trait]
impl ResourceProvider for ProviderRegistry {
    fn can_handle(&self, resource: &Resource) -> bool {
        self.providers.iter().any(|p| p.can_handle(resource))
    }

    async fn get_state(&self, resource: &Resource, full: bool) -> Result<ResourceState> {
        let provider = self.find_provider(resource)?;
        provider.get_state(resource, full).await
    }

    async fn apply(&self, resource: &Resource) -> Result<()> {
        let provider = self.find_provider(resource)?;
        provider.apply(resource).await
    }
}
