//! Integration tests for complete MDX → Nickel → AgentDefinition → Execution pipeline

use std::collections::HashMap;
use typedialog_ag_core::executor::AgentExecutor;
use typedialog_ag_core::nickel::NickelEvaluator;
use typedialog_ag_core::parser::MarkupParser;
use typedialog_ag_core::transpiler::NickelTranspiler;

#[test]
#[ignore = "NickelTranspiler bug: must_contain arrays not being parsed from @validate directives"]
fn test_architect_agent_pipeline() {
    // Read MDX file
    let mdx_content = std::fs::read_to_string("tests/fixtures/architect.agent.mdx")
        .expect("Failed to read architect.agent.mdx");

    // Step 1: Parse MDX to AST
    let parser = MarkupParser::new();
    let ast = parser.parse(&mdx_content).expect("Failed to parse MDX");

    // Verify AST contains expected nodes
    assert!(ast
        .nodes
        .iter()
        .any(|n| matches!(n, typedialog_ag_core::parser::ast::MarkupNode::Agent(_))));
    assert!(ast
        .nodes
        .iter()
        .any(|n| matches!(n, typedialog_ag_core::parser::ast::MarkupNode::Input(_))));
    assert!(ast
        .nodes
        .iter()
        .any(|n| matches!(n, typedialog_ag_core::parser::ast::MarkupNode::Validate(_))));

    // Step 2: Transpile AST to Nickel
    let transpiler = NickelTranspiler::new();
    let nickel_code = transpiler
        .transpile(&ast)
        .expect("Failed to transpile to Nickel");

    // Verify Nickel code contains expected sections
    assert!(nickel_code.contains("config = {"));
    assert!(nickel_code.contains("role = \"architect\","));
    assert!(nickel_code.contains("llm = \"claude-opus-4\","));
    assert!(nickel_code.contains("inputs = {},"));
    assert!(nickel_code.contains("validation = {"));
    assert!(nickel_code.contains("must_contain = ["));

    println!("Generated Nickel code:\n{}", nickel_code);

    // Step 3: Evaluate Nickel to AgentDefinition
    let evaluator = NickelEvaluator::new();
    let agent_def = evaluator
        .evaluate(&nickel_code)
        .expect("Failed to evaluate Nickel");

    // Verify AgentDefinition
    assert_eq!(agent_def.config.role, "architect");
    assert_eq!(agent_def.config.llm, "claude-opus-4");
    assert_eq!(
        agent_def.config.tools,
        vec!["analyze_codebase", "suggest_architecture"]
    );

    assert!(agent_def.validation.is_some());
    let validation = agent_def.validation.as_ref().unwrap();
    assert!(validation
        .must_contain
        .contains(&"## Architecture".to_string()));
    assert!(validation
        .must_contain
        .contains(&"## Components".to_string()));
    assert_eq!(validation.format, "markdown");

    println!("AgentDefinition: {:#?}", agent_def);
}

#[tokio::test]
#[ignore = "Requires ANTHROPIC_API_KEY environment variable"]
async fn test_architect_agent_execution() {
    // Read and parse MDX
    let mdx_content = std::fs::read_to_string("tests/fixtures/architect.agent.mdx")
        .expect("Failed to read architect.agent.mdx");

    let parser = MarkupParser::new();
    let ast = parser.parse(&mdx_content).expect("Failed to parse MDX");

    let transpiler = NickelTranspiler::new();
    let nickel_code = transpiler.transpile(&ast).expect("Failed to transpile");

    let evaluator = NickelEvaluator::new();
    let agent_def = evaluator
        .evaluate(&nickel_code)
        .expect("Failed to evaluate");

    // Step 4: Execute agent
    let executor = AgentExecutor::new();
    let mut inputs = HashMap::new();
    inputs.insert(
        "feature_name".to_string(),
        serde_json::json!("User Authentication"),
    );
    inputs.insert(
        "requirements".to_string(),
        serde_json::json!("OAuth2, JWT tokens, secure password hashing"),
    );

    let result = executor
        .execute(&agent_def, inputs)
        .await
        .expect("Failed to execute agent");

    // Verify execution result
    assert!(result.output.contains("User Authentication"));
    assert!(result.output.contains("OAuth2"));
    assert!(result.metadata.duration_ms.is_some());
    assert_eq!(result.metadata.model, Some("claude-opus-4".to_string()));

    println!("Execution result:\n{}", result.output);
    println!("Duration: {:?}ms", result.metadata.duration_ms);
}

#[test]
#[ignore = "NickelTranspiler bug: must_contain arrays not being parsed from @validate directives"]
fn test_code_reviewer_agent_pipeline() {
    // Read MDX file
    let mdx_content = std::fs::read_to_string("tests/fixtures/code-reviewer.agent.mdx")
        .expect("Failed to read code-reviewer.agent.mdx");

    // Parse MDX to AST
    let parser = MarkupParser::new();
    let ast = parser.parse(&mdx_content).expect("Failed to parse MDX");

    // Verify AST contains shell directive
    assert!(ast
        .nodes
        .iter()
        .any(|n| matches!(n, typedialog_ag_core::parser::ast::MarkupNode::Shell(_))));

    // Transpile to Nickel
    let transpiler = NickelTranspiler::new();
    let nickel_code = transpiler.transpile(&ast).expect("Failed to transpile");

    // Verify context section
    assert!(nickel_code.contains("context = {"));
    assert!(nickel_code.contains("shell_commands = ["));
    assert!(nickel_code.contains("git diff HEAD~1"));

    println!("Generated Nickel code:\n{}", nickel_code);

    // Evaluate Nickel
    let evaluator = NickelEvaluator::new();
    let agent_def = evaluator
        .evaluate(&nickel_code)
        .expect("Failed to evaluate");

    // Verify context sources
    assert!(agent_def.context.is_some());
    let context = agent_def.context.as_ref().unwrap();
    assert_eq!(context.shell_commands.len(), 1);
    assert_eq!(context.shell_commands[0], "git diff HEAD~1");

    // Verify validation
    assert!(agent_def.validation.is_some());
    let validation = agent_def.validation.as_ref().unwrap();
    assert!(validation.must_contain.contains(&"Security".to_string()));
    assert!(validation.must_contain.contains(&"Performance".to_string()));
    assert!(validation
        .must_contain
        .contains(&"Maintainability".to_string()));
    assert_eq!(validation.min_length, Some(100));

    println!("AgentDefinition: {:#?}", agent_def);
}

#[tokio::test]
#[ignore = "Requires ANTHROPIC_API_KEY environment variable"]
async fn test_validation_failure() {
    // Create a simple agent with strict validation
    let mdx_content = r#"---
@agent {
  role: tester,
  llm: claude
}

@validate output {
  must_contain: ["PASS", "SUCCESS"],
  format: json
}
---

Test result: {{ result }}
"#;

    let parser = MarkupParser::new();
    let ast = parser.parse(mdx_content).expect("Failed to parse");

    let transpiler = NickelTranspiler::new();
    let nickel_code = transpiler.transpile(&ast).expect("Failed to transpile");

    let evaluator = NickelEvaluator::new();
    let agent_def = evaluator
        .evaluate(&nickel_code)
        .expect("Failed to evaluate");

    let executor = AgentExecutor::new();
    let mut inputs = HashMap::new();
    inputs.insert("result".to_string(), serde_json::json!("FAIL"));

    let result = executor
        .execute(&agent_def, inputs)
        .await
        .expect("Failed to execute");

    // Validation should fail - output won't contain required patterns and won't be valid JSON
    assert!(!result.validation_passed);
    assert!(!result.validation_errors.is_empty());

    println!("Validation errors: {:?}", result.validation_errors);
}

#[test]
fn test_nickel_typecheck() {
    let mdx_content = r#"---
@agent {
  role: assistant,
  llm: claude
}
---

Hello world
"#;

    let parser = MarkupParser::new();
    let ast = parser.parse(mdx_content).expect("Failed to parse");

    let transpiler = NickelTranspiler::new();
    let nickel_code = transpiler.transpile(&ast).expect("Failed to transpile");

    // Type check should pass
    let evaluator = NickelEvaluator::new();
    let result = evaluator.typecheck(&nickel_code);
    assert!(result.is_ok(), "Typecheck failed: {:?}", result.err());
}

#[tokio::test]
#[ignore = "Requires ANTHROPIC_API_KEY environment variable"]
async fn test_template_with_conditional() {
    let mdx_content = r#"---
@agent {
  role: assistant,
  llm: claude
}

@input name: String
@input title?: String
---

Hello{% if title %} {{ title }}{% endif %} {{ name }}!
"#;

    let parser = MarkupParser::new();
    let ast = parser.parse(mdx_content).expect("Failed to parse");

    let transpiler = NickelTranspiler::new();
    let nickel_code = transpiler.transpile(&ast).expect("Failed to transpile");

    let evaluator = NickelEvaluator::new();
    let agent_def = evaluator
        .evaluate(&nickel_code)
        .expect("Failed to evaluate");

    let executor = AgentExecutor::new();

    // Test with title
    let mut inputs = HashMap::new();
    inputs.insert("name".to_string(), serde_json::json!("Alice"));
    inputs.insert("title".to_string(), serde_json::json!("Dr."));

    let result = executor
        .execute(&agent_def, inputs)
        .await
        .expect("Failed to execute");
    assert!(result.output.contains("Dr. Alice"));

    // Test without title
    let mut inputs = HashMap::new();
    inputs.insert("name".to_string(), serde_json::json!("Bob"));

    let result = executor
        .execute(&agent_def, inputs)
        .await
        .expect("Failed to execute");
    assert!(result.output.contains("Hello Bob"));
    assert!(!result.output.contains("Dr."));
}
