use std::fs;
use std::path::Path;

use super::verify_inside_clippy_dir;

/// Rusts setup uses `git rev-parse --git-common-dir` to get the root directory of the repo.
/// I've decided against this for the sake of simplicity and to make sure that it doesn't install
/// the hook if `clippy_dev` would be used in the rust tree. The hook also references this tool
/// for formatting and should therefor only be used in a normal clone of clippy
const REPO_GIT_DIR: &str = ".git";
const HOOK_SOURCE_FILE: &str = "util/etc/pre-commit.sh";
const HOOK_TARGET_FILE: &str = ".git/hooks/pre-commit";

pub fn install_hook(force_override: bool) {
    if !check_precondition(force_override) {
        return;
    }

    // So a little bit of a funny story. Git on unix requires the pre-commit file
    // to have the `execute` permission to be set. The Rust functions for modifying
    // these flags doesn't seem to work when executed with normal user permissions.
    //
    // However, there is a little hack that is also being used by Rust itself in their
    // setup script. Git saves the `execute` flag when syncing files. This means
    // that we can check in a file with execution permissions and the sync it to create
    // a file with the flag set. We then copy this file here. The copy function will also
    // include the `execute` permission.
    match fs::copy(HOOK_SOURCE_FILE, HOOK_TARGET_FILE) {
        Ok(_) => {
            println!("info: the hook can be removed with `cargo dev remove git-hook`");
            println!("git hook successfully installed");
        },
        Err(err) => eprintln!(
            "error: unable to copy `{}` to `{}` ({})",
            HOOK_SOURCE_FILE, HOOK_TARGET_FILE, err
        ),
    }
}

fn check_precondition(force_override: bool) -> bool {
    if !verify_inside_clippy_dir() {
        return false;
    }

    // Make sure that we can find the git repository
    let git_path = Path::new(REPO_GIT_DIR);
    if !git_path.exists() || !git_path.is_dir() {
        eprintln!("error: clippy_dev was unable to find the `.git` directory");
        return false;
    }

    // Make sure that we don't override an existing hook by accident
    let path = Path::new(HOOK_TARGET_FILE);
    if path.exists() {
        if force_override {
            return delete_git_hook_file(path);
        }

        eprintln!("error: there is already a pre-commit hook installed");
        println!("info: use the `--force-override` flag to override the existing hook");
        return false;
    }

    true
}

pub fn remove_hook() {
    let path = Path::new(HOOK_TARGET_FILE);
    if path.exists() {
        if delete_git_hook_file(path) {
            println!("git hook successfully removed");
        }
    } else {
        println!("no pre-commit hook was found");
    }
}

fn delete_git_hook_file(path: &Path) -> bool {
    if let Err(err) = fs::remove_file(path) {
        eprintln!("error: unable to delete existing pre-commit git hook ({})", err);
        false
    } else {
        true
    }
}
