use std::borrow::Cow;
use std::path::Path;

use tracing::debug;
use url::Url;

use crate::error::Error;
use crate::metadata::{Metadata, load_metadata_from_path};

pub(crate) fn find_in_vcs<P: AsRef<Path>>(root: P, name: &str) -> Option<(String, Metadata)> {
    let path = root.as_ref().join("Cargo.toml");

    let md = load_metadata_from_path(&path).ok()?;

    let [package]: [cargo_metadata::Package; 1] = md
        .packages
        .into_iter()
        .filter(|p| p.name.as_ref() == name)
        .collect::<Vec<_>>()
        .try_into()
        .inspect_err(|_| {
            debug!("Failed to load crate metadata (name does not match any single workspace member): {name}");
        })
        .ok()?;

    let Some(cpath_in_vcs) = package.manifest_path.parent() else {
        debug!(
            "Failed to load crate metadata (manifest path invalid): {}",
            package.manifest_path
        );
        return None;
    };

    let Ok(path_in_vcs) = cpath_in_vcs.as_std_path().strip_prefix(&root) else {
        debug!(
            "Failed to load crate metadata (manifest path invalid): {}",
            package.manifest_path
        );
        return None;
    };

    let path = path_in_vcs.to_string_lossy().to_string();

    Some((path.clone(), Metadata::from_cargo_metadata(package, root, Some(&path))))
}

pub(crate) fn sanitize_repo_url(url: &str) -> Result<Cow<'_, str>, Error> {
    let mut parsed = Url::parse(url).map_err(|_| Error::InvalidRepoUrl {
        repo: String::from(url),
    })?;

    if parsed.host_str() == Some("github.com") {
        let Some(segments) = parsed.path_segments() else {
            return Err(Error::InvalidRepoUrl {
                repo: String::from(url),
            });
        };

        let segments: Vec<_> = segments.collect();

        match segments.as_slice() {
            [_org, _repo] => {}, // OK
            [] | [_] => {
                // https://github.com | https://github.com/user
                return Err(Error::InvalidRepoUrl {
                    repo: String::from(url),
                });
            },
            [org, repo, ..] => {
                // https://github.com/user/project/tree/main/crate/
                // FIXME: report this in a better way than a debug log?
                debug!("Attempting to sanitize GitHub repository URL: {url}");
                parsed.set_path(&format!("{org}/{repo}"));
            },
        }

        return Ok(Cow::Owned(parsed.to_string()));
    }

    Ok(Cow::Borrowed(url))
}
