//! Cross-tier integration tests.
//!
//! Tests workflows that combine multiple tier features together.

use altimate_core::completer::complete;
use altimate_core::lineage::tracker::track_lineage;
use altimate_core::linter::lint;
use altimate_core::migration::analyzer::analyze_migration;
use altimate_core::migration::types::MigrationRisk;
use altimate_core::pruner::prune_schema;
use altimate_core::rewriter::engine::rewrite;
use altimate_core::schema::types::SchemaDefinition;
use altimate_core::testgen::engine::generate_tests;

// ─── Helpers ─────────────────────────────────────────────────────

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

// ─── Validate + Lint pipeline ────────────────────────────────────

#[tokio::test]
async fn test_validate_then_lint_clean_query() {
    let schema = ecommerce_schema();
    let sql = "SELECT id, email FROM users WHERE id = 1 LIMIT 100";
    let validation = altimate_core::validate(sql, &schema).await;
    assert!(validation.valid);
    let lint_result = lint(sql, &schema);
    assert!(lint_result.clean);
}

#[tokio::test]
async fn test_validate_then_lint_select_star() {
    let schema = ecommerce_schema();
    let sql = "SELECT * FROM users";
    let validation = altimate_core::validate(sql, &schema).await;
    assert!(validation.valid); // SELECT * is valid SQL
    let lint_result = lint(sql, &schema);
    assert!(!lint_result.clean); // But lint should flag it
}

#[tokio::test]
async fn test_validate_then_lint_null_comparison() {
    let schema = ecommerce_schema();
    let sql = "SELECT * FROM users WHERE name = NULL";
    let _validation = altimate_core::validate(sql, &schema).await;
    // Validation may or may not catch this, but lint should
    let lint_result = lint(sql, &schema);
    assert!(lint_result.findings.iter().any(|f| f.code == "L009"));
}

// ─── Lint + Rewrite pipeline ─────────────────────────────────────

#[test]
fn test_lint_then_rewrite_select_star() {
    let schema = ecommerce_schema();
    let sql = "SELECT * FROM users";
    let lint_result = lint(sql, &schema);
    assert!(lint_result.findings.iter().any(|f| f.code == "L001"));

    let rewrite_result = rewrite(sql, &schema);
    assert!(
        rewrite_result
            .suggestions
            .iter()
            .any(|s| s.rule == "star_to_columns")
    );
}

#[test]
fn test_lint_and_rewrite_both_clean() {
    let schema = ecommerce_schema();
    let sql = "SELECT id, email FROM users WHERE id = 1 LIMIT 100";
    let lint_result = lint(sql, &schema);
    assert!(lint_result.clean);
    let rewrite_result = rewrite(sql, &schema);
    assert!(rewrite_result.suggestions.is_empty());
}

// ─── Prune + Validate pipeline ───────────────────────────────────

#[tokio::test]
async fn test_prune_then_validate() {
    let schema = ecommerce_schema();
    let sql = "SELECT u.name, o.total_amount FROM users u JOIN orders o ON u.id = o.user_id";
    let prune_result = prune_schema(sql, &schema).expect("prune should work");
    assert!(prune_result.relevant_tables.contains(&"users".to_string()));
    assert!(prune_result.relevant_tables.contains(&"orders".to_string()));

    // Validate the same SQL against the full schema
    let validation = altimate_core::validate(sql, &schema).await;
    assert!(
        validation.valid,
        "Query should be valid: {:?}",
        validation.errors
    );
}

#[tokio::test]
async fn test_prune_and_validate_pipeline() {
    let schema = ecommerce_schema();
    let sql = "SELECT id FROM users WHERE id = 1";
    let prune_result = prune_schema(sql, &schema).expect("prune");
    // Pruner should produce a result (may include many tables via column matching)
    assert!(prune_result.total_tables == schema.tables.len());
    // Validation should still work
    let validation = altimate_core::validate(sql, &schema).await;
    assert!(validation.valid);
}

// ─── Migration + Lineage impact ──────────────────────────────────

#[test]
fn test_migration_drop_column_impacts_lineage() {
    let schema = ecommerce_schema();
    let migration_sql = "ALTER TABLE users DROP COLUMN email;";
    let migration = analyze_migration(migration_sql, &schema);
    assert_eq!(migration.overall_risk, MigrationRisk::Destructive);

    // Track lineage of a query that uses the dropped column
    let lineage = track_lineage(&["SELECT email FROM users"], &schema).unwrap();
    assert!(
        lineage.queries[0]
            .edges
            .iter()
            .any(|e| e.source.column == "email")
    );
}

#[test]
fn test_safe_migration_add_column() {
    let schema = ecommerce_schema();
    let migration = analyze_migration(
        "ALTER TABLE users ADD COLUMN phone VARCHAR DEFAULT '';",
        &schema,
    );
    assert_eq!(migration.overall_risk, MigrationRisk::Safe);
    assert!(migration.safe);
}

// ─── Completer + Schema ──────────────────────────────────────────

#[test]
fn test_completer_uses_schema_tables() {
    let schema = ecommerce_schema();
    let r = complete("SELECT * FROM ", 14, &schema);
    let labels: Vec<&str> = r.items.iter().map(|i| i.label.as_str()).collect();
    assert!(labels.contains(&"users"));
    assert!(labels.contains(&"orders"));
    assert!(labels.contains(&"products"));
    assert!(labels.contains(&"order_items"));
}

#[test]
fn test_completer_column_from_schema() {
    let schema = ecommerce_schema();
    let r = complete("SELECT  FROM users", 7, &schema);
    let labels: Vec<&str> = r.items.iter().map(|i| i.label.as_str()).collect();
    assert!(labels.contains(&"id"));
    assert!(labels.contains(&"email"));
    assert!(labels.contains(&"name"));
}

// ─── Testgen + Schema ────────────────────────────────────────────

#[test]
fn test_testgen_with_ecommerce_schema() {
    let schema = ecommerce_schema();
    let r = generate_tests("SELECT id, name, age FROM users WHERE age > 18", &schema);
    assert!(!r.test_cases.is_empty());
    assert!(r.tables_referenced.contains(&"users".to_string()));
    // Should generate boundary tests for INT columns
    assert!(r.test_cases.iter().any(|tc| tc.category == "boundary"));
}

#[test]
fn test_testgen_with_join_query() {
    let schema = ecommerce_schema();
    let r = generate_tests(
        "SELECT u.name, o.total_amount FROM users u JOIN orders o ON u.id = o.user_id",
        &schema,
    );
    assert!(r.tables_referenced.contains(&"users".to_string()));
    assert!(r.tables_referenced.contains(&"orders".to_string()));
    assert!(r.test_cases.iter().any(|tc| tc.category == "join"));
}

// ─── Lineage + Rewrite pipeline ──────────────────────────────────

#[test]
fn test_lineage_then_rewrite() {
    let schema = ecommerce_schema();
    let sql = "SELECT * FROM users WHERE id IN (SELECT user_id FROM orders)";
    let lineage = track_lineage(&[sql], &schema).unwrap();
    assert!(
        lineage.queries[0]
            .source_tables
            .contains(&"users".to_string())
    );

    let rewrite_result = rewrite(sql, &schema);
    // Should suggest both star expansion and subquery-to-join
    assert!(!rewrite_result.suggestions.is_empty());
}

// ─── Multi-step: Prune + Lint + Rewrite ──────────────────────────

#[test]
fn test_lint_rewrite_pipeline() {
    let schema = ecommerce_schema();
    let sql = "SELECT * FROM users";

    // Step 1: Lint — SELECT * should be flagged
    let lint_result = lint(sql, &schema);
    assert!(!lint_result.clean);

    // Step 2: Rewrite — should suggest star expansion
    let rewrite_result = rewrite(sql, &schema);
    assert!(!rewrite_result.suggestions.is_empty());
}

// ─── Explain + Validate ──────────────────────────────────────────

#[tokio::test]
async fn test_explain_valid_query() {
    let schema = ecommerce_schema();
    let sql = "SELECT id, name FROM users WHERE id = 1";
    let explanation = altimate_core::explain(sql, &schema).await;
    assert!(explanation.valid);
}

#[tokio::test]
async fn test_explain_invalid_query() {
    let schema = ecommerce_schema();
    let sql = "SELECT nonexistent_col FROM users";
    let explanation = altimate_core::explain(sql, &schema).await;
    assert!(!explanation.valid);
}

// ─── Validate multiple queries ───────────────────────────────────

#[tokio::test]
async fn test_validate_join_query() {
    let schema = ecommerce_schema();
    let sql = "SELECT u.name, o.total_amount FROM users u JOIN orders o ON u.id = o.user_id";
    let result = altimate_core::validate(sql, &schema).await;
    assert!(
        result.valid,
        "Join query should be valid: {:?}",
        result.errors
    );
}

#[tokio::test]
async fn test_validate_subquery() {
    let schema = ecommerce_schema();
    let sql = "SELECT name FROM users WHERE id IN (SELECT user_id FROM orders)";
    let result = altimate_core::validate(sql, &schema).await;
    assert!(
        result.valid,
        "Subquery should be valid: {:?}",
        result.errors
    );
}

#[tokio::test]
async fn test_validate_aggregate() {
    let schema = ecommerce_schema();
    let sql = "SELECT user_id, SUM(total_amount) AS total FROM orders GROUP BY user_id";
    let result = altimate_core::validate(sql, &schema).await;
    assert!(
        result.valid,
        "Aggregate should be valid: {:?}",
        result.errors
    );
}

#[tokio::test]
async fn test_validate_cte() {
    let schema = ecommerce_schema();
    let sql =
        "WITH active AS (SELECT id, name FROM users WHERE status = 'active') SELECT * FROM active";
    let result = altimate_core::validate(sql, &schema).await;
    assert!(result.valid, "CTE should be valid: {:?}", result.errors);
}

#[tokio::test]
async fn test_validate_invalid_table() {
    let schema = ecommerce_schema();
    let sql = "SELECT id FROM nonexistent_table";
    let result = altimate_core::validate(sql, &schema).await;
    assert!(!result.valid);
    assert!(!result.errors.is_empty());
}

#[tokio::test]
async fn test_validate_invalid_column() {
    let schema = ecommerce_schema();
    let sql = "SELECT nonexistent_col FROM users";
    let result = altimate_core::validate(sql, &schema).await;
    assert!(!result.valid);
}
