use std::fs::read_to_string;
use std::io::Write;
use std::process::{Command, Stdio};
use tempfile::TempDir;

use liwe::model::config::{Configuration, NoteTemplate};

fn setup_iwe_project() -> TempDir {
    let temp_dir = TempDir::new().expect("Failed to create temp directory");
    let temp_path = temp_dir.path();

    Command::new(crate::common::get_iwe_binary_path())
        .arg("init")
        .current_dir(temp_path)
        .output()
        .expect("Failed to initialize IWE");

    temp_dir
}

fn add_template(temp_dir: &TempDir, name: &str, template: NoteTemplate) {
    let config_path = temp_dir.path().join(".iwe").join("config.toml");
    let config_content = read_to_string(&config_path).expect("Read config");
    let mut config: Configuration = toml::from_str(&config_content).expect("Parse config");

    config.templates.insert(name.to_string(), template);

    let updated_config = toml::to_string(&config).expect("Serialize config");
    std::fs::write(&config_path, updated_config).expect("Write config");
}

#[test]
fn test_new_creates_file_with_default_template() {
    let temp_dir = setup_iwe_project();
    let temp_path = temp_dir.path();

    let output = Command::new(crate::common::get_iwe_binary_path())
        .arg("new")
        .arg("My Test Note")
        .current_dir(temp_path)
        .output()
        .expect("Failed to execute iwe new");

    assert!(output.status.success(), "Command should succeed");

    let stdout = String::from_utf8(output.stdout).expect("Valid UTF-8");
    let created_path = stdout.trim();

    assert!(
        std::path::Path::new(created_path).exists(),
        "Created file should exist"
    );

    let content = read_to_string(created_path).expect("Should read file");
    assert!(
        content.contains("# My Test Note"),
        "Should have title as header"
    );
}

#[test]
fn test_new_with_slug_in_filename() {
    let temp_dir = setup_iwe_project();
    let temp_path = temp_dir.path();

    let output = Command::new(crate::common::get_iwe_binary_path())
        .arg("new")
        .arg("Hello World Test")
        .current_dir(temp_path)
        .output()
        .expect("Failed to execute iwe new");

    assert!(output.status.success());

    let stdout = String::from_utf8(output.stdout).expect("Valid UTF-8");
    assert!(
        stdout.contains("hello-world-test.md"),
        "Filename should be slugified"
    );
}

#[test]
fn test_new_with_content_argument() {
    let temp_dir = setup_iwe_project();
    let temp_path = temp_dir.path();

    let output = Command::new(crate::common::get_iwe_binary_path())
        .arg("new")
        .arg("Note With Content")
        .arg("--content")
        .arg("This is the initial content")
        .current_dir(temp_path)
        .output()
        .expect("Failed to execute iwe new");

    assert!(output.status.success(), "Command should succeed");

    let stdout = String::from_utf8(output.stdout).expect("Valid UTF-8");
    let created_path = stdout.trim();
    let content = read_to_string(created_path).expect("Should read file");

    assert!(
        content.contains("# Note With Content"),
        "Should have title as header"
    );
    assert!(
        content.contains("This is the initial content"),
        "Should have content"
    );
}

#[test]
fn test_new_with_stdin_content() {
    let temp_dir = setup_iwe_project();
    let temp_path = temp_dir.path();

    let mut child = Command::new(crate::common::get_iwe_binary_path())
        .arg("new")
        .arg("Stdin Note")
        .stdin(Stdio::piped())
        .stdout(Stdio::piped())
        .current_dir(temp_path)
        .spawn()
        .expect("Failed to spawn command");

    if let Some(mut stdin) = child.stdin.take() {
        stdin
            .write_all(b"This is piped content")
            .expect("Failed to write stdin");
    }

    let output = child.wait_with_output().expect("Failed to wait");
    assert!(output.status.success());

    let stdout = String::from_utf8(output.stdout).expect("Valid UTF-8");
    let created_path = stdout.trim();
    let content = read_to_string(created_path).expect("Should read file");

    assert!(
        content.contains("This is piped content"),
        "File should contain stdin content"
    );
}

#[test]
fn test_new_default_appends_suffix_for_existing_file() {
    let temp_dir = setup_iwe_project();
    let temp_path = temp_dir.path();

    let first_output = Command::new(crate::common::get_iwe_binary_path())
        .arg("new")
        .arg("Duplicate Note")
        .current_dir(temp_path)
        .output()
        .expect("Failed to execute iwe new");

    assert!(first_output.status.success());
    let first_path = String::from_utf8(first_output.stdout).expect("Valid UTF-8");
    assert!(
        first_path.contains("duplicate-note.md"),
        "First file should be duplicate-note.md"
    );

    let second_output = Command::new(crate::common::get_iwe_binary_path())
        .arg("new")
        .arg("Duplicate Note")
        .current_dir(temp_path)
        .output()
        .expect("Failed to execute iwe new");

    assert!(
        second_output.status.success(),
        "Second creation should succeed with suffix"
    );
    let second_path = String::from_utf8(second_output.stdout).expect("Valid UTF-8");
    assert!(
        second_path.contains("duplicate-note-1.md"),
        "Second file should be duplicate-note-1.md, got: {}",
        second_path
    );
}

#[test]
fn test_new_creates_parent_directories() {
    let temp_dir = setup_iwe_project();
    let temp_path = temp_dir.path();

    add_template(
        &temp_dir,
        "nested",
        NoteTemplate {
            key_template: "notes/{{slug}}".to_string(),
            document_template: "# {{title}}\n".to_string(),
        },
    );

    let output = Command::new(crate::common::get_iwe_binary_path())
        .arg("new")
        .arg("Nested Note")
        .arg("--template")
        .arg("nested")
        .current_dir(temp_path)
        .output()
        .expect("Failed to execute iwe new");

    assert!(output.status.success(), "Command should succeed");

    let stdout = String::from_utf8(output.stdout).expect("Valid UTF-8");
    let created_path = stdout.trim();
    assert!(
        std::path::Path::new(created_path)
            .components()
            .any(|c| c.as_os_str() == "notes"),
        "Should create nested path"
    );

    assert!(
        std::path::Path::new(created_path).exists(),
        "File should exist in nested directory"
    );
}

#[test]
fn test_new_unknown_template_fails() {
    let temp_dir = setup_iwe_project();
    let temp_path = temp_dir.path();

    let output = Command::new(crate::common::get_iwe_binary_path())
        .arg("new")
        .arg("Test")
        .arg("--template")
        .arg("nonexistent")
        .current_dir(temp_path)
        .output()
        .expect("Failed to execute iwe new");

    assert!(!output.status.success(), "Should fail for unknown template");
}

#[test]
fn test_new_content_argument_takes_precedence_over_stdin() {
    let temp_dir = setup_iwe_project();
    let temp_path = temp_dir.path();

    let mut child = Command::new(crate::common::get_iwe_binary_path())
        .arg("new")
        .arg("Priority Test")
        .arg("--content")
        .arg("Content from argument")
        .stdin(Stdio::piped())
        .stdout(Stdio::piped())
        .current_dir(temp_path)
        .spawn()
        .expect("Failed to spawn command");

    if let Some(mut stdin) = child.stdin.take() {
        stdin
            .write_all(b"Content from stdin")
            .expect("Failed to write stdin");
    }

    let output = child.wait_with_output().expect("Failed to wait");
    assert!(output.status.success());

    let stdout = String::from_utf8(output.stdout).expect("Valid UTF-8");
    let created_path = stdout.trim();
    let content = read_to_string(created_path).expect("Should read file");

    assert!(
        content.contains("Content from argument"),
        "Should use content from argument"
    );
    assert!(
        !content.contains("Content from stdin"),
        "Should not use stdin content"
    );
}

#[test]
fn test_new_if_exists_suffix_appends_numbers() {
    let temp_dir = setup_iwe_project();
    let temp_path = temp_dir.path();

    let first_output = Command::new(crate::common::get_iwe_binary_path())
        .arg("new")
        .arg("Suffix Test")
        .arg("--if-exists")
        .arg("suffix")
        .current_dir(temp_path)
        .output()
        .expect("Failed to execute iwe new");

    assert!(first_output.status.success());
    let first_path = String::from_utf8(first_output.stdout).expect("Valid UTF-8");
    assert!(
        first_path.contains("suffix-test.md"),
        "First file should be suffix-test.md"
    );

    let second_output = Command::new(crate::common::get_iwe_binary_path())
        .arg("new")
        .arg("Suffix Test")
        .arg("--if-exists")
        .arg("suffix")
        .current_dir(temp_path)
        .output()
        .expect("Failed to execute iwe new");

    assert!(
        second_output.status.success(),
        "Second creation should succeed"
    );
    let second_path = String::from_utf8(second_output.stdout).expect("Valid UTF-8");
    assert!(
        second_path.contains("suffix-test-1.md"),
        "Second file should be suffix-test-1.md, got: {}",
        second_path
    );

    let third_output = Command::new(crate::common::get_iwe_binary_path())
        .arg("new")
        .arg("Suffix Test")
        .arg("--if-exists")
        .arg("suffix")
        .current_dir(temp_path)
        .output()
        .expect("Failed to execute iwe new");

    assert!(
        third_output.status.success(),
        "Third creation should succeed"
    );
    let third_path = String::from_utf8(third_output.stdout).expect("Valid UTF-8");
    assert!(
        third_path.contains("suffix-test-2.md"),
        "Third file should be suffix-test-2.md, got: {}",
        third_path
    );
}

#[test]
fn test_new_if_exists_override_replaces_file() {
    let temp_dir = setup_iwe_project();
    let temp_path = temp_dir.path();

    let first_output = Command::new(crate::common::get_iwe_binary_path())
        .arg("new")
        .arg("Override Test")
        .arg("--content")
        .arg("First content")
        .current_dir(temp_path)
        .output()
        .expect("Failed to execute iwe new");

    assert!(first_output.status.success());
    let first_path = String::from_utf8(first_output.stdout).expect("Valid UTF-8");
    let first_path = first_path.trim();
    let first_content = read_to_string(first_path).expect("Read first file");
    assert!(
        first_content.contains("First content"),
        "First file should have first content"
    );

    let second_output = Command::new(crate::common::get_iwe_binary_path())
        .arg("new")
        .arg("Override Test")
        .arg("--content")
        .arg("Second content")
        .arg("--if-exists")
        .arg("override")
        .current_dir(temp_path)
        .output()
        .expect("Failed to execute iwe new");

    assert!(second_output.status.success(), "Override should succeed");
    let second_path = String::from_utf8(second_output.stdout).expect("Valid UTF-8");
    assert!(
        second_path.contains("override-test.md"),
        "Should use same filename"
    );

    let overwritten_content = read_to_string(first_path).expect("Read overwritten file");
    assert!(
        overwritten_content.contains("Second content"),
        "File should have second content after override"
    );
    assert!(
        !overwritten_content.contains("First content"),
        "File should not have first content after override"
    );
}

#[test]
fn test_new_if_exists_skip_does_nothing() {
    let temp_dir = setup_iwe_project();
    let temp_path = temp_dir.path();

    let first_output = Command::new(crate::common::get_iwe_binary_path())
        .arg("new")
        .arg("Skip Test")
        .arg("--content")
        .arg("Original content")
        .current_dir(temp_path)
        .output()
        .expect("Failed to execute iwe new");

    assert!(first_output.status.success());
    let first_path = String::from_utf8(first_output.stdout).expect("Valid UTF-8");
    let first_path = first_path.trim();

    let second_output = Command::new(crate::common::get_iwe_binary_path())
        .arg("new")
        .arg("Skip Test")
        .arg("--content")
        .arg("New content")
        .arg("--if-exists")
        .arg("skip")
        .current_dir(temp_path)
        .output()
        .expect("Failed to execute iwe new");

    assert!(second_output.status.success(), "Skip should succeed");
    let second_stdout = String::from_utf8(second_output.stdout).expect("Valid UTF-8");
    assert!(
        second_stdout.trim().is_empty(),
        "Skip should print nothing, got: {}",
        second_stdout
    );

    let content = read_to_string(first_path).expect("Read file");
    assert!(
        content.contains("Original content"),
        "File should still have original content"
    );
    assert!(
        !content.contains("New content"),
        "File should not have new content"
    );
}

#[test]
fn test_new_with_german_locale_formats_date() {
    let temp_dir = setup_iwe_project();
    let temp_path = temp_dir.path();

    let config_path = temp_dir.path().join(".iwe").join("config.toml");
    let config_content = read_to_string(&config_path).expect("Read config");
    let mut config: Configuration = toml::from_str(&config_content).expect("Parse config");
    config.library.locale = Some("de_DE".to_string());
    config.markdown.locale = Some("de_DE".to_string());
    config.markdown.date_format = Some("%A, %d. %B %Y".to_string());
    let updated_config = toml::to_string(&config).expect("Serialize config");
    std::fs::write(&config_path, updated_config).expect("Write config");

    add_template(
        &temp_dir,
        "today",
        NoteTemplate {
            key_template: "journal/{{today}}".to_string(),
            document_template: "# {{today}}\n\n{{content}}".to_string(),
        },
    );

    let output = Command::new(crate::common::get_iwe_binary_path())
        .arg("new")
        .arg("Test")
        .arg("--template")
        .arg("today")
        .current_dir(temp_path)
        .output()
        .expect("Failed to execute iwe new");

    assert!(output.status.success(), "Command should succeed");

    let stdout = String::from_utf8(output.stdout).expect("Valid UTF-8");
    let created_path = stdout.trim();
    let content = read_to_string(created_path).expect("Should read file");

    let german_days = [
        "Montag",
        "Dienstag",
        "Mittwoch",
        "Donnerstag",
        "Freitag",
        "Samstag",
        "Sonntag",
    ];
    let has_german_day = german_days.iter().any(|day| content.contains(day));
    assert!(
        has_german_day,
        "Content should contain a German day name. Got: {}",
        content
    );
}

#[test]
fn test_new_long_title_error() {
    let temp_dir = setup_iwe_project();
    let temp_path = temp_dir.path();

    let long_title = "a".repeat(255);

    let output = Command::new(crate::common::get_iwe_binary_path())
        .arg("new")
        .arg(&long_title)
        .current_dir(temp_path)
        .output()
        .expect("Failed to execute iwe new");

    assert!(
        !output.status.success(),
        "Should fail for excessively long title"
    );

    let stderr = String::from_utf8(output.stderr).unwrap();
    assert!(
        stderr.contains("too long"),
        "Should report filename too long: {}",
        stderr
    );
}

#[test]
fn test_new_empty_title_error() {
    let temp_dir = setup_iwe_project();
    let temp_path = temp_dir.path();

    let output = Command::new(crate::common::get_iwe_binary_path())
        .arg("new")
        .arg("")
        .current_dir(temp_path)
        .output()
        .expect("Failed to execute iwe new");

    assert!(!output.status.success(), "Should fail for empty title");

    let stderr = String::from_utf8(output.stderr).unwrap();
    assert!(
        stderr.contains("empty"),
        "Should report empty key: {}",
        stderr
    );
}
