//! Mermaid Specification Compliance Integration Tests
//!
//! These tests validate generated Mermaid diagrams against the specification
//! using the Deno-based validator service.

use paiml_mcp_agent_toolkit::models::dag::{DependencyGraph, Edge, EdgeType, NodeInfo, NodeType};
use paiml_mcp_agent_toolkit::services::mermaid_generator::{MermaidGenerator, MermaidOptions};
use paiml_mcp_agent_toolkit::services::deterministic_mermaid_engine::DeterministicMermaidEngine;
use serde::{Deserialize, Serialize};
use std::fs;
use std::path::{Path, PathBuf};
use std::process::Command;
use tempfile::TempDir;

#[derive(Debug, Serialize, Deserialize)]
struct ValidationResult {
    valid: bool,
    diagram_type: Option<String>,
    nodes: Option<usize>,
    edges: Option<usize>,
    error: Option<ValidationError>,
    file: Option<String>,
}

#[derive(Debug, Serialize, Deserialize)]
struct ValidationError {
    message: String,
    line: Option<usize>,
    detail: String,
}

#[derive(Debug, Serialize, Deserialize)]
struct BatchValidationResult {
    total: usize,
    valid: usize,
    invalid: usize,
    results: Vec<ValidationResult>,
}

/// Integration test that validates generated Mermaid diagrams against the spec
#[cfg(test)]
mod mermaid_spec_integration_tests {
    use super::*;

    /// Validates a single diagram using the Deno validator
    fn validate_with_deno_validator(mmd_content: &str) -> Result<ValidationResult, String> {
        // Create a temporary file for the diagram
        let temp_dir = TempDir::new().map_err(|e| e.to_string())?;
        let mmd_path = temp_dir.path().join("test.mmd");
        fs::write(&mmd_path, mmd_content).map_err(|e| e.to_string())?;

        // Run the Deno validator
        let output = Command::new("deno")
            .args([
                "run",
                "--allow-read",
                "scripts/mermaid-validator.ts",
                mmd_path.to_str().unwrap(),
            ])
            .output()
            .map_err(|e| format!("Failed to run Deno validator: {}. Make sure Deno is installed.", e))?;

        if !output.status.success() {
            // Try to parse error output as JSON
            let stderr = String::from_utf8_lossy(&output.stderr);
            let stdout = String::from_utf8_lossy(&output.stdout);
            
            if let Ok(result) = serde_json::from_str::<ValidationResult>(&stdout) {
                return Ok(result);
            }
            
            return Err(format!(
                "Validator failed with status {}: stderr: {}, stdout: {}", 
                output.status, stderr, stdout
            ));
        }

        // Parse the JSON output
        let stdout = String::from_utf8_lossy(&output.stdout);
        serde_json::from_str(&stdout)
            .map_err(|e| format!("Failed to parse validator output: {}. Output: {}", e, stdout))
    }

    /// Check if Deno is available before running tests
    fn check_deno_available() -> bool {
        Command::new("deno")
            .args(["--version"])
            .output()
            .map(|output| output.status.success())
            .unwrap_or(false)
    }

    /// Test basic flowchart generation against spec
    #[test]
    fn test_flowchart_spec_compliance() {
        if !check_deno_available() {
            println!("Skipping Mermaid spec tests: Deno not available");
            return;
        }

        let mut graph = DependencyGraph::new();
        
        // Add nodes with various types
        graph.add_node("start", NodeInfo {
            label: "Start Process".to_string(),
            node_type: NodeType::Function,
            complexity: 1,
        });
        
        graph.add_node("decision", NodeInfo {
            label: "Is Valid?".to_string(),
            node_type: NodeType::Function,
            complexity: 3,
        });
        
        graph.add_node("process", NodeInfo {
            label: "Process Data".to_string(),
            node_type: NodeType::Function,
            complexity: 5,
        });
        
        graph.add_node("end", NodeInfo {
            label: "End".to_string(),
            node_type: NodeType::Function,
            complexity: 1,
        });

        // Add edges
        graph.add_edge(Edge {
            from: "start".to_string(),
            to: "decision".to_string(),
            edge_type: EdgeType::Import,
        });
        
        graph.add_edge(Edge {
            from: "decision".to_string(),
            to: "process".to_string(),
            edge_type: EdgeType::Call,
        });
        
        graph.add_edge(Edge {
            from: "decision".to_string(),
            to: "end".to_string(),
            edge_type: EdgeType::Call,
        });
        
        graph.add_edge(Edge {
            from: "process".to_string(),
            to: "end".to_string(),
            edge_type: EdgeType::Call,
        });

        let generator = MermaidGenerator::new();
        let mmd = generator.generate(&graph, &MermaidOptions::default());

        // Validate against spec
        let result = validate_with_deno_validator(&mmd).expect("Validation failed");
        
        assert!(result.valid, "Generated diagram should be valid: {:?}", result.error);
        assert_eq!(result.diagram_type.as_deref(), Some("flowchart"), "Should be detected as flowchart");
        
        // Node and edge counts should be reasonable
        if let Some(nodes) = result.nodes {
            assert!(nodes >= 4, "Should have at least 4 nodes, got {}", nodes);
        }
        if let Some(edges) = result.edges {
            assert!(edges >= 4, "Should have at least 4 edges, got {}", edges);
        }
    }

    /// Test edge cases and special characters
    #[test]
    fn test_special_characters_spec_compliance() {
        if !check_deno_available() {
            println!("Skipping Mermaid spec tests: Deno not available");
            return;
        }

        let test_cases = vec![
            ("node_with_quotes", r#"Node with quotes"#),
            ("node_with_apostrophe", "Node's label"),
            ("node_with_brackets", "Node with brackets"),
            ("node_with_parens", "Node with parens"),
            ("node_with_pipes", "Node with pipes"),
            ("node_with_unicode", "Node with émojis"),
        ];

        for (id, label) in test_cases {
            let mut graph = DependencyGraph::new();
            graph.add_node(id, NodeInfo {
                label: label.to_string(),
                node_type: NodeType::Module,
                complexity: 1,
            });

            let generator = MermaidGenerator::new();
            let mmd = generator.generate(&graph, &MermaidOptions::default());

            let result = validate_with_deno_validator(&mmd)
                .unwrap_or_else(|e| panic!("Validation failed for label '{}': {}", label, e));
            
            assert!(
                result.valid, 
                "Diagram with label '{}' should be valid: {:?}", 
                label, 
                result.error
            );
        }
    }

    /// Test deterministic engine compliance
    #[test]
    fn test_deterministic_engine_spec_compliance() {
        if !check_deno_available() {
            println!("Skipping Mermaid spec tests: Deno not available");
            return;
        }

        let engine = DeterministicMermaidEngine::new();
        let mut graph = petgraph::stable_graph::StableGraph::new();

        // Create test nodes
        let node1 = graph.add_node(paiml_mcp_agent_toolkit::services::unified_ast_engine::ModuleNode {
            name: "module_a".to_string(),
            path: PathBuf::from("a.rs"),
            visibility: "public".to_string(),
            metrics: paiml_mcp_agent_toolkit::services::unified_ast_engine::ModuleMetrics::default(),
        });

        let node2 = graph.add_node(paiml_mcp_agent_toolkit::services::unified_ast_engine::ModuleNode {
            name: "module_b".to_string(),
            path: PathBuf::from("b.rs"),
            visibility: "public".to_string(),
            metrics: paiml_mcp_agent_toolkit::services::unified_ast_engine::ModuleMetrics::default(),
        });

        graph.add_edge(node1, node2, paiml_mcp_agent_toolkit::models::dag::EdgeType::Imports);

        let mmd = engine.generate_codebase_modules_mmd(&graph);

        let result = validate_with_deno_validator(&mmd).expect("Validation failed");
        
        assert!(result.valid, "Deterministic engine output should be valid: {:?}", result.error);
        assert_eq!(result.diagram_type.as_deref(), Some("flowchart"));
    }

    /// Test all node types are spec-compliant
    #[test]
    fn test_all_node_types_spec_compliance() {
        if !check_deno_available() {
            println!("Skipping Mermaid spec tests: Deno not available");
            return;
        }

        let node_types = vec![
            (NodeType::Module, "module_node"),
            (NodeType::Function, "function_node"),
            (NodeType::Struct, "struct_node"),
            (NodeType::Enum, "enum_node"),
            (NodeType::Trait, "trait_node"),
            (NodeType::Impl, "impl_node"),
            (NodeType::Interface, "interface_node"),
            (NodeType::Class, "class_node"),
            (NodeType::Variable, "variable_node"),
            (NodeType::Import, "import_node"),
        ];

        for (node_type, id) in node_types {
            let mut graph = DependencyGraph::new();
            graph.add_node(id, NodeInfo {
                label: format!("{:?} Node", node_type),
                node_type,
                complexity: 1,
            });

            let generator = MermaidGenerator::new();
            let mmd = generator.generate(&graph, &MermaidOptions::default());

            let result = validate_with_deno_validator(&mmd)
                .unwrap_or_else(|e| panic!("Validation failed for node type {:?}: {}", node_type, e));
            
            assert!(
                result.valid, 
                "Diagram with node type {:?} should be valid: {:?}", 
                node_type, 
                result.error
            );
        }
    }

    /// Test complex graph structures
    #[test]
    fn test_complex_graph_spec_compliance() {
        if !check_deno_available() {
            println!("Skipping Mermaid spec tests: Deno not available");
            return;
        }

        let mut graph = DependencyGraph::new();
        
        // Create a more complex graph with cycles and multiple edge types
        for i in 0..10 {
            graph.add_node(&format!("node_{}", i), NodeInfo {
                label: format!("Node {}", i),
                node_type: if i % 2 == 0 { NodeType::Module } else { NodeType::Function },
                complexity: (i * 2 + 1) as u32,
            });
        }

        // Add various edge patterns
        for i in 0..9 {
            graph.add_edge(Edge {
                from: format!("node_{}", i),
                to: format!("node_{}", i + 1),
                edge_type: if i % 2 == 0 { EdgeType::Import } else { EdgeType::Call },
            });
        }

        // Add some back edges (cycles)
        graph.add_edge(Edge {
            from: "node_5".to_string(),
            to: "node_2".to_string(),
            edge_type: EdgeType::Call,
        });

        graph.add_edge(Edge {
            from: "node_9".to_string(),
            to: "node_0".to_string(),
            edge_type: EdgeType::Import,
        });

        let generator = MermaidGenerator::new();
        let options = MermaidOptions {
            direction: "TB".to_string(),
            show_complexity: true,
        };
        let mmd = generator.generate(&graph, &options);

        let result = validate_with_deno_validator(&mmd).expect("Validation failed");
        
        assert!(result.valid, "Complex diagram should be valid: {:?}", result.error);
        
        // Verify node and edge counts
        if let Some(nodes) = result.nodes {
            assert!(nodes >= 10, "Should have at least 10 nodes, got {}", nodes);
        }
        if let Some(edges) = result.edges {
            assert!(edges >= 11, "Should have at least 11 edges, got {}", edges);
        }
    }

    /// Test batch validation of generated artifacts
    #[test]
    fn test_validate_all_artifacts() {
        if !check_deno_available() {
            println!("Skipping Mermaid spec tests: Deno not available");
            return;
        }

        let artifacts_dir = PathBuf::from("artifacts/mermaid");
        if !artifacts_dir.exists() {
            println!("Skipping artifact validation: directory not found");
            return;
        }

        // Run batch validation using Deno
        let output = Command::new("deno")
            .args([
                "run",
                "--allow-read",
                "scripts/mermaid-validator.ts",
                artifacts_dir.to_str().unwrap(),
            ])
            .output()
            .expect("Failed to run batch validation");

        if !output.status.success() {
            let stderr = String::from_utf8_lossy(&output.stderr);
            let stdout = String::from_utf8_lossy(&output.stdout);
            panic!("Batch validation failed: stderr: {}, stdout: {}", stderr, stdout);
        }

        let stdout = String::from_utf8_lossy(&output.stdout);
        let batch_result: BatchValidationResult = serde_json::from_str(&stdout)
            .expect("Failed to parse batch validation results");

        // All artifacts should be valid
        assert_eq!(
            batch_result.invalid, 0,
            "All generated artifacts should be valid. Invalid files: {:?}",
            batch_result.results
                .iter()
                .filter(|r| !r.valid)
                .collect::<Vec<_>>()
        );

        println!(
            "✅ Validated {} Mermaid artifacts ({} valid, {} invalid)",
            batch_result.total, batch_result.valid, batch_result.invalid
        );
    }

    /// Test error detection and reporting
    #[test]
    fn test_invalid_diagram_detection() {
        if !check_deno_available() {
            println!("Skipping Mermaid spec tests: Deno not available");
            return;
        }

        let invalid_diagrams = vec![
            ("Missing arrow", "graph TD\n  A B"),
            ("Invalid node ID", "graph TD\n  1node --> B"),
            ("Unclosed quote", r#"graph TD\n  A["Label] --> B"#),
            ("Invalid direction", "graph XY\n  A --> B"),
            ("Empty content", ""),
        ];

        for (description, mmd) in invalid_diagrams {
            let result = validate_with_deno_validator(mmd).expect("Validation call failed");
            
            assert!(
                !result.valid,
                "Diagram '{}' should be invalid but was marked as valid",
                description
            );
            
            assert!(
                result.error.is_some(),
                "Invalid diagram '{}' should have error details",
                description
            );
            
            println!("✅ Correctly detected invalid diagram: {}", description);
        }
    }

    /// Test ID sanitization compliance
    #[test]
    fn test_id_sanitization_compliance() {
        if !check_deno_available() {
            println!("Skipping Mermaid spec tests: Deno not available");
            return;
        }

        let engine = DeterministicMermaidEngine::new();
        
        let test_cases = vec![
            ("foo::bar", "foo_bar"),
            ("foo/bar.rs", "foo_bar_rs"),
            ("foo-bar", "foo_bar"),
            ("foo bar", "foo_bar"),
            ("123foo", "_123foo"),
            ("_foo", "_foo"),
            ("", "_empty"),
        ];

        for (input, expected) in test_cases {
            let sanitized = engine.sanitize_id(input);
            assert_eq!(sanitized, expected, "ID sanitization failed for '{}'", input);
            
            // Test that sanitized ID produces valid Mermaid
            let mmd = format!("graph TD\n  {} --> test", sanitized);
            let result = validate_with_deno_validator(&mmd).expect("Validation failed");
            assert!(result.valid, "Sanitized ID '{}' should produce valid Mermaid", sanitized);
        }
    }
}

/// Performance test for validation overhead
#[test]
#[ignore] // Run with --ignored flag
fn test_validation_performance() {
    if !Command::new("deno").args(["--version"]).output()
        .map(|output| output.status.success()).unwrap_or(false) {
        println!("Skipping performance test: Deno not available");
        return;
    }

    use std::time::Instant;

    let mut total_generation_time = std::time::Duration::ZERO;
    let mut total_validation_time = std::time::Duration::ZERO;
    let iterations = 50; // Reduced for faster testing

    for i in 0..iterations {
        let mut graph = DependencyGraph::new();
        
        // Create a graph of varying size
        let node_count = 5 + (i % 10);
        for j in 0..node_count {
            graph.add_node(&format!("node_{}", j), NodeInfo {
                label: format!("Node {}", j),
                node_type: NodeType::Function,
                complexity: j as u32,
            });
        }

        // Add edges
        for j in 0..node_count - 1 {
            graph.add_edge(Edge {
                from: format!("node_{}", j),
                to: format!("node_{}", j + 1),
                edge_type: EdgeType::Call,
            });
        }

        // Time generation
        let gen_start = Instant::now();
        let generator = MermaidGenerator::new();
        let mmd = generator.generate(&graph, &MermaidOptions::default());
        total_generation_time += gen_start.elapsed();

        // Time validation
        let val_start = Instant::now();
        let _ = validate_with_deno_validator(&mmd);
        total_validation_time += val_start.elapsed();
    }

    let avg_generation = total_generation_time / iterations;
    let avg_validation = total_validation_time / iterations;

    println!("Performance Results ({} iterations):", iterations);
    println!("  Average generation time: {:?}", avg_generation);
    println!("  Average validation time: {:?}", avg_validation);
    println!("  Validation overhead: {:.2}%", 
        (avg_validation.as_micros() as f64 / avg_generation.as_micros() as f64) * 100.0);
    
    // Ensure validation doesn't add excessive overhead (allow more time for Deno startup)
    assert!(
        avg_validation < avg_generation * 50, // More lenient than the original spec
        "Validation overhead too high: {:?} vs {:?}",
        avg_validation,
        avg_generation
    );
}