use crate::{diff, fs::clean::clean_repo, fs::util::*};
use humantime::Duration;
use std::io;
use std::path::PathBuf;

pub fn create_patch(
    repo_dir: &PathBuf,
    data_dir: &PathBuf,
    keep_dur: Duration,
    dry_run: bool,
) -> Result<(), io::Error> {
    let dirs = setup_dirs(repo_dir, Some(data_dir), true, true);
    init_lock(repo_dir)?;

    let ts = get_ts();
    let table_path = if !dry_run {
        Some(format!("{}/{}", dirs.patches_path, ts))
    } else {
        None
    };

    let new_paths = get_walk(&dirs.data_dir.as_ref().unwrap(), &dirs.ignore_path)?;
    let repo_paths = get_walk(&dirs.repo_base, &dirs.ignore_path)?;

    let dir_diffs = get_diffs_dir(
        &new_paths,
        &repo_paths,
        &dirs.data_dir.as_ref().unwrap(),
        &dirs.repo_base,
    );

    let file_diffs = get_diffs_file(
        &new_paths,
        &repo_paths,
        &dirs.data_dir.as_ref().unwrap(),
        &dirs.repo_base,
        false,
    );

    let copy_files: Vec<PathBuf> = diff::new_table(
        file_diffs.clone(),
        &dirs.repo_base,
        &dirs.data_dir.as_ref().unwrap(),
        &table_path,
        &dirs.hash_path,
    )?
    .into_iter()
    .map(|f| PathBuf::from(format!("{}/{}", &dirs.data_dir.clone().unwrap(), f)))
    .collect();

    if !dry_run {
        // make any new dirs
        copy_dir_all(
            &dirs.data_dir.clone().unwrap(),
            &new_paths.iter().collect(),
            &dirs.repo_base,
            false,
            true,
            false,
        )?;

        // delete old dirs + files
        rm_old(
            &dirs.repo_base,
            dir_diffs
                .1
                .into_iter()
                .chain(file_diffs.1.into_iter().map(|g| g.1))
                .collect(),
        )?;

        // copy any new or updated files
        copy_dir_all(
            &dirs.data_dir.unwrap(),
            &copy_files.iter().collect(),
            &dirs.repo_base,
            true,
            false,
            true,
        )?;

        clean_repo(&repo_dir, keep_dur, Some(ts))?;
    }
    Ok(())
}
