//! Client trait hierarchy for extension sources and distributions
//!
//! Defines three-tier trait system:
//! - `ExtensionClient`: Base trait for all backends
//! - `SourceClient`: Git-based repository sources (Gitea, Forgejo, GitHub)
//! - `DistributionClient`: OCI-compatible registries (Harbor, Docker Hub, etc.)

use async_trait::async_trait;
use bytes::Bytes;

use crate::error::Result;
use crate::models::{Extension, ExtensionType, ExtensionVersion};

/// Backend type identifier
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum BackendType {
    Gitea,
    Forgejo,
    GitHub,
    Oci,
}

/// Base trait for all extension clients
///
/// Common operations that all backends must support, regardless of whether
/// they're source (Git) or distribution (OCI) backends.
#[async_trait]
pub trait ExtensionClient: Send + Sync {
    /// Get unique backend identifier for this instance
    fn backend_id(&self) -> &str;

    /// Get backend type
    fn backend_type(&self) -> BackendType;

    /// List all extensions available from this backend
    async fn list_extensions(
        &self,
        extension_type: Option<ExtensionType>,
    ) -> Result<Vec<Extension>>;

    /// Get specific extension metadata
    async fn get_extension(&self, extension_type: ExtensionType, name: &str) -> Result<Extension>;

    /// List all versions for an extension
    async fn list_versions(
        &self,
        extension_type: ExtensionType,
        name: &str,
    ) -> Result<Vec<ExtensionVersion>>;

    /// Download extension artifact
    async fn download_extension(
        &self,
        extension_type: ExtensionType,
        name: &str,
        version: &str,
    ) -> Result<Bytes>;

    /// Check backend health
    async fn health_check(&self) -> Result<()>;
}

/// Source client trait for Git-based backends
///
/// Extends `ExtensionClient` with operations specific to Git repository sources
/// like Gitea, Forgejo, and GitHub. These backends treat extensions as releases
/// in repositories.
#[async_trait]
pub trait SourceClient: ExtensionClient {
    /// Get repository URL for an extension
    async fn get_repository_url(&self, extension_type: ExtensionType, name: &str)
        -> Result<String>;

    /// List all releases for a repository
    ///
    /// Returns release info including tags, assets, and creation times
    async fn list_releases(&self, repo_name: &str) -> Result<Vec<ReleaseInfo>>;

    /// Get release notes/description for a specific version
    async fn get_release_notes(&self, repo_name: &str, version: &str) -> Result<String>;
}

/// Distribution client trait for OCI registries
///
/// Extends `ExtensionClient` with operations specific to OCI registries
/// like Docker Hub, Harbor, Quay, and ghcr.io. These backends treat extensions
/// as container images or artifacts with OCI manifests.
#[async_trait]
pub trait DistributionClient: ExtensionClient {
    /// Get manifest for an artifact
    async fn get_manifest(&self, repo_name: &str, tag: &str) -> Result<ManifestInfo>;

    /// List all catalogs/repositories in the registry
    async fn list_catalog(&self) -> Result<Vec<String>>;

    /// Get digest for an artifact
    async fn get_digest(&self, repo_name: &str, tag: &str) -> Result<String>;

    /// Verify artifact integrity
    async fn verify_artifact(&self, repo_name: &str, tag: &str, digest: &str) -> Result<bool>;
}

/// Release information from source backends
#[derive(Debug, Clone)]
pub struct ReleaseInfo {
    pub tag: String,
    pub created_at: chrono::DateTime<chrono::Utc>,
    pub published_at: Option<chrono::DateTime<chrono::Utc>>,
    pub assets: Vec<ReleaseAsset>,
    pub description: String,
}

/// Asset attached to a release
#[derive(Debug, Clone)]
pub struct ReleaseAsset {
    pub name: String,
    pub download_url: String,
    pub size: Option<u64>,
}

/// OCI manifest information
#[derive(Debug, Clone)]
pub struct ManifestInfo {
    pub config_digest: String,
    pub layers: Vec<LayerInfo>,
    pub total_size: u64,
}

/// OCI layer information
#[derive(Debug, Clone)]
pub struct LayerInfo {
    pub digest: String,
    pub size: u64,
    pub media_type: String,
}
