// Integration tests for ValidationPipeline
// Demonstrates end-to-end schema validation workflow

use std::path::PathBuf;
use std::sync::Arc;

use serde_json::json;
use vapora_shared::validation::{
    CompiledSchema, Contract, FieldSchema, FieldType, SchemaRegistry, SchemaSource,
    ValidationPipeline,
};

/// Create a test schema for MCP tool validation
fn create_mcp_tool_schema() -> CompiledSchema {
    CompiledSchema {
        name: "tools/kanban_create_task".to_string(),
        fields: vec![
            FieldSchema {
                name: "project_id".to_string(),
                field_type: FieldType::String,
                required: true,
                contracts: vec![Contract::NonEmpty, Contract::Uuid],
                default: None,
                doc: Some("Project UUID".to_string()),
            },
            FieldSchema {
                name: "title".to_string(),
                field_type: FieldType::String,
                required: true,
                contracts: vec![
                    Contract::NonEmpty,
                    Contract::MinLength(3),
                    Contract::MaxLength(200),
                ],
                default: None,
                doc: Some("Task title (3-200 chars)".to_string()),
            },
            FieldSchema {
                name: "description".to_string(),
                field_type: FieldType::String,
                required: false,
                contracts: vec![],
                default: Some(json!("")),
                doc: Some("Task description".to_string()),
            },
            FieldSchema {
                name: "priority".to_string(),
                field_type: FieldType::String,
                required: true,
                contracts: vec![],
                default: None,
                doc: Some("Task priority".to_string()),
            },
        ],
        custom_contracts: vec![],
        source_path: PathBuf::from("schemas/tools/kanban_create_task.ncl"),
    }
}

/// Create a test schema for agent task assignment
fn create_agent_assignment_schema() -> CompiledSchema {
    CompiledSchema {
        name: "agents/task_assignment".to_string(),
        fields: vec![
            FieldSchema {
                name: "role".to_string(),
                field_type: FieldType::Enum(vec![
                    "developer".to_string(),
                    "reviewer".to_string(),
                    "architect".to_string(),
                    "tester".to_string(),
                ]),
                required: true,
                contracts: vec![],
                default: None,
                doc: Some("Agent role".to_string()),
            },
            FieldSchema {
                name: "title".to_string(),
                field_type: FieldType::String,
                required: true,
                contracts: vec![Contract::NonEmpty, Contract::MaxLength(500)],
                default: None,
                doc: Some("Task title".to_string()),
            },
            FieldSchema {
                name: "priority".to_string(),
                field_type: FieldType::Number,
                required: false,
                contracts: vec![Contract::Range {
                    min: 0.0,
                    max: 100.0,
                }],
                default: Some(json!(50)),
                doc: Some("Priority score (0-100)".to_string()),
            },
        ],
        custom_contracts: vec![],
        source_path: PathBuf::from("schemas/agents/task_assignment.ncl"),
    }
}

#[tokio::test]
async fn test_mcp_tool_valid_input() {
    let registry = Arc::new(SchemaRegistry::new(PathBuf::from("schemas")));

    // Load pre-compiled schema
    let schema = create_mcp_tool_schema();
    let source = SchemaSource::Compiled(schema);
    let loaded_schema = registry
        .load_from_source(source, "tools/kanban_create_task")
        .await
        .unwrap();

    let pipeline = ValidationPipeline::new(registry);

    let input = json!({
        "project_id": "550e8400-e29b-41d4-a716-446655440000",
        "title": "Implement validation pipeline",
        "priority": "high"
    });

    let result = pipeline
        .validate_against_schema(&loaded_schema, &input)
        .unwrap();

    assert!(result.valid, "Valid input should pass validation");
    assert!(result.errors.is_empty());
    assert!(result.validated_data.is_some());

    // Check defaults applied
    let validated = result.validated_data.unwrap();
    assert_eq!(validated["description"], "");
}

#[tokio::test]
async fn test_mcp_tool_missing_required_field() {
    let registry = Arc::new(SchemaRegistry::new(PathBuf::from("schemas")));
    let schema = create_mcp_tool_schema();
    let pipeline = ValidationPipeline::new(registry);

    let input = json!({
        "title": "Test Task",
        // Missing project_id (required)
        "priority": "high"
    });

    let result = pipeline.validate_against_schema(&schema, &input).unwrap();

    assert!(!result.valid);
    assert!(result.has_field_error("project_id"));
    assert!(result.error_messages()[0].contains("Required field missing"));
}

#[tokio::test]
async fn test_mcp_tool_invalid_uuid() {
    let registry = Arc::new(SchemaRegistry::new(PathBuf::from("schemas")));
    let schema = create_mcp_tool_schema();
    let pipeline = ValidationPipeline::new(registry);

    let input = json!({
        "project_id": "not-a-valid-uuid",
        "title": "Test Task",
        "priority": "high"
    });

    let result = pipeline.validate_against_schema(&schema, &input).unwrap();

    assert!(!result.valid);
    assert!(result.has_field_error("project_id"));

    let error_msg = result.error_messages()[0].to_lowercase();
    assert!(error_msg.contains("uuid") || error_msg.contains("format"));
}

#[tokio::test]
async fn test_mcp_tool_title_too_short() {
    let registry = Arc::new(SchemaRegistry::new(PathBuf::from("schemas")));
    let schema = create_mcp_tool_schema();
    let pipeline = ValidationPipeline::new(registry);

    let input = json!({
        "project_id": "550e8400-e29b-41d4-a716-446655440000",
        "title": "AB", // Only 2 chars, min is 3
        "priority": "high"
    });

    let result = pipeline.validate_against_schema(&schema, &input).unwrap();

    assert!(!result.valid);
    assert!(result.has_field_error("title"));
}

#[tokio::test]
async fn test_mcp_tool_title_too_long() {
    let registry = Arc::new(SchemaRegistry::new(PathBuf::from("schemas")));
    let schema = create_mcp_tool_schema();
    let pipeline = ValidationPipeline::new(registry);

    let long_title = "A".repeat(201); // Max is 200

    let input = json!({
        "project_id": "550e8400-e29b-41d4-a716-446655440000",
        "title": long_title,
        "priority": "high"
    });

    let result = pipeline.validate_against_schema(&schema, &input).unwrap();

    assert!(!result.valid);
    assert!(result.has_field_error("title"));
}

#[tokio::test]
async fn test_agent_assignment_valid_input() {
    let registry = Arc::new(SchemaRegistry::new(PathBuf::from("schemas")));
    let schema = create_agent_assignment_schema();
    let pipeline = ValidationPipeline::new(registry);

    let input = json!({
        "role": "developer",
        "title": "Fix authentication bug"
    });

    let result = pipeline.validate_against_schema(&schema, &input).unwrap();

    assert!(result.valid);
    assert!(result.errors.is_empty());

    // Check default priority applied
    let validated = result.validated_data.unwrap();
    assert_eq!(validated["priority"], 50);
}

#[tokio::test]
async fn test_agent_assignment_invalid_role() {
    let registry = Arc::new(SchemaRegistry::new(PathBuf::from("schemas")));
    let schema = create_agent_assignment_schema();
    let pipeline = ValidationPipeline::new(registry);

    let input = json!({
        "role": "invalid_role", // Not in enum
        "title": "Test Task"
    });

    let result = pipeline.validate_against_schema(&schema, &input).unwrap();

    assert!(!result.valid);
    assert!(result.has_field_error("role"));
}

#[tokio::test]
async fn test_agent_assignment_priority_out_of_range() {
    let registry = Arc::new(SchemaRegistry::new(PathBuf::from("schemas")));
    let schema = create_agent_assignment_schema();
    let pipeline = ValidationPipeline::new(registry);

    let input = json!({
        "role": "developer",
        "title": "Test Task",
        "priority": 150 // > 100
    });

    let result = pipeline.validate_against_schema(&schema, &input).unwrap();

    assert!(!result.valid);
    assert!(result.has_field_error("priority"));
}

#[tokio::test]
async fn test_strict_mode_rejects_unknown_fields() {
    let registry = Arc::new(SchemaRegistry::new(PathBuf::from("schemas")));
    let schema = create_mcp_tool_schema();
    let pipeline = ValidationPipeline::new(registry).with_strict_mode(true);

    let input = json!({
        "project_id": "550e8400-e29b-41d4-a716-446655440000",
        "title": "Test Task",
        "priority": "high",
        "unknown_field": "value" // Not in schema
    });

    let result = pipeline.validate_against_schema(&schema, &input).unwrap();

    assert!(!result.valid);
    assert!(result.has_field_error("unknown_field"));
}

#[tokio::test]
async fn test_non_strict_mode_allows_unknown_fields() {
    let registry = Arc::new(SchemaRegistry::new(PathBuf::from("schemas")));
    let schema = create_mcp_tool_schema();
    let pipeline = ValidationPipeline::new(registry).with_strict_mode(false);

    let input = json!({
        "project_id": "550e8400-e29b-41d4-a716-446655440000",
        "title": "Test Task",
        "priority": "high",
        "unknown_field": "value" // Allowed in non-strict mode
    });

    let result = pipeline.validate_against_schema(&schema, &input).unwrap();

    // Should pass, but unknown_field won't be in validated_data
    assert!(result.valid);
    let validated = result.validated_data.unwrap();
    assert!(!validated.as_object().unwrap().contains_key("unknown_field"));
}

#[tokio::test]
async fn test_multiple_validation_errors() {
    let registry = Arc::new(SchemaRegistry::new(PathBuf::from("schemas")));
    let schema = create_mcp_tool_schema();
    let pipeline = ValidationPipeline::new(registry);

    let input = json!({
        "project_id": "invalid-uuid",
        "title": "AB", // Too short
        "priority": "high"
    });

    let result = pipeline.validate_against_schema(&schema, &input).unwrap();

    assert!(!result.valid);
    // Should have errors for both project_id and title
    assert!(result.errors.len() >= 2);
    assert!(result.has_field_error("project_id"));
    assert!(result.has_field_error("title"));
}

#[tokio::test]
async fn test_validation_result_serialization() {
    let registry = Arc::new(SchemaRegistry::new(PathBuf::from("schemas")));
    let schema = create_mcp_tool_schema();
    let pipeline = ValidationPipeline::new(registry);

    let input = json!({
        "title": "", // Empty violates NonEmpty
        "priority": "high"
    });

    let result = pipeline.validate_against_schema(&schema, &input).unwrap();

    // Serialize to JSON
    let json_result = serde_json::to_string(&result).unwrap();
    assert!(json_result.contains("valid"));
    assert!(json_result.contains("errors"));

    // Deserialize back
    let _deserialized: vapora_shared::validation::ValidationResult =
        serde_json::from_str(&json_result).unwrap();
}
