//! Integration tests for the `update` subcommand.
//!
//! Covers: updating without prior install, with prior install, `--quiet`
//! flag, remote URL changes, revision changes, and invalid revision
//! handling (tags, branches, commit SHAs on wrong remote).
//!
//! Requires network access on first run (repos are cached in `tmp/repos/`).

mod utils;

use std::process::Command;

use crate::utils::{setup, REPO_NAME};
use anyhow::{ensure, Result};
use utils::write_to_file;

#[test]
fn test_cli_update_subcommand_without_setup() -> Result<()> {
    // -------
    // Arrange
    // -------
    let (_, _data_path, command_vec, _temp_dir) =
        setup("test_cli_update_subcommand_without_setup", "update", false)?;
    let expected_output = format!("tinted-shell not installed (run `{REPO_NAME} install`)");

    // ---
    // Act
    // ---
    let (stdout, _) = utils::run_command(&command_vec)?;

    // ------
    // Assert
    // ------
    ensure!(
        stdout.contains(&expected_output),
        "Expected stdout to contain: {expected_output}\nGot: {stdout}"
    );

    Ok(())
}

#[test]
fn test_cli_update_subcommand_with_setup() -> Result<()> {
    // -------
    // Arrange
    // -------
    let (_, _data_path, command_vec, _temp_dir) =
        setup("test_cli_update_subcommand_with_setup", "update", true)?;
    let expected_output = "tinted-shell up to date";

    // ---
    // Act
    // ---
    let (stdout, stderr) = utils::run_command(&command_vec)?;

    // ------
    // Assert
    // ------
    ensure!(
        stdout.contains(expected_output),
        "Expected stdout to contain: {expected_output}\nGot: {stdout}"
    );
    ensure!(stderr.is_empty(), "Expected empty stderr, got: {stderr}");

    Ok(())
}

#[test]
fn test_cli_update_subcommand_with_setup_quiet_flag() -> Result<()> {
    // -------
    // Arrange
    // -------
    let (_, _data_path, command_vec, _temp_dir) = setup(
        "test_cli_update_subcommand_with_setup_quiet_flag",
        "update --quiet",
        true,
    )?;

    // ---
    // Act
    // ---
    let (stdout, stderr) = utils::run_command(&command_vec)?;

    // ------
    // Assert
    // ------
    ensure!(stdout.is_empty(), "Expected empty stdout, got: {stdout}");
    ensure!(stderr.is_empty(), "Expected empty stderr, got: {stderr}");

    Ok(())
}

#[test]
fn test_cli_update_subcommand_with_new_remote() -> Result<()> {
    // -------
    // Arrange
    // -------
    let (config_path, data_path, command_vec, _temp_dir) = setup(
        "test_cli_update_subcommand_with_new_remote",
        "update",
        false,
    )?;
    let expected_output = "tinted-vim up to date";

    let config_content = r#"[[items]]
path = "https://github.com/tinted-theming/base16-vim"
name = "tinted-vim"
themes-dir = "colors"
"#;

    write_to_file(&config_path, config_content)?;

    // ---
    // Act
    // ---
    utils::run_install_command(&config_path, &data_path)?;

    // Replace the remote with a new one
    let config_content = r#"[[items]]
path = "https://github.com/tinted-theming/tinted-vim"
name = "tinted-vim"
themes-dir = "colors"
"#;
    write_to_file(&config_path, config_content)?;
    let (stdout, _) = utils::run_command(&command_vec)?;

    let repo_path = data_path.join("repos/tinted-vim");
    let output = Command::new("git")
        .args(vec!["remote", "get-url", "origin"])
        .current_dir(&repo_path)
        .output()?;

    let remote_stdout = String::from_utf8(output.stdout)?;

    // ------
    // Assert
    // ------
    ensure!(
        stdout.contains(expected_output),
        "Expected stdout to contain: {expected_output}\nGot: {stdout}"
    );
    let remote_url = "https://github.com/tinted-theming/tinted-vim";
    let has_match = remote_stdout.lines().any(|line| line == remote_url);

    ensure!(
        has_match,
        format!("Expected origin remote to point to URL {remote_url}")
    );

    Ok(())
}

#[test]
fn test_cli_update_subcommand_with_new_revision() -> Result<()> {
    // -------
    // Arrange
    // -------
    let (config_path, data_path, command_vec, _temp_dir) = setup(
        "test_cli_update_subcommand_with_new_revision",
        "update",
        true,
    )?;
    let expected_output = "tinted-vim up to date";
    let expected_revision = "c7ab4daadd143a78d4fc561d216d83ef0188f343";
    let config_content = r#"[[items]]
path = "https://github.com/tinted-theming/tinted-vim"
name = "tinted-vim"
themes-dir = "colors"
"#;

    write_to_file(&config_path, config_content)?;

    // ---
    // Act
    utils::run_install_command(&config_path, &data_path)?;

    // Replace the remote with a new one
    let config_content = r#"[[items]]
path = "https://github.com/tinted-theming/tinted-vim"
name = "tinted-vim"
themes-dir = "colors"
revision = "tinty-test-tag-01"
"#;
    write_to_file(&config_path, config_content)?;
    let (stdout, _) = utils::run_command(&command_vec)?;

    let repo_path = data_path.join("repos/tinted-vim");
    let output = Command::new("git")
        .args(vec!["rev-parse", "--verify", "HEAD"])
        .current_dir(&repo_path)
        .output()?;
    let rev_parse_out = String::from_utf8(output.stdout)?;

    // ------
    // Assert
    // ------
    ensure!(
        stdout.contains(expected_output),
        "stdout does not contain expected output"
    );
    let has_match = rev_parse_out.lines().any(|line| line == expected_revision);
    ensure!(has_match, "Expected revision {expected_revision}");

    Ok(())
}

#[test]
fn test_cli_update_subcommand_with_new_remote_but_invalid_tag_revision() -> Result<()> {
    // -------
    // Arrange
    // -------
    let (config_path, data_path, command_vec, _temp_dir) = setup(
        "test_cli_update_subcommand_with_new_remote_but_invalid_tag_revision",
        "update",
        true,
    )?;
    let expected_output = "tinted-vim up to date";
    let config_content = r#"[[items]]
path = "https://github.com/tinted-theming/tinted-vim"
name = "tinted-vim"
themes-dir = "colors"
"#;
    let git_tag_name = "invalid-git-tag";

    write_to_file(&config_path, config_content)?;

    // ---
    // Act
    // ---
    utils::run_install_command(&config_path, &data_path)?;

    // Replace the remote with a new one
    // tinty-test-tag-01 exist in tinted-theming but not on this one.
    let config_content = format!(
        r#"[[items]]
path = "https://github.com/tinted-theming/base16-vim"
name = "tinted-vim"
themes-dir = "colors"
revision = "{git_tag_name}"
"#
    );
    write_to_file(&config_path, &config_content)?;
    let (stdout, stderr) = utils::run_command(&command_vec)?;

    let repo_path = data_path.join("repos/tinted-vim");
    let output = Command::new("git")
        .args(vec!["remote", "get-url", "origin"])
        .current_dir(&repo_path)
        .output()?;

    let remote_out = String::from_utf8(output.stdout)?;

    // ------
    // Assert
    // ------
    ensure!(
        !stdout.contains(expected_output),
        "stdout contains unexpected output"
    );
    ensure!(
        stderr.contains(format!("cannot resolve {git_tag_name}").as_str()),
        "Expected stderr to contain 'cannot resolve {git_tag_name}'\nGot: {stderr}"
    );
    let expected_remote_url = "https://github.com/tinted-theming/tinted-vim";
    let has_match = remote_out.starts_with(expected_remote_url);

    ensure!(
        has_match,
        format!("Expected remote URL {expected_remote_url}")
    );

    Ok(())
}

#[test]
fn test_cli_update_subcommand_with_new_remote_but_invalid_branch_revision() -> Result<()> {
    // -------
    // Arrange
    // -------
    let (config_path, data_path, command_vec, _temp_dir) = setup(
        "test_cli_update_subcommand_with_new_remote_but_invalid_branch_revision",
        "update",
        true,
    )?;
    let unexpected_output = "tinted-vim up to date";
    let config_content = r#"[[items]]
path = "https://github.com/tinted-theming/tinted-vim"
name = "tinted-vim"
themes-dir = "colors"
"#;

    write_to_file(&config_path, config_content)?;

    // ---
    // Act
    // ---
    // Replace the remote with a new one
    // tinty-test-01 exist in tinted-theming but not on this one.
    let config_content = r#"[[items]]
path = "https://github.com/tinted-theming/base16-vim"
name = "tinted-vim"
themes-dir = "colors"
revision = "tinty-test-01"
"#;
    write_to_file(&config_path, config_content)?;
    let (stdout, stderr) = utils::run_command(&command_vec)?;
    let repo_path = data_path.join("repos/tinted-vim");
    let output = Command::new("git")
        .args(vec!["remote", "get-url", "origin"])
        .current_dir(&repo_path)
        .output()?;
    let remote_out = String::from_utf8(output.stdout)?;

    // ------
    // Assert
    // ------
    ensure!(
        !stdout.contains(unexpected_output),
        "stdout contains unexpected output"
    );
    ensure!(
        stderr.contains("cannot resolve tinty-test-01"),
        "Expected stderr to contain 'cannot resolve tinty-test-01'\nGot: {stderr}"
    );
    let expected_remote_url = "https://github.com/tinted-theming/tinted-vim";
    let has_match = remote_out.trim().starts_with(expected_remote_url);

    ensure!(
        has_match,
        format!("Expected remote URL {expected_remote_url}")
    );

    Ok(())
}

#[test]
fn test_cli_update_subcommand_with_new_remote_but_invalid_commit_sha1_revision() -> Result<()> {
    // -------
    // Arrange
    // -------
    let (config_path, data_path, command_vec, _temp_dir) = setup(
        "test_cli_update_subcommand_with_new_remote_but_commit_sha1_revision",
        "update",
        false,
    )?;
    let expected_output = "tinted-vim up to date";
    let config_content = r#"[[items]]
path = "https://github.com/tinted-theming/tinted-vim"
name = "tinted-vim"
themes-dir = "colors"
"#;
    write_to_file(&config_path, config_content)?;

    // ---
    // Act
    // ---
    utils::run_install_command(&config_path, &data_path)?;

    // Replace the remote with a new one
    // This commit SHA only exist in tinted-theming but not on this one.
    let config_content = r#"[[items]]
path = "https://github.com/tinted-theming/base16-vim"
name = "tinted-vim"
themes-dir = "colors"
revision = "43b36ed5eadad59a5027e442330d2485b8607b34"
"#;
    write_to_file(&config_path, config_content)?;

    let (stdout, stderr) = utils::run_command(&command_vec)?;

    let repo_path = data_path.join("repos/tinted-vim");
    let output = Command::new("git")
        .args(vec!["remote", "get-url", "origin"])
        .current_dir(&repo_path)
        .output()?;

    let remote_out = String::from_utf8(output.stdout)?;

    // ------
    // Assert
    // ------
    ensure!(
        !stdout.contains(expected_output),
        "stdout contains unexpected output"
    );
    ensure!(
        stderr.contains("cannot find revision 43b36ed5eadad59a5027e442330d2485b8607b34"),
        "Expected stderr to contain 'cannot find revision 43b36ed...'\nGot: {stderr}"
    );
    let expected_remote_url = "https://github.com/tinted-theming/tinted-vim";
    let has_match = remote_out.lines().any(|line| line == expected_remote_url);

    ensure!(
        has_match,
        format!("Expected remote URL {expected_remote_url}")
    );

    Ok(())
}
