use std::fs;
use std::io::Write;

mod common;
use common::Environment;

fn create_environment() -> anyhow::Result<(Environment, String)> {
    let (e, root) = Environment::scooby_gang_bootstrap()?;

    // Create a bare repository at our scratch location.
    let scratch = e.scratch_state().display().to_string();
    e.git(&["init", "--bare", &scratch])?;

    // Set us as update hook.
    let update_hook = e.scratch_state().join("hooks").join("update");
    let mut update = fs::File::create(&update_hook)?;
    writeln!(update, "#!/bin/sh")?;
    writeln!(update)?;
    writeln!(update,
             "{} update-hook --trust-root={} \"$@\"",
             Environment::sq_git_path()?.display(),
             root)?;

    #[cfg(unix)]
    {
        // Make file executable.
        use std::os::unix::fs::PermissionsExt;

        let metadata = update.metadata()?;
        let mut permissions = metadata.permissions();
        permissions.set_mode(0o755);
        update.set_permissions(permissions)?;
    }

    // Add as origin.
    e.git(&["remote", "add", "origin", &scratch])?;

    Ok((e, root))
}

#[test]
fn update_hook() -> anyhow::Result<()> {
    let (e, _root) = create_environment()?;
    let p = e.git_state();

    // Bookmark.
    e.git(&["checkout", "-b", "test-base"])?;

    // Willow's code-signing key can change the source code, as she
    // has the sign-commit right.
    e.git(&["checkout", "-b", "test-willow"])?;
    fs::write(p.join("a"), "Aller Anfang ist schwer.")?;
    e.git(&["add", "a"])?;
    e.git(&[
        "commit",
        "-m", "First change.",
        &format!("-S{}", e.willow.fingerprint),
    ])?;

    e.git(&["push", "origin", "test-willow"])?;

    // Reset.
    e.git(&["checkout", "test-base"])?;
    e.git(&["clean", "-fdx"])?;

    // Her release key also has that right, because she needs it in
    // order to give it to new users.
    e.git(&["checkout", "-b", "test-willow-release"])?;
    fs::write(p.join("a"), "Aller Anfang ist schwer.  -- Schiller")?;
    e.git(&["add", "a"])?;
    e.git(&[
        "commit",
        "-m", "Someone is not quite correct on the internet.",
        &format!("-S{}", e.willow_release.fingerprint),
    ])?;

    e.git(&["push", "origin", "test-willow-release"])?;

    // Reset.
    e.git(&["checkout", "test-base"])?;
    e.git(&["clean", "-fdx"])?;

    // Buffy's cert was not yet added, so she may not sign commits.
    e.git(&["checkout", "-b", "test-buffy"])?;
    fs::write(p.join("a"),
              "Aller Anfang ist schwer, unless you are super strong!1")?;
    e.git(&["add", "a"])?;
    e.git(&[
        "commit",
        "-m", "Well, actually...",
        &format!("-S{}", e.buffy.fingerprint),
    ])?;

    if let Ok(output) = e.git(&["push", "origin", "test-buffy"]) {
        eprintln!("stderr: {}", String::from_utf8_lossy(&output.stderr));
    }
    assert!(e.git(&["push", "origin", "test-buffy"]).is_err());

    Ok(())
}

#[test]
fn rebase() -> anyhow::Result<()> {
    let (e, _root) = create_environment()?;
    let p = e.git_state();

    // Bookmark.
    e.git(&["checkout", "-b", "test-base"])?;

    // There are two threads of development.  Let's start the first
    // one.
    e.git(&["checkout", "-b", "feature-one"])?;
    fs::write(p.join("a"), "Aller Anfang ist schwer.")?;
    e.git(&["add", "a"])?;
    e.git(&[
        "commit",
        "-m", "First change of the first feature.",
        &format!("-S{}", e.willow.fingerprint),
    ])?;

    e.git(&["push", "origin", "feature-one"])?;

    // Reset.
    e.git(&["checkout", "test-base"])?;
    e.git(&["clean", "-fdx"])?;

    // There are two threads of development.  Let's start the second
    // one.
    e.git(&["checkout", "-b", "feature-two"])?;
    fs::write(p.join("b"), "And now for something completely different.")?;
    e.git(&["add", "b"])?;
    e.git(&[
        "commit",
        "-m", "First change of the second feature.",
        &format!("-S{}", e.willow.fingerprint),
    ])?;

    e.git(&["push", "origin", "feature-two"])?;

    // Now we rebase feature-two on top of feature-one and push it to
    // update the remote feature-two branch.
    e.git(&[
        "rebase",
        "feature-one",
        &format!("-S{}", e.willow.fingerprint),
    ])?;
    e.git(&["push", "origin", "--force", "feature-two"])?;

    Ok(())
}
