mod docker_auth;

use std::error::Error;

use serde::{Deserialize, Serialize};

use crate::{
    docker_config::DockerPassword,
    fetcher::{DockerTagsFetcher, QueryArgs},
};

/// docker registry api response struct
#[derive(Serialize, Deserialize)]
struct RegistryTagsResponse {
    name: Option<String>,
    tags: Option<Vec<String>>,
}

pub(crate) struct RegistryTagsFetcher {
    username: String,
    password: String,
}

impl RegistryTagsFetcher {
    pub fn new(docker_password: &DockerPassword) -> RegistryTagsFetcher {
        RegistryTagsFetcher {
            username: docker_password.username.to_string(),
            password: docker_password.password.to_string(),
        }
    }
}

impl DockerTagsFetcher for RegistryTagsFetcher {
    async fn get_tags(
        &self,
        args: &QueryArgs,
    ) -> Result<Vec<String>, Box<dyn Error + Send + Sync>> {
        let namespace = args.namespace.as_str();
        let repository = args.repository.as_str();
        let token = docker_auth::get_token(
            namespace,
            repository,
            self.username.as_str(),
            self.password.as_str(),
        )
        .await?;

        let url = format!("https://registry-1.docker.io/v2/{namespace}/{repository}/tags/list");
        let client = reqwest::Client::new();
        let response = client
            .get(url)
            .header("Authorization", format!("Bearer {token}"))
            .send()
            .await?;
        let mut results = Vec::<String>::new();

        let json: RegistryTagsResponse = response.json().await?;
        match json.tags {
            Some(mut x) => {
                results.append(&mut x);
            }
            None => {
                results.push(format!("Image not found: {namespace}/{repository}"));
            }
        }
        if args.name.is_some() {
            let pat = args.name.clone().unwrap();
            results = results
                .into_iter()
                .filter(|x| x.contains(&pat))
                .collect::<Vec<String>>();
        };
        Ok(results)
    }
}
