//! Integration tests for Lua-based macros.
//!
//! These tests verify that Lua macros work identically to YAML macros
//! and test Lua-specific features.

use assert_cmd::prelude::*;
use predicates::prelude::*;
use std::fs;
use std::process::Command;
use tempfile::tempdir;

fn write(dir: &std::path::Path, rel: &str, content: impl AsRef<str>) {
    let path = dir.join(rel);
    if let Some(parent) = path.parent() {
        fs::create_dir_all(parent).unwrap();
    }
    fs::write(path, content.as_ref()).unwrap();
}

fn make_config(vault_root: &str) -> String {
    format!(
        r#"
version = 1
profile = "test"

[profiles.test]
vault_root = "{vault_root}"
templates_dir = "{{{{vault_root}}}}/templates"
captures_dir = "{{{{vault_root}}}}/captures"
macros_dir = "{{{{vault_root}}}}/macros"

[security]
allow_shell = false
"#
    )
}

#[test]
fn lua_macro_list_shows_available() {
    let tmp = tempdir().unwrap();
    let root = tmp.path();
    let vault = root.join("vault");

    write(root, "config.toml", make_config(&vault.to_string_lossy()));

    write(
        root,
        "vault/macros/weekly-review.lua",
        r#"
return {
    name = "weekly-review",
    description = "Set up weekly review documents",
    steps = {
        { template = "summary" },
    },
}
"#,
    );

    write(
        root,
        "vault/macros/daily-setup.lua",
        r#"
return {
    name = "daily-setup",
    description = "Create daily note",
    steps = {
        { template = "daily" },
        { capture = "inbox-clear" },
    },
}
"#,
    );

    fs::create_dir_all(vault.join("templates")).unwrap();
    fs::create_dir_all(vault.join("captures")).unwrap();

    let mut cmd = Command::new(assert_cmd::cargo::cargo_bin!("mdv"));
    cmd.arg("--config").arg(root.join("config.toml")).arg("macro").arg("--list");

    cmd.assert()
        .success()
        .stdout(predicate::str::contains("weekly-review"))
        .stdout(predicate::str::contains("Set up weekly review"))
        .stdout(predicate::str::contains("daily-setup"))
        .stdout(predicate::str::contains("2 steps"))
        .stdout(predicate::str::contains("-- 2 macros --"));
}

#[test]
fn lua_macro_runs_template_step() {
    let tmp = tempdir().unwrap();
    let root = tmp.path();
    let vault = root.join("vault");

    write(root, "config.toml", make_config(&vault.to_string_lossy()));

    write(
        root,
        "vault/templates/note.md",
        r#"---
output: "notes/{{title}}.md"
---
# {{title}}

Created on {{date}}
"#,
    );

    write(
        root,
        "vault/macros/create-note.lua",
        r#"
return {
    name = "create-note",
    description = "Create a note",
    vars = {
        title = "Note title",
    },
    steps = {
        { type = "template", template = "note" },
    },
}
"#,
    );

    fs::create_dir_all(vault.join("captures")).unwrap();

    let mut cmd = Command::new(assert_cmd::cargo::cargo_bin!("mdv"));
    cmd.arg("--config")
        .arg(root.join("config.toml"))
        .arg("macro")
        .arg("create-note")
        .arg("--var")
        .arg("title=My Lua Note")
        .arg("--batch");

    cmd.assert()
        .success()
        .stdout(predicate::str::contains("OK   mdv macro"))
        .stdout(predicate::str::contains("1 completed"));

    let output_file = vault.join("notes/My Lua Note.md");
    assert!(output_file.exists(), "Output file should be created");

    let content = fs::read_to_string(&output_file).unwrap();
    assert!(content.contains("# My Lua Note"));
}

#[test]
fn lua_macro_runs_capture_step() {
    let tmp = tempdir().unwrap();
    let root = tmp.path();
    let vault = root.join("vault");

    write(root, "config.toml", make_config(&vault.to_string_lossy()));

    write(
        root,
        "vault/inbox.md",
        r#"# Inbox

## Items

- Existing item

## Archive
"#,
    );

    write(
        root,
        "vault/captures/add-item.lua",
        r#"
return {
    name = "add-item",
    target = {
        file = "inbox.md",
        section = "Items",
        position = "end",
    },
    content = "- {{text}}",
}
"#,
    );

    write(
        root,
        "vault/macros/quick-add.lua",
        r#"
return {
    name = "quick-add",
    description = "Add item to inbox",
    vars = {
        text = "Item text",
    },
    steps = {
        { type = "capture", capture = "add-item" },
    },
}
"#,
    );

    fs::create_dir_all(vault.join("templates")).unwrap();

    let mut cmd = Command::new(assert_cmd::cargo::cargo_bin!("mdv"));
    cmd.arg("--config")
        .arg(root.join("config.toml"))
        .arg("macro")
        .arg("quick-add")
        .arg("--var")
        .arg("text=New Lua macro item")
        .arg("--batch");

    cmd.assert().success().stdout(predicate::str::contains("OK   mdv macro"));

    let content = fs::read_to_string(vault.join("inbox.md")).unwrap();
    assert!(content.contains("- New Lua macro item"));
    assert!(content.contains("- Existing item"));
}

#[test]
fn lua_macro_runs_multiple_steps() {
    let tmp = tempdir().unwrap();
    let root = tmp.path();
    let vault = root.join("vault");

    write(root, "config.toml", make_config(&vault.to_string_lossy()));

    write(
        root,
        "vault/templates/meeting.md",
        r#"---
output: "meetings/{{topic}}.md"
---
# Meeting: {{topic}}

## Notes
"#,
    );

    write(
        root,
        "vault/log.md",
        r#"# Meeting Log

## Recent

"#,
    );

    write(
        root,
        "vault/captures/log-meeting.lua",
        r#"
return {
    name = "log-meeting",
    target = {
        file = "log.md",
        section = "Recent",
        position = "begin",
    },
    content = "- [[meetings/{{topic}}]] - {{date}}",
}
"#,
    );

    write(
        root,
        "vault/macros/new-meeting.lua",
        r#"
return {
    name = "new-meeting",
    description = "Create meeting note and log it",
    vars = {
        topic = "Meeting topic",
    },
    steps = {
        { template = "meeting" },
        { capture = "log-meeting" },
    },
}
"#,
    );

    let mut cmd = Command::new(assert_cmd::cargo::cargo_bin!("mdv"));
    cmd.arg("--config")
        .arg(root.join("config.toml"))
        .arg("macro")
        .arg("new-meeting")
        .arg("--var")
        .arg("topic=Lua Weekly Sync")
        .arg("--batch");

    cmd.assert().success().stdout(predicate::str::contains("2 completed"));

    let meeting_file = vault.join("meetings/Lua Weekly Sync.md");
    assert!(meeting_file.exists());
    let meeting_content = fs::read_to_string(&meeting_file).unwrap();
    assert!(meeting_content.contains("# Meeting: Lua Weekly Sync"));

    let log_content = fs::read_to_string(vault.join("log.md")).unwrap();
    assert!(log_content.contains("[[meetings/Lua Weekly Sync]]"));
}

#[test]
fn lua_macro_with_step_vars_override() {
    let tmp = tempdir().unwrap();
    let root = tmp.path();
    let vault = root.join("vault");

    write(root, "config.toml", make_config(&vault.to_string_lossy()));

    write(
        root,
        "vault/templates/note.md",
        r#"---
output: "notes/{{filename}}.md"
---
# {{title}}
"#,
    );

    write(
        root,
        "vault/macros/fixed-output.lua",
        r#"
return {
    name = "fixed-output",
    description = "Create note with fixed filename",
    vars = {
        title = "Note title",
    },
    steps = {
        {
            type = "template",
            template = "note",
            with = {
                filename = "lua-fixed-name",
            },
        },
    },
}
"#,
    );

    fs::create_dir_all(vault.join("captures")).unwrap();

    let mut cmd = Command::new(assert_cmd::cargo::cargo_bin!("mdv"));
    cmd.arg("--config")
        .arg(root.join("config.toml"))
        .arg("macro")
        .arg("fixed-output")
        .arg("--var")
        .arg("title=Dynamic Lua Title")
        .arg("--batch");

    cmd.assert().success();

    let output_file = vault.join("notes/lua-fixed-name.md");
    assert!(output_file.exists());

    let content = fs::read_to_string(&output_file).unwrap();
    assert!(content.contains("# Dynamic Lua Title"));
}

#[test]
fn lua_macro_with_shell_requires_trust() {
    let tmp = tempdir().unwrap();
    let root = tmp.path();
    let vault = root.join("vault");

    write(root, "config.toml", make_config(&vault.to_string_lossy()));

    write(
        root,
        "vault/macros/deploy.lua",
        r#"
return {
    name = "deploy",
    description = "Deploy with git",
    steps = {
        { type = "shell", shell = "git add .", description = "Stage changes" },
        { type = "shell", shell = "git commit -m 'deploy'", description = "Commit" },
    },
}
"#,
    );

    fs::create_dir_all(vault.join("templates")).unwrap();
    fs::create_dir_all(vault.join("captures")).unwrap();

    let mut cmd = Command::new(assert_cmd::cargo::cargo_bin!("mdv"));
    cmd.arg("--config").arg(root.join("config.toml")).arg("macro").arg("deploy");

    cmd.assert()
        .failure()
        .stderr(predicate::str::contains("--trust"))
        .stderr(predicate::str::contains("git add"));
}

#[test]
fn lua_macro_precedence_over_yaml() {
    let tmp = tempdir().unwrap();
    let root = tmp.path();
    let vault = root.join("vault");

    write(root, "config.toml", make_config(&vault.to_string_lossy()));

    // Create both YAML and Lua with same name
    write(
        root,
        "vault/macros/test.yaml",
        r#"
name: test
description: YAML version
steps:
  - template: yaml-template
"#,
    );

    write(
        root,
        "vault/macros/test.lua",
        r#"
return {
    name = "test",
    description = "Lua version",
    steps = {
        { template = "lua-template" },
    },
}
"#,
    );

    fs::create_dir_all(vault.join("templates")).unwrap();
    fs::create_dir_all(vault.join("captures")).unwrap();

    let mut cmd = Command::new(assert_cmd::cargo::cargo_bin!("mdv"));
    cmd.arg("--config").arg(root.join("config.toml")).arg("macro").arg("--list");

    cmd.assert().success().stdout(predicate::str::contains("Lua version")); // Lua should win
}

#[test]
fn lua_macro_with_on_error_continue() {
    let tmp = tempdir().unwrap();
    let root = tmp.path();
    let vault = root.join("vault");

    write(root, "config.toml", make_config(&vault.to_string_lossy()));

    // First template exists
    write(
        root,
        "vault/templates/first.md",
        r#"---
output: "first.md"
---
# First
"#,
    );

    // Third template exists
    write(
        root,
        "vault/templates/third.md",
        r#"---
output: "third.md"
---
# Third
"#,
    );

    // Second template does NOT exist - will fail
    write(
        root,
        "vault/macros/continue-test.lua",
        r#"
return {
    name = "continue-test",
    description = "Test on_error = continue",
    on_error = "continue",
    steps = {
        { template = "first" },
        { template = "second" },  -- This will fail
        { template = "third" },
    },
}
"#,
    );

    fs::create_dir_all(vault.join("captures")).unwrap();

    let mut cmd = Command::new(assert_cmd::cargo::cargo_bin!("mdv"));
    cmd.arg("--config")
        .arg(root.join("config.toml"))
        .arg("macro")
        .arg("continue-test")
        .arg("--batch");

    cmd.assert()
        .failure() // Overall fails because of step 2
        .stderr(predicate::str::contains("[OK] Step 1"))
        .stderr(predicate::str::contains("[FAIL] Step 2"))
        .stderr(predicate::str::contains("[OK] Step 3"));

    // First and third should still be created
    assert!(vault.join("first.md").exists());
    assert!(vault.join("third.md").exists());
}

#[test]
fn lua_macro_with_full_vars_metadata() {
    let tmp = tempdir().unwrap();
    let root = tmp.path();
    let vault = root.join("vault");

    write(root, "config.toml", make_config(&vault.to_string_lossy()));

    write(
        root,
        "vault/templates/note.md",
        r#"---
output: "notes/{{title}}.md"
---
# {{title}}

Priority: {{priority}}
"#,
    );

    write(
        root,
        "vault/macros/detailed.lua",
        r#"
return {
    name = "detailed",
    description = "Macro with full vars",
    vars = {
        title = {
            prompt = "Note title?",
            required = true,
        },
        priority = {
            prompt = "Priority?",
            default = "medium",
        },
    },
    steps = {
        { template = "note" },
    },
}
"#,
    );

    fs::create_dir_all(vault.join("captures")).unwrap();

    let mut cmd = Command::new(assert_cmd::cargo::cargo_bin!("mdv"));
    cmd.arg("--config")
        .arg(root.join("config.toml"))
        .arg("macro")
        .arg("detailed")
        .arg("--var")
        .arg("title=Full Vars Test")
        .arg("--var")
        .arg("priority=high")
        .arg("--batch");

    cmd.assert().success();

    let content = fs::read_to_string(vault.join("notes/Full Vars Test.md")).unwrap();
    assert!(content.contains("# Full Vars Test"));
    assert!(content.contains("Priority: high"));
}
