//! Integration tests for the agent-optimized API layer.
//!
//! These tests exercise the `agent` module functions (`full_check`, `batch_validate`,
//! `from_validation`, `from_explanation`, `from_fix`, `from_policy`) against real
//! schemas and SQL queries, verifying response shapes, error codes, metadata, and
//! JSON serialization.

use altimate_core::agent::{
    AgentResponse, BatchRequest, BatchResponse, ResponseStatus, batch_validate, from_explanation,
    from_fix, from_policy, from_validation, full_check,
};
use altimate_core::guardrails::policy::PolicyConfig;
use altimate_core::schema::types::SchemaDefinition;

// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------

fn ecommerce_schema() -> SchemaDefinition {
    SchemaDefinition::from_yaml(include_str!(
        "../../../tests/fixtures/schemas/ecommerce.yaml"
    ))
    .expect("Failed to load ecommerce schema")
}

fn dbt_schema() -> SchemaDefinition {
    SchemaDefinition::from_yaml(include_str!(
        "../../../tests/fixtures/schemas/dbt_ecommerce.yaml"
    ))
    .expect("Failed to load dbt schema")
}

fn strict_policy() -> PolicyConfig {
    PolicyConfig::from_yaml(include_str!("../../../tests/fixtures/policies/strict.yaml"))
        .expect("Failed to load strict policy")
}

// ===========================================================================
// Group 1: Valid SQL -> Success Response (6 tests)
// ===========================================================================

#[tokio::test]
async fn valid_simple_select_returns_success() {
    let schema = ecommerce_schema();
    let result = altimate_core::validate("SELECT id, email FROM users", &schema).await;
    let response = from_validation(&result, &schema, 1);

    assert_eq!(response.status, ResponseStatus::Success);
    assert_eq!(response.code, "VALID");
    assert!(response.errors.is_empty());
}

#[tokio::test]
async fn valid_select_with_join_returns_success() {
    let schema = ecommerce_schema();
    let result = altimate_core::validate(
        "SELECT u.id, o.total_amount FROM users u JOIN orders o ON u.id = o.user_id",
        &schema,
    )
    .await;
    let response = from_validation(&result, &schema, 1);

    assert_eq!(response.status, ResponseStatus::Success);
    assert_eq!(response.code, "VALID");
}

#[tokio::test]
async fn valid_select_with_where_returns_success() {
    let schema = ecommerce_schema();
    let result = altimate_core::validate(
        "SELECT id, name FROM users WHERE status = 'active'",
        &schema,
    )
    .await;
    let response = from_validation(&result, &schema, 1);

    assert_eq!(response.status, ResponseStatus::Success);
    assert_eq!(response.code, "VALID");
}

#[tokio::test]
async fn valid_cte_query_returns_success() {
    let schema = ecommerce_schema();
    let result = altimate_core::validate(
        "WITH active_users AS (SELECT id, email FROM users WHERE status = 'active') \
         SELECT * FROM active_users",
        &schema,
    )
    .await;
    let response = from_validation(&result, &schema, 1);

    assert_eq!(response.status, ResponseStatus::Success);
    assert_eq!(response.code, "VALID");
}

#[tokio::test]
async fn valid_aggregation_query_returns_success() {
    let schema = ecommerce_schema();
    let result = altimate_core::validate(
        "SELECT status, COUNT(*) AS cnt FROM users GROUP BY status",
        &schema,
    )
    .await;
    let response = from_validation(&result, &schema, 1);

    assert_eq!(response.status, ResponseStatus::Success);
    assert_eq!(response.code, "VALID");
}

#[tokio::test]
async fn valid_query_summary_is_short() {
    let schema = ecommerce_schema();
    let result = altimate_core::validate("SELECT id FROM users", &schema).await;
    let response = from_validation(&result, &schema, 1);

    assert!(
        response.summary.len() <= 200,
        "Summary should be under 200 chars, got {} chars: '{}'",
        response.summary.len(),
        response.summary
    );
    assert!(
        !response.summary.contains('\n'),
        "Summary should be one line"
    );
}

// ===========================================================================
// Group 2: Error Responses (9 tests)
// ===========================================================================

#[tokio::test]
async fn error_table_not_found() {
    let schema = ecommerce_schema();
    let result = altimate_core::validate("SELECT * FROM nonexistent", &schema).await;
    let response = from_validation(&result, &schema, 1);

    assert_eq!(response.status, ResponseStatus::Error);
    assert_eq!(response.code, "TABLE_NOT_FOUND");
    assert!(!response.errors.is_empty());
    assert_eq!(response.errors[0].code, "TABLE_NOT_FOUND");
}

#[tokio::test]
async fn error_column_not_found() {
    let schema = ecommerce_schema();
    let result = altimate_core::validate("SELECT nonexistent_col FROM users", &schema).await;
    let response = from_validation(&result, &schema, 1);

    assert_eq!(response.status, ResponseStatus::Error);
    assert_eq!(response.code, "COLUMN_NOT_FOUND");
    assert!(!response.errors.is_empty());
    assert_eq!(response.errors[0].code, "COLUMN_NOT_FOUND");
}

#[tokio::test]
async fn error_syntax_error() {
    let schema = ecommerce_schema();
    let result = altimate_core::validate("SELECTZ * FROM users", &schema).await;
    let response = from_validation(&result, &schema, 1);

    assert_eq!(response.status, ResponseStatus::Error);
    assert_eq!(response.code, "SYNTAX_ERROR");
}

#[tokio::test]
async fn error_table_typo_has_suggested_fix() {
    let schema = ecommerce_schema();
    // "usrs" is close to "users"
    let result = altimate_core::validate("SELECT * FROM usrs", &schema).await;
    let response = from_validation(&result, &schema, 1);

    assert_eq!(response.status, ResponseStatus::Error);
    assert_eq!(response.errors[0].code, "TABLE_NOT_FOUND");
    assert!(
        response.errors[0].suggested_fix.is_some(),
        "Expected suggested_fix for table typo 'usrs', got None"
    );
}

#[tokio::test]
async fn error_column_typo_has_suggested_fix() {
    let schema = ecommerce_schema();
    // "emal" is close to "email"
    let result = altimate_core::validate("SELECT emal FROM users", &schema).await;
    let response = from_validation(&result, &schema, 1);

    assert_eq!(response.status, ResponseStatus::Error);
    assert_eq!(response.errors[0].code, "COLUMN_NOT_FOUND");
    assert!(
        response.errors[0].suggested_fix.is_some(),
        "Expected suggested_fix for column typo 'emal', got None"
    );
}

#[tokio::test]
async fn error_fix_confidence_is_bounded() {
    let schema = ecommerce_schema();
    let result = altimate_core::validate("SELECT * FROM usrs", &schema).await;
    let response = from_validation(&result, &schema, 1);

    if let Some(confidence) = response.errors[0].fix_confidence {
        assert!(
            (0.0..=1.0).contains(&confidence),
            "Fix confidence should be between 0.0 and 1.0, got {}",
            confidence
        );
    }
}

#[tokio::test]
async fn error_multiple_errors_reported() {
    let schema = ecommerce_schema();
    // Two separate invalid references in one query
    let result = altimate_core::validate("SELECT bad_col FROM nonexistent_table", &schema).await;
    let response = from_validation(&result, &schema, 1);

    assert_eq!(response.status, ResponseStatus::Error);
    assert!(
        !response.errors.is_empty(),
        "Expected at least one error for bad query"
    );
}

#[tokio::test]
async fn error_codes_are_uppercase_no_spaces() {
    let schema = ecommerce_schema();

    // Test several error types
    for sql in &[
        "SELECT * FROM nonexistent",
        "SELECT bad_col FROM users",
        "SELECTZ * FROM users",
    ] {
        let result = altimate_core::validate(sql, &schema).await;
        let response = from_validation(&result, &schema, 1);

        assert!(
            response.code.chars().all(|c| c.is_uppercase() || c == '_'),
            "Error code '{}' should be uppercase with underscores only (sql: {})",
            response.code,
            sql
        );

        for err in &response.errors {
            assert!(
                err.code.chars().all(|c| c.is_uppercase() || c == '_'),
                "Agent error code '{}' should be uppercase with underscores only",
                err.code
            );
        }
    }
}

#[tokio::test]
async fn error_sql_fragment_is_set_for_table_not_found() {
    let schema = ecommerce_schema();
    let result = altimate_core::validate("SELECT * FROM nonexistent", &schema).await;
    let response = from_validation(&result, &schema, 1);

    assert_eq!(
        response.errors[0].sql_fragment.as_deref(),
        Some("nonexistent"),
        "sql_fragment should contain the table name"
    );
}

// ===========================================================================
// Group 3: Suggested Actions (5 tests)
// ===========================================================================

#[tokio::test]
async fn error_response_includes_apply_fix_when_fixable() {
    let schema = ecommerce_schema();
    let result = altimate_core::validate("SELECT * FROM usrs", &schema).await;
    let response = from_validation(&result, &schema, 1);

    let has_apply_fix = response
        .suggested_actions
        .iter()
        .any(|a| a.action == "apply_fix");
    assert!(
        has_apply_fix,
        "Expected 'apply_fix' action when errors are fixable"
    );
}

#[tokio::test]
async fn valid_response_has_no_apply_fix_action() {
    let schema = ecommerce_schema();
    let result = altimate_core::validate("SELECT id FROM users", &schema).await;
    let response = from_validation(&result, &schema, 1);

    let has_apply_fix = response
        .suggested_actions
        .iter()
        .any(|a| a.action == "apply_fix");
    assert!(
        !has_apply_fix,
        "Valid query should not have 'apply_fix' action"
    );
}

#[tokio::test]
async fn fix_response_includes_explain_action() {
    let schema = ecommerce_schema();
    let fix_result = altimate_core::fix("SELECT * FROM usrs", &schema, &Default::default()).await;
    let response = from_fix(&fix_result, "SELECT * FROM usrs", &schema, 1);

    if response.status == ResponseStatus::Success {
        let has_explain = response
            .suggested_actions
            .iter()
            .any(|a| a.action == "explain_query");
        assert!(has_explain, "Fixed response should suggest 'explain_query'");
    }
}

#[tokio::test]
async fn action_strings_are_valid_identifiers() {
    let schema = ecommerce_schema();

    // Test actions from valid and invalid queries
    for sql in &["SELECT id FROM users", "SELECT * FROM usrs"] {
        let result = altimate_core::validate(sql, &schema).await;
        let response = from_validation(&result, &schema, 1);

        for action in &response.suggested_actions {
            assert!(
                action
                    .action
                    .chars()
                    .all(|c| c.is_alphanumeric() || c == '_'),
                "Action '{}' should be a valid identifier (alphanumeric + underscore)",
                action.action
            );
        }
    }
}

#[tokio::test]
async fn valid_response_has_explain_and_policy_actions() {
    let schema = ecommerce_schema();
    let result = altimate_core::validate("SELECT id FROM users", &schema).await;
    let response = from_validation(&result, &schema, 1);

    let actions: Vec<&str> = response
        .suggested_actions
        .iter()
        .map(|a| a.action.as_str())
        .collect();
    assert!(
        actions.contains(&"explain_query"),
        "Valid query should suggest 'explain_query'"
    );
    assert!(
        actions.contains(&"check_policy"),
        "Valid query should suggest 'check_policy'"
    );
}

// ===========================================================================
// Group 4: Fix Responses (5 tests)
// ===========================================================================

#[tokio::test]
async fn fix_applied_returns_success_with_fixed_sql() {
    let schema = ecommerce_schema();
    let fix_result = altimate_core::fix("SELECT * FROM usrs", &schema, &Default::default()).await;
    let response = from_fix(&fix_result, "SELECT * FROM usrs", &schema, 1);

    assert_eq!(response.status, ResponseStatus::Success);
    assert_eq!(response.code, "FIXED");
    assert!(response.result.is_some());

    let payload = response.result.as_ref().unwrap();
    assert_eq!(payload["fixed_sql"], "SELECT * FROM users");
}

#[tokio::test]
async fn fix_not_possible_returns_error() {
    let schema = ecommerce_schema();
    let fix_result = altimate_core::fix("SELECTZ * FROM users", &schema, &Default::default()).await;
    let response = from_fix(&fix_result, "SELECTZ * FROM users", &schema, 1);

    assert_eq!(response.status, ResponseStatus::Error);
    assert_eq!(response.code, "FIX_FAILED");
}

#[tokio::test]
async fn fix_confidence_is_positive_for_fuzzy_matches() {
    let schema = ecommerce_schema();
    let result = altimate_core::validate("SELECT * FROM usrs", &schema).await;
    let response = from_validation(&result, &schema, 1);

    if let Some(confidence) = response.errors[0].fix_confidence {
        assert!(
            confidence > 0.0,
            "Fix confidence should be positive for fuzzy match, got {}",
            confidence
        );
    }
}

#[tokio::test]
async fn fixed_sql_passes_validation() {
    let schema = ecommerce_schema();
    let fix_result = altimate_core::fix("SELECT * FROM usrs", &schema, &Default::default()).await;

    if fix_result.fixed && fix_result.post_fix_valid {
        // Re-validate the fixed SQL
        let recheck = altimate_core::validate(&fix_result.fixed_sql, &schema).await;
        assert!(
            recheck.valid,
            "Fixed SQL should pass validation, but got errors: {:?}",
            recheck.errors
        );
    }
}

#[tokio::test]
async fn fix_result_contains_original_sql() {
    let schema = ecommerce_schema();
    let original = "SELECT * FROM usrs";
    let fix_result = altimate_core::fix(original, &schema, &Default::default()).await;
    let response = from_fix(&fix_result, original, &schema, 1);

    if let Some(payload) = &response.result {
        assert_eq!(
            payload["original_sql"], original,
            "Fix response should include the original SQL"
        );
    }
}

// ===========================================================================
// Group 5: Full Check Pipeline (6 tests)
// ===========================================================================

#[tokio::test]
async fn full_check_valid_sql_returns_success_with_explanation() {
    let schema = ecommerce_schema();
    let response = full_check("SELECT id, email FROM users", &schema, None).await;

    assert_eq!(response.status, ResponseStatus::Success);
    assert_eq!(response.code, "VALID");
    // full_check includes explanation in result
    assert!(
        response.result.is_some(),
        "full_check on valid SQL should include explanation in result"
    );
}

#[tokio::test]
async fn full_check_invalid_sql_returns_error_with_suggestions() {
    let schema = ecommerce_schema();
    let response = full_check("SELECT * FROM nonexistent", &schema, None).await;

    assert_eq!(response.status, ResponseStatus::Error);
    assert!(!response.errors.is_empty());
}

#[tokio::test]
async fn full_check_with_policy_includes_policy_check() {
    let schema = ecommerce_schema();
    let policy = strict_policy();
    let response = full_check("SELECT id FROM users WHERE id = 1", &schema, Some(&policy)).await;

    // With strict policy requiring LIMIT, this might produce warnings or violations
    // The important thing is the response is valid and contains policy info
    assert!(
        response.status == ResponseStatus::Success
            || response.status == ResponseStatus::Warning
            || response.status == ResponseStatus::Error
    );
}

#[tokio::test]
async fn full_check_without_policy_no_policy_info() {
    let schema = ecommerce_schema();
    let response = full_check("SELECT id FROM users", &schema, None).await;

    // Without policy, there should be no POLICY_VIOLATION errors
    let has_policy_error = response.errors.iter().any(|e| e.code.starts_with("POLICY"));
    assert!(
        !has_policy_error,
        "Without policy, there should be no policy errors"
    );
}

#[tokio::test]
async fn full_check_metadata_has_processing_time() {
    let schema = ecommerce_schema();
    let response = full_check("SELECT id FROM users", &schema, None).await;

    // processing_time_ms should be set — the field must exist in the response
    let _ = response.metadata.processing_time_ms;
}

#[tokio::test]
async fn full_check_invalid_suggests_apply_fix() {
    let schema = ecommerce_schema();
    let response = full_check("SELECT * FROM usrs", &schema, None).await;

    assert_eq!(response.status, ResponseStatus::Error);
    let has_apply_fix = response
        .suggested_actions
        .iter()
        .any(|a| a.action == "apply_fix");
    assert!(
        has_apply_fix,
        "full_check on invalid SQL should suggest apply_fix"
    );
}

// ===========================================================================
// Group 6: Batch Validation (6 tests)
// ===========================================================================

#[tokio::test]
async fn batch_all_valid_counts_match() {
    let schema = ecommerce_schema();
    let requests = vec![
        BatchRequest {
            id: "q1".to_string(),
            sql: "SELECT id FROM users".to_string(),
        },
        BatchRequest {
            id: "q2".to_string(),
            sql: "SELECT * FROM orders".to_string(),
        },
        BatchRequest {
            id: "q3".to_string(),
            sql: "SELECT name FROM products".to_string(),
        },
    ];

    let batch = batch_validate(requests, &schema).await;

    assert_eq!(batch.total, 3);
    assert_eq!(batch.passed, 3);
    assert_eq!(batch.failed, 0);
}

#[tokio::test]
async fn batch_mix_valid_invalid_counts_correct() {
    let schema = ecommerce_schema();
    let requests = vec![
        BatchRequest {
            id: "valid1".to_string(),
            sql: "SELECT id FROM users".to_string(),
        },
        BatchRequest {
            id: "invalid1".to_string(),
            sql: "SELECT * FROM nonexistent".to_string(),
        },
        BatchRequest {
            id: "valid2".to_string(),
            sql: "SELECT * FROM products".to_string(),
        },
        BatchRequest {
            id: "invalid2".to_string(),
            sql: "SELECTZ broken".to_string(),
        },
    ];

    let batch = batch_validate(requests, &schema).await;

    assert_eq!(batch.total, 4);
    assert_eq!(batch.passed, 2);
    assert_eq!(batch.failed, 2);
}

#[tokio::test]
async fn batch_empty_list_returns_empty() {
    let schema = ecommerce_schema();
    let batch = batch_validate(vec![], &schema).await;

    assert_eq!(batch.total, 0);
    assert_eq!(batch.passed, 0);
    assert_eq!(batch.failed, 0);
    assert!(batch.results.is_empty());
}

#[tokio::test]
async fn batch_items_have_correct_ids() {
    let schema = ecommerce_schema();
    let requests = vec![
        BatchRequest {
            id: "model_users".to_string(),
            sql: "SELECT id FROM users".to_string(),
        },
        BatchRequest {
            id: "model_orders".to_string(),
            sql: "SELECT id FROM orders".to_string(),
        },
    ];

    let batch = batch_validate(requests, &schema).await;

    assert_eq!(batch.results[0].id, "model_users");
    assert_eq!(batch.results[1].id, "model_orders");
}

#[tokio::test]
async fn batch_metadata_has_processing_time() {
    let schema = ecommerce_schema();
    let requests = vec![BatchRequest {
        id: "q1".to_string(),
        sql: "SELECT id FROM users".to_string(),
    }];

    let batch = batch_validate(requests, &schema).await;

    // Batch metadata should have processing_time_ms field
    let _ = batch.metadata.processing_time_ms;
}

#[tokio::test]
async fn batch_item_responses_have_correct_status() {
    let schema = ecommerce_schema();
    let requests = vec![
        BatchRequest {
            id: "good".to_string(),
            sql: "SELECT id FROM users".to_string(),
        },
        BatchRequest {
            id: "bad".to_string(),
            sql: "SELECT * FROM not_a_table".to_string(),
        },
    ];

    let batch = batch_validate(requests, &schema).await;

    assert_eq!(batch.results[0].response.status, ResponseStatus::Success);
    assert_eq!(batch.results[1].response.status, ResponseStatus::Error);
}

// ===========================================================================
// Group 7: JSON Serialization (5 tests)
// ===========================================================================

#[tokio::test]
async fn agent_response_serializes_to_valid_json() {
    let schema = ecommerce_schema();
    let result = altimate_core::validate("SELECT id FROM users", &schema).await;
    let response = from_validation(&result, &schema, 10);

    let json = serde_json::to_string(&response);
    assert!(json.is_ok(), "AgentResponse should serialize to JSON");

    // Verify it parses back as valid JSON
    let value: Result<serde_json::Value, _> = serde_json::from_str(&json.unwrap());
    assert!(value.is_ok(), "Serialized JSON should be valid");
}

#[tokio::test]
async fn agent_response_json_roundtrip() {
    let schema = ecommerce_schema();
    let result = altimate_core::validate("SELECT id FROM users", &schema).await;
    let original = from_validation(&result, &schema, 42);

    let json = serde_json::to_string(&original).expect("serialize");
    let deserialized: AgentResponse = serde_json::from_str(&json).expect("deserialize");

    assert_eq!(deserialized.status, original.status);
    assert_eq!(deserialized.code, original.code);
    assert_eq!(deserialized.summary, original.summary);
    assert_eq!(deserialized.errors.len(), original.errors.len());
    assert_eq!(
        deserialized.suggested_actions.len(),
        original.suggested_actions.len()
    );
    assert_eq!(
        deserialized.metadata.processing_time_ms,
        original.metadata.processing_time_ms
    );
}

#[tokio::test]
async fn valid_response_json_skips_empty_errors() {
    let schema = ecommerce_schema();
    let result = altimate_core::validate("SELECT id FROM users", &schema).await;
    let response = from_validation(&result, &schema, 1);

    let json = serde_json::to_string(&response).expect("serialize");

    // "errors" field should not appear in serialized JSON for valid queries
    // because of #[serde(skip_serializing_if = "Vec::is_empty")]
    assert!(
        !json.contains("\"errors\""),
        "Valid response JSON should skip empty errors field, got: {}",
        json
    );
}

#[tokio::test]
async fn metadata_always_present_in_json() {
    let schema = ecommerce_schema();

    // Valid query
    let result = altimate_core::validate("SELECT id FROM users", &schema).await;
    let response = from_validation(&result, &schema, 1);
    let json = serde_json::to_string(&response).expect("serialize");
    assert!(
        json.contains("\"metadata\""),
        "metadata should always be present in JSON"
    );

    // Invalid query
    let result = altimate_core::validate("SELECT * FROM nonexistent", &schema).await;
    let response = from_validation(&result, &schema, 1);
    let json = serde_json::to_string(&response).expect("serialize");
    assert!(
        json.contains("\"metadata\""),
        "metadata should always be present in JSON for error responses too"
    );
}

#[tokio::test]
async fn batch_response_json_roundtrip() {
    let schema = ecommerce_schema();
    let requests = vec![
        BatchRequest {
            id: "q1".to_string(),
            sql: "SELECT id FROM users".to_string(),
        },
        BatchRequest {
            id: "q2".to_string(),
            sql: "SELECT * FROM nonexistent".to_string(),
        },
    ];
    let original = batch_validate(requests, &schema).await;

    let json = serde_json::to_string(&original).expect("serialize");
    let deserialized: BatchResponse = serde_json::from_str(&json).expect("deserialize");

    assert_eq!(deserialized.total, original.total);
    assert_eq!(deserialized.passed, original.passed);
    assert_eq!(deserialized.failed, original.failed);
    assert_eq!(deserialized.results.len(), original.results.len());
}

// ===========================================================================
// Group 8: Metadata (5 tests)
// ===========================================================================

#[tokio::test]
async fn metadata_schema_tables_lists_all_tables() {
    let schema = ecommerce_schema();
    let result = altimate_core::validate("SELECT id FROM users", &schema).await;
    let response = from_validation(&result, &schema, 1);

    let tables = &response.metadata.schema_tables;

    assert!(tables.contains(&"users".to_string()), "Should list users");
    assert!(tables.contains(&"orders".to_string()), "Should list orders");
    assert!(
        tables.contains(&"products".to_string()),
        "Should list products"
    );
    assert!(
        tables.contains(&"order_items".to_string()),
        "Should list order_items"
    );
    assert_eq!(
        tables.len(),
        4,
        "Ecommerce schema has 4 tables, got {}",
        tables.len()
    );
}

#[tokio::test]
async fn metadata_dialect_matches_schema() {
    let schema = ecommerce_schema();
    let result = altimate_core::validate("SELECT id FROM users", &schema).await;
    let response = from_validation(&result, &schema, 1);

    assert_eq!(
        response.metadata.dialect, "generic",
        "Ecommerce schema dialect should be 'generic'"
    );
}

#[tokio::test]
async fn metadata_altimate_version_is_set() {
    let schema = ecommerce_schema();
    let result = altimate_core::validate("SELECT id FROM users", &schema).await;
    let response = from_validation(&result, &schema, 1);

    assert!(
        !response.metadata.altimate_version.is_empty(),
        "altimate_version should be set"
    );
    // Version should look like a semver string
    assert!(
        response.metadata.altimate_version.contains('.'),
        "Version '{}' should be semver-like",
        response.metadata.altimate_version
    );
}

#[tokio::test]
async fn metadata_processing_time_is_set() {
    let schema = ecommerce_schema();
    let result = altimate_core::validate("SELECT id FROM users", &schema).await;
    let response = from_validation(&result, &schema, 42);

    assert_eq!(
        response.metadata.processing_time_ms, 42,
        "processing_time_ms should match what was passed"
    );
}

#[tokio::test]
async fn metadata_tables_are_sorted() {
    let schema = ecommerce_schema();
    let result = altimate_core::validate("SELECT id FROM users", &schema).await;
    let response = from_validation(&result, &schema, 1);

    let tables = &response.metadata.schema_tables;
    let mut sorted = tables.clone();
    sorted.sort();
    assert_eq!(tables, &sorted, "schema_tables should be sorted");
}

// ===========================================================================
// Group 9: Edge Cases (4 tests)
// ===========================================================================

#[tokio::test]
async fn empty_sql_returns_error() {
    let schema = ecommerce_schema();
    let result = altimate_core::validate("", &schema).await;
    let response = from_validation(&result, &schema, 1);

    assert_eq!(response.status, ResponseStatus::Error);
    assert!(!response.errors.is_empty());
}

#[tokio::test]
async fn very_long_sql_does_not_crash() {
    let schema = ecommerce_schema();
    // Build a very long SQL with many columns
    let cols: Vec<String> = (0..500).map(|i| format!("id AS col_{i}")).collect();
    let long_sql = format!("SELECT {} FROM users", cols.join(", "));

    let result = altimate_core::validate(&long_sql, &schema).await;
    let response = from_validation(&result, &schema, 1);

    // Should either succeed or fail gracefully
    assert!(response.status == ResponseStatus::Success || response.status == ResponseStatus::Error);
}

#[tokio::test]
async fn sql_with_comments_handles_correctly() {
    let schema = ecommerce_schema();
    let sql = "-- This is a comment\nSELECT id FROM users /* inline comment */";
    let result = altimate_core::validate(sql, &schema).await;
    let response = from_validation(&result, &schema, 1);

    assert_eq!(
        response.status,
        ResponseStatus::Success,
        "SQL with comments should still validate. Errors: {:?}",
        response.errors
    );
}

#[tokio::test]
async fn whitespace_only_sql_returns_error() {
    let schema = ecommerce_schema();
    let result = altimate_core::validate("   \n\t  ", &schema).await;
    let response = from_validation(&result, &schema, 1);

    assert_eq!(response.status, ResponseStatus::Error);
}

// ===========================================================================
// Group 10: Explanation Responses (3 tests)
// ===========================================================================

#[tokio::test]
async fn from_explanation_valid_has_plan_steps() {
    let schema = ecommerce_schema();
    let explanation = altimate_core::explain("SELECT id, email FROM users", &schema).await;
    let response = from_explanation(&explanation, &schema, 1);

    assert_eq!(response.status, ResponseStatus::Success);
    assert!(response.result.is_some());
    let payload = response.result.as_ref().unwrap();
    assert!(
        payload.get("plan_steps").is_some(),
        "Explanation result should have plan_steps"
    );
    assert!(
        payload.get("lineage").is_some(),
        "Explanation result should have lineage"
    );
}

#[tokio::test]
async fn from_explanation_invalid_returns_error() {
    let schema = ecommerce_schema();
    let explanation = altimate_core::explain("SELECT * FROM nonexistent", &schema).await;
    let response = from_explanation(&explanation, &schema, 1);

    assert_eq!(response.status, ResponseStatus::Error);
    assert!(response.result.is_none());
    assert!(!response.errors.is_empty());
}

#[tokio::test]
async fn from_explanation_has_cost_signals() {
    let schema = ecommerce_schema();
    let explanation = altimate_core::explain(
        "SELECT u.id, o.total_amount FROM users u JOIN orders o ON u.id = o.user_id",
        &schema,
    )
    .await;
    let response = from_explanation(&explanation, &schema, 1);

    assert_eq!(response.status, ResponseStatus::Success);
    let payload = response.result.as_ref().unwrap();
    let cost = payload.get("cost_signals").unwrap();
    assert!(cost.get("join_count").is_some());
    assert!(cost.get("complexity").is_some());
}

// ===========================================================================
// Group 11: Policy Responses (3 tests)
// ===========================================================================

#[tokio::test]
async fn from_policy_allowed_returns_success() {
    let schema = ecommerce_schema();
    let policy = PolicyConfig::default();
    let result = altimate_core::check_policy("SELECT id FROM users", &schema, &policy).await;
    let response = from_policy(&result, &schema, 1);

    assert_eq!(response.status, ResponseStatus::Success);
    assert_eq!(response.code, "POLICY_PASS");
}

#[tokio::test]
async fn from_policy_blocked_returns_error() {
    let schema = ecommerce_schema();
    let policy = strict_policy();
    // SELECT * is blocked by strict policy
    let result = altimate_core::check_policy("SELECT * FROM users", &schema, &policy).await;
    let response = from_policy(&result, &schema, 1);

    // Strict policy blocks SELECT * and requires WHERE and LIMIT
    if !result.allowed {
        assert_eq!(response.status, ResponseStatus::Error);
        assert_eq!(response.code, "POLICY_VIOLATION");
        assert!(!response.errors.is_empty());
    }
}

#[tokio::test]
async fn from_policy_result_has_audit() {
    let schema = ecommerce_schema();
    let policy = PolicyConfig::default();
    let result = altimate_core::check_policy("SELECT id FROM users", &schema, &policy).await;
    let response = from_policy(&result, &schema, 1);

    if let Some(payload) = &response.result {
        assert!(
            payload.get("audit").is_some(),
            "Policy response should include audit trail"
        );
    }
}

// ===========================================================================
// Group 12: Cross-Schema Tests with dbt schema (3 tests)
// ===========================================================================

#[tokio::test]
async fn dbt_schema_valid_query() {
    let schema = dbt_schema();
    let response = full_check(
        "SELECT customer_id, first_name FROM stg_customers",
        &schema,
        None,
    )
    .await;

    assert_eq!(response.status, ResponseStatus::Success);
}

#[tokio::test]
async fn dbt_schema_invalid_table() {
    let schema = dbt_schema();
    let response = full_check("SELECT * FROM nonexistent_model", &schema, None).await;

    assert_eq!(response.status, ResponseStatus::Error);
    assert_eq!(response.code, "TABLE_NOT_FOUND");
}

#[tokio::test]
async fn dbt_schema_metadata_lists_dbt_tables() {
    let schema = dbt_schema();
    let result = altimate_core::validate("SELECT customer_id FROM stg_customers", &schema).await;
    let response = from_validation(&result, &schema, 1);

    let tables = &response.metadata.schema_tables;
    assert!(
        tables.contains(&"stg_customers".to_string()),
        "dbt schema should list stg_customers"
    );
    assert!(
        tables.contains(&"stg_orders".to_string()),
        "dbt schema should list stg_orders"
    );
}
