use std::fs;
use std::path::Path;
use std::process::{Command, Output};

use tempfile::TempDir;

fn write_repo_files(repo_root: &Path, version: &str, changelog: &str) {
    fs::write(
        repo_root.join("Cargo.toml"),
        format!("[package]\nname = \"runewarp\"\nversion = \"{version}\"\nedition = \"2024\"\n"),
    )
    .unwrap();
    fs::write(repo_root.join("CHANGELOG.md"), changelog).unwrap();
}

fn run_validator(repo_root: &Path, args: &[&str]) -> Output {
    let script_path = Path::new(env!("CARGO_MANIFEST_DIR"))
        .join("scripts")
        .join("validate-release-metadata.sh");

    Command::new("bash")
        .arg(script_path)
        .args(args)
        .arg("--repo-root")
        .arg(repo_root)
        .output()
        .unwrap()
}

fn run_release_notes(repo_root: &Path, version: &str) -> Output {
    let script_path = Path::new(env!("CARGO_MANIFEST_DIR"))
        .join("scripts")
        .join("render-release-notes.sh");

    Command::new("bash")
        .arg(script_path)
        .arg("--repo-root")
        .arg(repo_root)
        .arg("--version")
        .arg(version)
        .output()
        .unwrap()
}

fn init_git_repo(repo_root: &Path, tag: &str) {
    let run = |args: &[&str]| {
        let status = Command::new("git")
            .args(args)
            .current_dir(repo_root)
            .status()
            .unwrap();
        assert!(status.success(), "git command failed: {args:?}");
    };

    run(&["init", "-q"]);
    run(&["config", "user.name", "Runewarp Tests"]);
    run(&["config", "user.email", "tests@example.com"]);
    run(&["config", "commit.gpgsign", "false"]);
    run(&["add", "Cargo.toml", "CHANGELOG.md"]);
    run(&["commit", "-qm", "test release metadata"]);
    run(&["tag", "-a", tag, "-m", tag]);
}

#[test]
fn ci_mode_accepts_a_stable_release_entry_without_unreleased() {
    let temp_dir = TempDir::new().unwrap();
    write_repo_files(
        temp_dir.path(),
        "0.1.0",
        "# Changelog\n\nAll notable changes to Runewarp will be documented in this file.\n\nThe format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).\n\n## [0.1.0] - 2026-05-29\n\n### Added\n\n- Public release metadata contract.\n",
    );

    let output = run_validator(temp_dir.path(), &["ci"]);
    assert!(output.status.success());
}

#[test]
fn ci_mode_rejects_unreleased_for_a_stable_version() {
    let temp_dir = TempDir::new().unwrap();
    write_repo_files(
        temp_dir.path(),
        "0.1.0",
        "# Changelog\n\nAll notable changes to Runewarp will be documented in this file.\n\nThe format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).\n\n## Unreleased\n\n### Added\n\n- Work in progress.\n\n## [0.1.0] - 2026-05-29\n\n### Added\n\n- Public release metadata contract.\n",
    );

    let output = run_validator(temp_dir.path(), &["ci"]);
    assert!(!output.status.success());

    let stderr = String::from_utf8(output.stderr).unwrap();
    assert!(stderr.contains("must not keep an Unreleased section"));
}

#[test]
fn ci_mode_accepts_a_dev_version_with_unreleased() {
    let temp_dir = TempDir::new().unwrap();
    write_repo_files(
        temp_dir.path(),
        "0.2.0-dev",
        "# Changelog\n\nAll notable changes to Runewarp will be documented in this file.\n\nThe format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).\n\n## Unreleased\n\n### Added\n\n- Upcoming release notes.\n\n## [0.1.0] - 2026-05-29\n\n### Added\n\n- Public release metadata contract.\n",
    );

    let output = run_validator(temp_dir.path(), &["ci"]);
    assert!(output.status.success());
}

#[test]
fn ci_mode_rejects_nonstandard_changelog_subsections() {
    let temp_dir = TempDir::new().unwrap();
    write_repo_files(
        temp_dir.path(),
        "0.1.0",
        "# Changelog\n\nAll notable changes to Runewarp will be documented in this file.\n\nThe format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).\n\n## [0.1.0] - 2026-05-29\n\n### Features\n\n- Public release metadata contract.\n",
    );

    let output = run_validator(temp_dir.path(), &["ci"]);
    assert!(!output.status.success());

    let stderr = String::from_utf8(output.stderr).unwrap();
    assert!(stderr.contains("invalid changelog subsection: Features"));
}

#[test]
fn release_mode_requires_a_matching_head_tag() {
    let temp_dir = TempDir::new().unwrap();
    write_repo_files(
        temp_dir.path(),
        "0.1.0",
        "# Changelog\n\nAll notable changes to Runewarp will be documented in this file.\n\nThe format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).\n\n## [0.1.0] - 2026-05-29\n\n### Added\n\n- Public release metadata contract.\n",
    );
    init_git_repo(temp_dir.path(), "v0.1.0");

    let output = run_validator(temp_dir.path(), &["release", "--tag", "v0.1.0"]);
    assert!(output.status.success());
}

#[test]
fn release_mode_rejects_a_mismatched_tag() {
    let temp_dir = TempDir::new().unwrap();
    write_repo_files(
        temp_dir.path(),
        "0.1.0",
        "# Changelog\n\nAll notable changes to Runewarp will be documented in this file.\n\nThe format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).\n\n## [0.1.0] - 2026-05-29\n\n### Added\n\n- Public release metadata contract.\n",
    );
    init_git_repo(temp_dir.path(), "v0.1.0");

    let output = run_validator(temp_dir.path(), &["release", "--tag", "v0.1.1"]);
    assert!(!output.status.success());

    let stderr = String::from_utf8(output.stderr).unwrap();
    assert!(stderr.contains("must match Cargo version 0.1.0"));
}

#[test]
fn render_release_notes_outputs_the_changelog_entry_and_install_appendix() {
    let temp_dir = TempDir::new().unwrap();
    write_repo_files(
        temp_dir.path(),
        "0.1.0",
        "# Changelog\n\nAll notable changes to Runewarp will be documented in this file.\n\nThe format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).\n\n## [0.1.0] - 2026-05-29\n\n### Added\n\n- Public release metadata contract.\n\n### Security\n\n- Stable trust boundaries.\n",
    );

    let output = run_release_notes(temp_dir.path(), "0.1.0");
    assert!(output.status.success());

    let stdout = String::from_utf8(output.stdout).unwrap();
    assert!(stdout.contains("### Added"));
    assert!(stdout.contains("- Public release metadata contract."));
    assert!(stdout.contains("## Install"));
    assert!(stdout.contains("cargo install --version 0.1.0 runewarp"));
    assert!(stdout.contains("docker pull runewarp/runewarp:0.1.0"));
}
