//! OCI Integration Module
//!
//! Provides OCI registry integration for pulling KCL packages and extension
//! artifacts.

pub mod client;

use std::num::NonZeroUsize;
use std::path::PathBuf;
use std::sync::Arc;

use anyhow::Result;
pub use client::{OciArtifact, OciClient, OciManifest};
use lru::LruCache;
use tokio::sync::Mutex;
use tracing::{debug, info};

/// OCI Manager for registry operations
pub struct OciManager {
    client: OciClient,
    manifest_cache: Arc<Mutex<LruCache<String, OciManifest>>>,
    cache_dir: PathBuf,
}

impl OciManager {
    /// Create new OCI manager
    pub fn new(registry_url: String, namespace: String, cache_dir: PathBuf) -> Self {
        let cache_size = NonZeroUsize::new(100).unwrap();

        Self {
            client: OciClient::new(registry_url, namespace),
            manifest_cache: Arc::new(Mutex::new(LruCache::new(cache_size))),
            cache_dir,
        }
    }

    /// Pull KCL package from OCI registry
    pub async fn pull_kcl_package(&self, name: &str, version: &str) -> Result<PathBuf> {
        info!("Pulling KCL package: {}:{}", name, version);

        let artifact = self
            .client
            .pull_artifact(&format!("kcl/{}", name), version)
            .await?;

        // Save to cache directory
        let package_dir = self.cache_dir.join("kcl").join(name).join(version);
        tokio::fs::create_dir_all(&package_dir).await?;

        let package_path = package_dir.join("package.tar.gz");
        tokio::fs::write(&package_path, &artifact.content).await?;

        info!(
            "Successfully pulled KCL package to {}",
            package_path.display()
        );

        Ok(package_path)
    }

    /// Pull extension artifact from OCI registry
    pub async fn pull_extension_artifact(
        &self,
        extension_type: &str,
        name: &str,
        version: &str,
    ) -> Result<PathBuf> {
        info!(
            "Pulling extension artifact: {} ({}):{}",
            name, extension_type, version
        );

        let artifact = self
            .client
            .pull_artifact(&format!("extensions/{}/{}", extension_type, name), version)
            .await?;

        // Save to cache directory
        let artifact_dir = self
            .cache_dir
            .join("extensions")
            .join(extension_type)
            .join(name)
            .join(version);
        tokio::fs::create_dir_all(&artifact_dir).await?;

        let artifact_path = artifact_dir.join("artifact.tar.gz");
        tokio::fs::write(&artifact_path, &artifact.content).await?;

        info!(
            "Successfully pulled extension artifact to {}",
            artifact_path.display()
        );

        Ok(artifact_path)
    }

    /// List OCI artifacts in namespace
    pub async fn list_oci_artifacts(&self, namespace: &str) -> Result<Vec<OciArtifact>> {
        debug!("Listing OCI artifacts in namespace: {}", namespace);

        let artifacts = self.client.list_artifacts(namespace).await?;

        info!("Found {} artifacts in {}", artifacts.len(), namespace);

        Ok(artifacts)
    }

    /// Get manifest (with caching)
    pub async fn get_manifest(&self, name: &str, version: &str) -> Result<OciManifest> {
        let cache_key = format!("{}:{}", name, version);

        // Check cache first
        {
            let mut cache = self.manifest_cache.lock().await;
            if let Some(manifest) = cache.get(&cache_key) {
                debug!("Manifest found in cache: {}", cache_key);
                return Ok(manifest.clone());
            }
        }

        // Fetch from registry
        let manifest = self.client.get_manifest(name, version).await?;

        // Cache manifest
        {
            let mut cache = self.manifest_cache.lock().await;
            cache.put(cache_key.clone(), manifest.clone());
        }

        debug!("Cached manifest: {}", cache_key);

        Ok(manifest)
    }

    /// Clear manifest cache
    pub async fn clear_cache(&self) {
        let mut cache = self.manifest_cache.lock().await;
        cache.clear();
        info!("Cleared OCI manifest cache");
    }

    /// Check if artifact exists
    pub async fn artifact_exists(&self, name: &str, version: &str) -> Result<bool> {
        self.client.artifact_exists(name, version).await
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_oci_manager_creation() {
        let manager = OciManager::new(
            "http://localhost:5000".to_string(),
            "provisioning-extensions".to_string(),
            PathBuf::from("/tmp/oci-cache"),
        );

        assert_eq!(manager.cache_dir, PathBuf::from("/tmp/oci-cache"));
    }

    #[tokio::test]
    async fn test_cache_operations() {
        let manager = OciManager::new(
            "http://localhost:5000".to_string(),
            "provisioning-extensions".to_string(),
            PathBuf::from("/tmp/oci-cache"),
        );

        // Clear empty cache
        manager.clear_cache().await;
    }
}
