pub mod docker_hub_fetcher;
pub mod docker_registry_v2_fetcher;
use std::error::Error;

use crate::{
    docker_config::DockerPassword,
    fetcher::{
        docker_hub_fetcher::DockerHubTagsFetcher, docker_registry_v2_fetcher::RegistryTagsFetcher,
    },
};

/// fetcher trait
trait DockerTagsFetcher {
    /// query tags
    async fn get_tags(&self, args: &QueryArgs)
        -> Result<Vec<String>, Box<dyn Error + Send + Sync>>;
}

/// query args
pub(crate) struct QueryArgs {
    pub namespace: String,
    pub repository: String,
    pub name: Option<String>,
    pub arch: Option<String>,
}

impl QueryArgs {
    pub fn new(repository: &str, name: Option<String>, arch: Option<String>) -> Self {
        let (namespace, repository) = repository
            .split_once('/')
            .unwrap_or(("library", repository));
        Self {
            namespace: namespace.to_string(),
            repository: repository.to_string(),
            name,
            arch,
        }
    }
}

pub(crate) enum FetcherType {
    DockerHub,
    Registry(DockerPassword),
}

/// supported fetcher enum
pub(crate) enum Fetcher {
    DockerHub(DockerHubTagsFetcher),
    Registry(RegistryTagsFetcher),
}

/// fetcher dispatcher
impl Fetcher {
    pub(crate) async fn fetch(
        &self,
        args: &QueryArgs,
    ) -> Result<Vec<String>, Box<dyn Error + Send + Sync>> {
        match self {
            Fetcher::DockerHub(f) => f.get_tags(args).await,
            Fetcher::Registry(f) => f.get_tags(args).await,
        }
    }

    /// fetcher factory
    pub(crate) fn create_fetcher(fetcher_type: FetcherType) -> Fetcher {
        match fetcher_type {
            FetcherType::DockerHub => Fetcher::DockerHub(DockerHubTagsFetcher::new()),
            FetcherType::Registry(password) => {
                Fetcher::Registry(RegistryTagsFetcher::new(&password))
            }
        }
    }
}
