use assert_cmd::assert::OutputAssertExt;
use assert_cmd::cargo::cargo_bin;
use predicates::prelude::*;
use std::fs;
use std::process::Command;
use tempfile::TempDir;

#[test]
fn test_help_flag() {
    let mut cmd = Command::new(cargo_bin!("ntu"));
    cmd.arg("--help");
    cmd.assert()
        .success()
        .stdout(predicate::str::contains("Usage: ntu"));
}

#[test]
fn test_version_flag() {
    let mut cmd = Command::new(cargo_bin!("ntu"));
    cmd.arg("--version");
    cmd.assert()
        .success()
        .stdout(predicate::str::contains("NameToUnix"));
}

#[test]
fn test_dry_run_no_changes() {
    let temp_dir = TempDir::new().unwrap();
    let file_path = temp_dir.path().join("test file.txt");
    fs::write(&file_path, "test content").unwrap();

    let mut cmd = Command::new(cargo_bin!("ntu"));
    cmd.arg("--dry-run").arg("-r").arg(temp_dir.path());
    cmd.assert().success();

    // File should still have spaces (not renamed)
    assert!(file_path.exists());
    assert!(!temp_dir.path().join("test_file.txt").exists());
}

#[test]
fn test_actual_rename() {
    let temp_dir = TempDir::new().unwrap();
    let file_path = temp_dir.path().join("test file.txt");
    fs::write(&file_path, "test content").unwrap();

    let mut cmd = Command::new(cargo_bin!("ntu"));
    cmd.arg("-r").arg(temp_dir.path());
    cmd.assert().success();

    // File should be renamed
    assert!(!file_path.exists());
    assert!(temp_dir.path().join("test_file.txt").exists());
}

#[test]
fn test_hidden_files_preserved() {
    let temp_dir = TempDir::new().unwrap();
    let file_path = temp_dir.path().join(".gitignore");
    fs::write(&file_path, "*.tmp").unwrap();

    let mut cmd = Command::new(cargo_bin!("ntu"));
    cmd.arg("-r").arg(temp_dir.path());
    cmd.assert().success();

    // Hidden file should not be renamed
    assert!(file_path.exists());
}

#[test]
fn test_hidden_file_with_spaces() {
    let temp_dir = TempDir::new().unwrap();
    let file_path = temp_dir.path().join(".my config");
    fs::write(&file_path, "config content").unwrap();

    let mut cmd = Command::new(cargo_bin!("ntu"));
    cmd.arg("-r").arg(temp_dir.path());
    cmd.assert().success();

    // Hidden file should be renamed but keep leading dot
    assert!(!file_path.exists());
    assert!(temp_dir.path().join(".my_config").exists());
}

#[test]
fn test_umlaut_conversion() {
    let temp_dir = TempDir::new().unwrap();
    let file_path = temp_dir.path().join("Müller.txt");
    fs::write(&file_path, "test").unwrap();

    let mut cmd = Command::new(cargo_bin!("ntu"));
    cmd.arg("-r").arg(temp_dir.path());
    cmd.assert().success();

    // Umlaut should be converted
    assert!(!file_path.exists());
    assert!(temp_dir.path().join("Mueller.txt").exists());
}

#[test]
fn test_double_extension() {
    let temp_dir = TempDir::new().unwrap();
    let file_path = temp_dir.path().join("my archive.tar.gz");
    fs::write(&file_path, "archive").unwrap();

    let mut cmd = Command::new(cargo_bin!("ntu"));
    cmd.arg("-r").arg(temp_dir.path());
    cmd.assert().success();

    // Should keep .tar.gz intact
    assert!(!file_path.exists());
    assert!(temp_dir.path().join("my_archive.tar.gz").exists());
}

#[test]
fn test_exclude_pattern() {
    let temp_dir = TempDir::new().unwrap();
    let file1 = temp_dir.path().join("test file.txt");
    let file2 = temp_dir.path().join("test file.tmp");
    fs::write(&file1, "test1").unwrap();
    fs::write(&file2, "test2").unwrap();

    let mut cmd = Command::new(cargo_bin!("ntu"));
    cmd.arg("--exclude")
        .arg("*.tmp")
        .arg("-r")
        .arg(temp_dir.path());
    cmd.assert().success();

    // .txt should be renamed, .tmp should not
    assert!(!file1.exists());
    assert!(temp_dir.path().join("test_file.txt").exists());
    assert!(file2.exists()); // Excluded
}

#[test]
fn test_quiet_mode() {
    let temp_dir = TempDir::new().unwrap();
    let file_path = temp_dir.path().join("test file.txt");
    fs::write(&file_path, "test").unwrap();

    let mut cmd = Command::new(cargo_bin!("ntu"));
    cmd.arg("--quiet").arg("-r").arg(temp_dir.path());
    cmd.assert()
        .success()
        .stdout(predicate::str::is_empty());
}

#[test]
fn test_multiple_paths() {
    let temp_dir1 = TempDir::new().unwrap();
    let temp_dir2 = TempDir::new().unwrap();
    let file1 = temp_dir1.path().join("file 1.txt");
    let file2 = temp_dir2.path().join("file 2.txt");
    fs::write(&file1, "test1").unwrap();
    fs::write(&file2, "test2").unwrap();

    let mut cmd = Command::new(cargo_bin!("ntu"));
    cmd.arg("-r").arg(temp_dir1.path()).arg(temp_dir2.path());
    cmd.assert().success();

    // Both files should be renamed
    assert!(temp_dir1.path().join("file_1.txt").exists());
    assert!(temp_dir2.path().join("file_2.txt").exists());
}

#[test]
fn test_parentheses_removed() {
    let temp_dir = TempDir::new().unwrap();
    let file_path = temp_dir.path().join("Document (1).txt");
    fs::write(&file_path, "test").unwrap();

    let mut cmd = Command::new(cargo_bin!("ntu"));
    cmd.arg("-r").arg(temp_dir.path());
    cmd.assert().success();

    // Parentheses should be replaced with underscores
    assert!(!file_path.exists());
    assert!(temp_dir.path().join("Document_1.txt").exists());
}

#[test]
fn test_special_identifiers_preserved() {
    let temp_dir = TempDir::new().unwrap();
    let file_path = temp_dir.path().join("C++ Guide.pdf");
    fs::write(&file_path, "test").unwrap();

    let mut cmd = Command::new(cargo_bin!("ntu"));
    cmd.arg("-r").arg(temp_dir.path());
    cmd.assert().success();

    // C++ should be preserved
    assert!(!file_path.exists());
    assert!(temp_dir.path().join("C++_Guide.pdf").exists());
}

#[test]
fn test_non_recursive_default() {
    let temp_dir = TempDir::new().unwrap();
    fs::create_dir(temp_dir.path().join("subdir")).unwrap();
    let file1 = temp_dir.path().join("top file.txt");
    let file2 = temp_dir.path().join("subdir").join("nested file.txt");
    fs::write(&file1, "test1").unwrap();
    fs::write(&file2, "test2").unwrap();

    let mut cmd = Command::new(cargo_bin!("ntu"));
    cmd.arg(temp_dir.path());
    cmd.assert().success();

    // Top-Level umbenannt
    assert!(temp_dir.path().join("top_file.txt").exists());
    // Nested NICHT umbenannt (non-recursive)
    assert!(file2.exists());
}

#[test]
fn test_recursive_flag() {
    let temp_dir = TempDir::new().unwrap();
    fs::create_dir(temp_dir.path().join("subdir")).unwrap();
    let file1 = temp_dir.path().join("top file.txt");
    let file2 = temp_dir.path().join("subdir").join("nested file.txt");
    fs::write(&file1, "test1").unwrap();
    fs::write(&file2, "test2").unwrap();

    let mut cmd = Command::new(cargo_bin!("ntu"));
    cmd.arg("-r").arg(temp_dir.path());
    cmd.assert().success();

    // Beide umbenannt
    assert!(temp_dir.path().join("top_file.txt").exists());
    assert!(temp_dir.path().join("subdir").join("nested_file.txt").exists());
}

#[test]
fn test_conf_option_valid_file() {
    let temp_dir = TempDir::new().unwrap();
    let config_file = temp_dir.path().join("custom.toml");
    fs::write(&config_file, "[replacements]\n\"test\" = \"xyz\"").unwrap();
    let file_path = temp_dir.path().join("test_file.txt");
    fs::write(&file_path, "content").unwrap();

    let mut cmd = Command::new(cargo_bin!("ntu"));
    cmd.arg("--conf").arg(&config_file).arg(temp_dir.path());
    cmd.assert().success();

    assert!(temp_dir.path().join("xyz_file.txt").exists());
}

#[test]
fn test_conf_option_missing_file() {
    let temp_dir = TempDir::new().unwrap();
    let nonexistent = temp_dir.path().join("nonexistent.toml");

    let mut cmd = Command::new(cargo_bin!("ntu"));
    cmd.arg("--conf").arg(&nonexistent).arg(temp_dir.path());
    cmd.assert()
        .failure()
        .stderr(predicate::str::contains("nicht gefunden"));
}
