Documentation
use diwe::config::{Configuration, LibraryOptions, MarkdownOptions, RefsText};
use indoc::indoc;
use std::fs::{create_dir_all, read_to_string, write};
use std::process::Command;
use tempfile::TempDir;

#[test]
fn test_delete_basic() {
    let temp_dir = setup_workspace_with_docs(vec![
        (
            "a",
            indoc! {"
            # Doc A

            [Link to B](b)
        "},
        ),
        (
            "b",
            indoc! {"
            # Doc B

            Content here
        "},
        ),
    ]);
    let temp_path = temp_dir.path();

    let output = run_delete_command(temp_path, &["b"]);
    assert!(output.status.success(), "Delete command should succeed");

    assert!(!temp_path.join("b.md").exists(), "File should be deleted");

    let a_content = read_to_string(temp_path.join("a.md")).unwrap();
    assert_eq!(a_content, "# Doc A\n");
}

#[test]
fn test_delete_removes_multiple_inclusion_edges() {
    let temp_dir = setup_workspace_with_docs(vec![
        (
            "a",
            indoc! {"
            # Doc A

            [Link 1](b)

            [Link 2](b)
        "},
        ),
        ("b", "# Doc B"),
    ]);
    let temp_path = temp_dir.path();

    let output = run_delete_command(temp_path, &["b"]);
    assert!(output.status.success());

    let a_content = read_to_string(temp_path.join("a.md")).unwrap();
    assert_eq!(a_content, "# Doc A\n");
}

#[test]
fn test_delete_updates_reference_edges() {
    let temp_dir = setup_workspace_with_docs(vec![
        (
            "a",
            indoc! {"
            # Doc A

            Some text with [inline link](b) in it.
        "},
        ),
        ("b", "# Doc B"),
    ]);
    let temp_path = temp_dir.path();

    let output = run_delete_command(temp_path, &["b"]);
    assert!(output.status.success());

    let a_content = read_to_string(temp_path.join("a.md")).unwrap();
    assert_eq!(
        a_content,
        indoc! {"
            # Doc A

            Some text with Doc B in it.
        "}
    );
}

#[test]
fn test_delete_updates_multiple_files() {
    let temp_dir = setup_workspace_with_docs(vec![
        (
            "a",
            indoc! {"
            # Doc A

            [link](target)
        "},
        ),
        (
            "b",
            indoc! {"
            # Doc B

            [another link](target)
        "},
        ),
        ("target", "# Target"),
    ]);
    let temp_path = temp_dir.path();

    let output = run_delete_command(temp_path, &["target"]);
    assert!(output.status.success());

    let a_content = read_to_string(temp_path.join("a.md")).unwrap();
    let b_content = read_to_string(temp_path.join("b.md")).unwrap();

    assert_eq!(a_content, "# Doc A\n");
    assert_eq!(b_content, "# Doc B\n");
}

#[test]
fn test_delete_nonexistent_key() {
    let temp_dir = setup_workspace_with_docs(vec![("a", "# Doc A")]);
    let temp_path = temp_dir.path();

    let output = run_delete_command(temp_path, &["nonexistent"]);
    assert!(!output.status.success(), "Should fail for nonexistent key");

    let stderr = String::from_utf8(output.stderr).unwrap();
    assert_eq!(stderr, "Error: Document 'nonexistent' not found\n");
}

#[test]
fn test_delete_dry_run() {
    let temp_dir = setup_workspace_with_docs(vec![
        (
            "a",
            indoc! {"
            # Doc A

            [link](b)
        "},
        ),
        ("b", "# Doc B"),
    ]);
    let temp_path = temp_dir.path();

    let output = run_delete_command(temp_path, &["b", "--dry-run"]);
    assert!(output.status.success());

    assert!(temp_path.join("b.md").exists(), "File should still exist");

    let a_content = read_to_string(temp_path.join("a.md")).unwrap();
    assert_eq!(
        a_content,
        indoc! {"
            # Doc A

            [link](b)
        "}
    );
}

#[test]
fn test_delete_keys_output() {
    let temp_dir = setup_workspace_with_docs(vec![("a", "[link](b)"), ("b", "# Doc B")]);
    let temp_path = temp_dir.path();

    let output = run_delete_command(temp_path, &["b", "--keys", "--dry-run"]);
    assert!(output.status.success());

    let stdout = String::from_utf8(output.stdout).unwrap();
    assert_eq!(stdout, "a\nb\n");
}

#[test]
fn test_delete_quiet_mode() {
    let temp_dir = setup_workspace_with_docs(vec![("a", "[link](b)"), ("b", "# Doc B")]);
    let temp_path = temp_dir.path();

    let output = run_delete_command(temp_path, &["b", "--quiet"]);
    assert!(output.status.success());

    let stdout = String::from_utf8(output.stdout).unwrap();
    assert!(
        stdout.trim().is_empty(),
        "Quiet mode should suppress output"
    );
}

#[test]
fn test_delete_preserves_other_content() {
    let temp_dir = setup_workspace_with_docs(vec![
        (
            "a",
            indoc! {"
            # Doc A

            Some content before.

            [link](b)

            Some content after.

            ## Section

            More content.
        "},
        ),
        ("b", "# Doc B"),
    ]);
    let temp_path = temp_dir.path();

    let output = run_delete_command(temp_path, &["b"]);
    assert!(output.status.success());

    let a_content = read_to_string(temp_path.join("a.md")).unwrap();
    assert_eq!(
        a_content,
        indoc! {"
            # Doc A

            Some content before.

            Some content after.

            ## Section

            More content.
        "}
    );
}

#[test]
fn document_expect_violation_aborts_without_deleting() {
    let temp_dir = setup_workspace_with_docs(vec![("a", "# Alpha"), ("b", "# Beta")]);
    let temp_path = temp_dir.path();

    let output = run_delete_command(temp_path, &["--filter", "{}", "--expect", "1"]);
    assert!(!output.status.success());
    let stderr = String::from_utf8_lossy(&output.stderr);
    assert_eq!(
        stderr,
        indoc! {"
            error: delete expects 1 document, matched 2
              a › Alpha
              b › Beta
            hint: adjust the filter or raise expect
        "}
    );
    assert!(temp_path.join("a.md").exists());
    assert!(temp_path.join("b.md").exists());
}

#[test]
fn strict_without_expect_aborts_without_deleting() {
    let temp_dir = setup_workspace_with_docs(vec![("a", "# Alpha")]);
    let temp_path = temp_dir.path();

    let output = run_delete_command(temp_path, &["--filter", "{}", "--strict"]);
    assert!(!output.status.success());
    let stderr = String::from_utf8_lossy(&output.stderr);
    assert_eq!(
        stderr,
        indoc! {"
            error: --strict requires the document-level --expect guard; missing: document-level --expect
            hint: state the expected count — 1 for a precision edit, '{ min: 1 }' for a bulk delete that must match, '{ min: 0 }' when zero is acceptable
        "}
    );
    assert!(temp_path.join("a.md").exists());
}

#[test]
fn strict_with_expect_deletes() {
    let temp_dir = setup_workspace_with_docs(vec![("a", "# Alpha"), ("b", "# Beta")]);
    let temp_path = temp_dir.path();

    let output = run_delete_command(
        temp_path,
        &["--filter", "{}", "--strict", "--expect", "{ max: 5 }"],
    );
    assert!(output.status.success());
    assert!(!temp_path.join("a.md").exists());
    assert!(!temp_path.join("b.md").exists());
}

fn setup_workspace_with_docs(docs: Vec<(&str, &str)>) -> TempDir {
    let temp_dir = TempDir::new().expect("Failed to create temp directory");
    let temp_path = temp_dir.path();

    setup_iwe_config(temp_path);

    for (key, content) in docs {
        write(temp_path.join(format!("{}.md", key)), content).expect("Should write file");
    }

    temp_dir
}

fn setup_iwe_config(temp_path: &std::path::Path) {
    create_dir_all(temp_path.join(".iwe")).expect("Failed to create .iwe directory");

    let config = Configuration {
        library: LibraryOptions {
            path: "".to_string(),
            ..Default::default()
        },
        markdown: MarkdownOptions {
            refs_extension: "".to_string(),
            refs_text: RefsText::Normalize,
            ..Default::default()
        },
        ..Default::default()
    };

    let config_content = toml::to_string(&config).expect("Failed to serialize config");
    write(temp_path.join(".iwe").join("config.toml"), config_content).expect("Should write config");
}

fn run_delete_command(work_dir: &std::path::Path, args: &[&str]) -> std::process::Output {
    let mut command = Command::new(crate::common::get_iwe_binary_path());
    command.arg("delete").current_dir(work_dir);

    for arg in args {
        command.arg(arg);
    }

    command.output().expect("Failed to execute iwe delete")
}