use crate::constants::{REPO_DIR, SCHEMES_REPO_NAME, SCHEMES_REPO_REVISION, SCHEMES_REPO_URL};
use crate::repo;
use crate::{config::Config, constants::REPO_NAME};
use anyhow::{Context, Result};
use std::path::Path;

fn update_item(
    item_name: &str,
    item_url: &str,
    item_path: &Path,
    revision: Option<&str>,
    is_quiet: bool,
) -> Result<()> {
    if item_path.is_dir() {
        let is_clean = repo::is_clean(item_path)?;

        if is_clean {
            let rev = revision.unwrap_or("main");

            repo::update(item_path, item_url, revision)
                .with_context(|| format!("Error updating {item_name} to {item_url}@{rev}"))?;

            if !is_quiet {
                println!("{item_name} up to date");
            }
        } else if !is_quiet {
            println!("{item_name} contains uncommitted changes, please commit or remove and then run `{REPO_NAME} update` again.");
        }
    } else if !is_quiet {
        println!("{item_name} not installed (run `{REPO_NAME} install`)");
    }

    Ok(())
}

/// Updates local files
///
/// Updates the provided repositories in config file by doing a git pull
pub fn update(config_path: &Path, data_path: &Path, is_quiet: bool) -> Result<()> {
    let config = Config::read(config_path)?;
    let items = config.items.unwrap_or_default();
    let hooks_path = data_path.join(REPO_DIR);

    for item in items {
        let item_path = hooks_path.join(&item.name);

        update_item(
            item.name.as_str(),
            item.path.as_str(),
            &item_path,
            item.revision.as_deref(),
            is_quiet,
        )?;
    }

    let schemes_repo_path = hooks_path.join(SCHEMES_REPO_NAME);

    update_item(
        SCHEMES_REPO_NAME,
        SCHEMES_REPO_URL,
        &schemes_repo_path,
        Some(SCHEMES_REPO_REVISION),
        is_quiet,
    )?;

    Ok(())
}
