mod providers;
mod settings;

use std::sync::Arc;

use anyhow::Context as _;
use anyhow::Result;
use git::GitHostingProviderRegistry;
use git::repository::GitRepository;
use gpui::App;
use url::Url;
use util::maybe;

pub use crate::providers::*;
pub use crate::settings::*;

/// Initializes the Git hosting providers.
pub fn init(cx: &mut App) {
    crate::settings::init(cx);

    let provider_registry = GitHostingProviderRegistry::global(cx);
    provider_registry.register_hosting_provider(Arc::new(Azure));
    provider_registry.register_hosting_provider(Arc::new(Bitbucket::public_instance()));
    provider_registry.register_hosting_provider(Arc::new(Chromium));
    provider_registry.register_hosting_provider(Arc::new(Forgejo::public_instance()));
    provider_registry.register_hosting_provider(Arc::new(Gitea::public_instance()));
    provider_registry.register_hosting_provider(Arc::new(Gitee));
    provider_registry.register_hosting_provider(Arc::new(Github::public_instance()));
    provider_registry.register_hosting_provider(Arc::new(Gitlab::public_instance()));
    provider_registry.register_hosting_provider(Arc::new(SourceHut::public_instance()));
}

/// Registers additional Git hosting providers.
///
/// These require information from the Git repository to construct, so their
/// registration is deferred until we have a Git repository initialized.
pub async fn register_additional_providers(
    provider_registry: Arc<GitHostingProviderRegistry>,
    repository: Arc<dyn GitRepository>,
) {
    let Some(origin_url) = repository.remote_url("origin").await else {
        return;
    };

    if let Ok(gitlab_self_hosted) = Gitlab::from_remote_url(&origin_url) {
        provider_registry.register_hosting_provider(Arc::new(gitlab_self_hosted));
    } else if let Ok(github_self_hosted) = Github::from_remote_url(&origin_url) {
        provider_registry.register_hosting_provider(Arc::new(github_self_hosted));
    } else if let Ok(forgejo_self_hosted) = Forgejo::from_remote_url(&origin_url) {
        provider_registry.register_hosting_provider(Arc::new(forgejo_self_hosted));
    } else if let Ok(gitea_self_hosted) = Gitea::from_remote_url(&origin_url) {
        provider_registry.register_hosting_provider(Arc::new(gitea_self_hosted));
    } else if let Ok(bitbucket_self_hosted) = Bitbucket::from_remote_url(&origin_url) {
        provider_registry.register_hosting_provider(Arc::new(bitbucket_self_hosted));
    } else if let Ok(sourcehut_self_hosted) = SourceHut::from_remote_url(&origin_url) {
        provider_registry.register_hosting_provider(Arc::new(sourcehut_self_hosted));
    }
}

pub fn get_host_from_git_remote_url(remote_url: &str) -> Result<String> {
    maybe!({
        if let Some(remote_url) = remote_url.strip_prefix("git@")
            && let Some((host, _)) = remote_url.trim_start_matches("git@").split_once(':')
        {
            return Some(host.to_string());
        }

        Url::parse(remote_url)
            .ok()
            .and_then(|remote_url| remote_url.host_str().map(|host| host.to_string()))
    })
    .context("URL has no host")
}

#[cfg(test)]
mod tests {
    use super::get_host_from_git_remote_url;
    use pretty_assertions::assert_eq;

    #[test]
    fn test_get_host_from_git_remote_url() {
        let tests = [
            (
                "https://jlannister@github.com/some-org/some-repo.git",
                Some("github.com".to_string()),
            ),
            (
                "git@github.com:zed-industries/zed.git",
                Some("github.com".to_string()),
            ),
            (
                "git@my.super.long.subdomain.com:zed-industries/zed.git",
                Some("my.super.long.subdomain.com".to_string()),
            ),
        ];

        for (remote_url, expected_host) in tests {
            let host = get_host_from_git_remote_url(remote_url).ok();
            assert_eq!(host, expected_host);
        }
    }
}
