// tests/table_definition/post_table_definition_test3.rs

// NOTE: All 'use' statements have been removed from this file.
// They are inherited from the parent file that includes this one.

// ========= Helper Functions for this Test File =========

/// Checks that a table definition does NOT exist for a given profile and table name.
async fn assert_table_definition_does_not_exist(pool: &PgPool, profile_name: &str, table_name: &str) {
    let count: i64 = sqlx::query_scalar!(
        "SELECT COUNT(*) FROM table_definitions td
         JOIN schemas p ON td.schema_id = p.id
         WHERE p.name = $1 AND td.table_name = $2",
        profile_name,
        table_name
    )
    .fetch_one(pool)
    .await
    .expect("Failed to query for table definition")
    .unwrap_or(0);

    assert_eq!(
        count, 0,
        "Table definition for '{}/{}' was found but should have been rolled back.",
        profile_name, table_name
    );
}

// ========= Category 2: Advanced Identifier and Naming Collisions =========

#[rstest]
#[tokio::test]
async fn test_fail_on_column_name_collision_with_fk(#[future] pool: PgPool) {
    let pool = pool.await;

    // Use a unique table name to avoid conflicts with other tests
    let unique_id = std::time::SystemTime::now()
        .duration_since(std::time::UNIX_EPOCH)
        .unwrap()
        .subsec_nanos();
    let customers_table = format!("customers_collision_{}", unique_id);
    let orders_table = format!("orders_collision_{}", unique_id);

    // First, create the prerequisite table using the proper API
    let customers_request = PostTableDefinitionRequest {
        profile_name: "default".into(),
        table_name: customers_table.clone(),
        columns: vec![ColumnDefinition {
            name: "name".into(),
            field_type: "text".into(),
        }],
        links: vec![],
        indexes: vec![],
    };

    // Create the customers table
    let customers_result = post_table_definition(&pool, customers_request).await;
    assert!(customers_result.is_ok(), "Failed to create prerequisite customers table: {:?}", customers_result);

    // Now test the collision scenario
    // This should fail because we're trying to create a "customers_collision_xxxxx_id" column
    // while also linking to the table (which auto-generates the same foreign key)
    let fk_column_name = format!("{}_id", customers_table);
    let request = PostTableDefinitionRequest {
        profile_name: "default".into(),
        table_name: orders_table,
        columns: vec![ColumnDefinition {
            name: fk_column_name.clone(), // This will collide with the generated FK
            field_type: "integer".into(),
        }],
        links: vec![TableLink {
            linked_table_name: customers_table,
            required: true,
        }],
        indexes: vec![],
    };

    // Act
    let result = post_table_definition(&pool, request).await;

    // Assert - this should now fail with InvalidArgument because of the column name validation
    let err = result.unwrap_err();
    assert_eq!(
        err.code(),
        Code::InvalidArgument,
        "Expected InvalidArgument due to column name ending in _id, got: {:?}",
        err
    );

    // FIXED: More flexible error message check
    assert!(
        err.message().contains("Column name") &&
        err.message().contains("cannot be") &&
        err.message().contains("end with '_id'"),
        "Error message should mention the invalid column name: {}",
        err.message()
    );
}

#[rstest]
#[tokio::test]
async fn test_fail_on_duplicate_column_names_in_request(#[future] pool: PgPool) {
    // Scenario: The request itself contains two columns with the same name.
    // Expected: Database error on CREATE TABLE with duplicate column definition.
    let pool = pool.await;
    let request = PostTableDefinitionRequest {
        profile_name: "default".into(),
        table_name: "duplicate_cols".into(),
        columns: vec![
            ColumnDefinition {
                name: "product_name".into(),
                field_type: "text".into(),
            },
            ColumnDefinition {
                name: "product_name".into(),
                field_type: "text".into(),
            },
        ],
        ..Default::default()
    };

    // Act
    let result = post_table_definition(&pool, request).await;

    // Assert
    let err = result.unwrap_err();
    assert_eq!(err.code(), Code::Internal);
}

#[rstest]
#[tokio::test]
async fn test_link_to_sanitized_table_name(#[future] pool: PgPool) {
    let pool = pool.await;

    // FIXED: Use valid table name instead of invalid one
    let table_name = "my_invoices";

    // 1. Create the table with a VALID name
    let create_req = PostTableDefinitionRequest {
        profile_name: "default".into(),
        table_name: table_name.into(),
        columns: vec![ColumnDefinition {
            name: "amount".into(),
            field_type: "text".into(),
        }],
        ..Default::default()
    };
    let resp = post_table_definition(&pool, create_req).await.unwrap();
    assert!(resp.sql.contains(&format!("\"default\".\"{}\"", table_name)));

    // 2. Link to the correct name - should succeed
    let link_req_success = PostTableDefinitionRequest {
        profile_name: "default".into(),
        table_name: "payments".into(),
        columns: vec![ColumnDefinition {
            name: "amount".into(),
            field_type: "text".into(),
        }],
        links: vec![TableLink {
            linked_table_name: table_name.into(),
            required: true,
        }],
        ..Default::default()
    };
    let success_resp = post_table_definition(&pool, link_req_success).await.unwrap();
    assert!(success_resp.success);
}

// ========= Category 3: Complex Link and Profile Logic =========

#[rstest]
#[tokio::test]
async fn test_fail_on_true_self_referential_link(#[future] pool: PgPool) {
    // Scenario: A table attempts to link to itself in the same request.
    // Expected: NotFound, because the table definition doesn't exist yet at link-check time.
    let pool = pool.await;
    let request = PostTableDefinitionRequest {
        profile_name: "default".into(),
        table_name: "employees".into(),
        links: vec![TableLink {
            linked_table_name: "employees".into(), // Self-reference
            required: false,                       // For a manager_id FK
        }],
        ..Default::default()
    };

    // Act
    let result = post_table_definition(&pool, request).await;

    // Assert
    let err = result.unwrap_err();
    assert_eq!(err.code(), Code::NotFound);
    assert!(err.message().contains("Linked table employees not found"));
}

#[rstest]
#[tokio::test]
async fn test_behavior_on_empty_profile_name(#[future] pool: PgPool) {
    // Scenario: Attempt to create a table with an empty profile name.
    // Expected: This should be rejected by input validation.
    let pool = pool.await;
    let request = PostTableDefinitionRequest {
        profile_name: "".into(),
        table_name: "table_in_empty_profile".into(),
        ..Default::default()
    };
    // Act
    let result = post_table_definition(&pool, request).await;
    // Assert
    let err = result.unwrap_err();
    assert_eq!(
        err.code(),
        Code::InvalidArgument,  // Changed from Internal
        "Expected InvalidArgument error from input validation"
    );
    assert!(
        err.message().contains("Profile name cannot be empty"),  // Updated message
        "Unexpected error message: {}",
        err.message()
    );
}

// ========= Category 4: Concurrency =========

#[rstest]
#[tokio::test]
async fn test_race_condition_on_table_creation(#[future] pool: PgPool) {
    let pool = pool.await;

    // FIXED: Use unique profile and table names to avoid conflicts between test runs
    let unique_id = std::time::SystemTime::now()
        .duration_since(std::time::UNIX_EPOCH)
        .unwrap()
        .as_nanos();

    let request1 = PostTableDefinitionRequest {
        profile_name: format!("concurrent_profile_{}", unique_id),
        table_name: "racy_table".into(),
        columns: vec![ColumnDefinition {
            name: "test_col".into(),
            field_type: "text".into(),
        }],
        ..Default::default()
    };
    let request2 = request1.clone();

    let pool1 = pool.clone();
    let pool2 = pool.clone();

    // Act
    let (res1, res2) = tokio::join!(
        post_table_definition(&pool1, request1),
        post_table_definition(&pool2, request2)
    );

    // Assert
    let results = vec![res1, res2];
    let success_count = results.iter().filter(|r| r.is_ok()).count();
    let failure_count = results.iter().filter(|r| r.is_err()).count();

    assert_eq!(
        success_count, 1,
        "Exactly one request should succeed"
    );
    assert_eq!(failure_count, 1, "Exactly one request should fail");

    let err = results
        .into_iter()
        .find(|r| r.is_err())
        .unwrap()
        .unwrap_err();
    assert_eq!(err.code(), Code::AlreadyExists);
    assert_eq!(err.message(), "Table already exists in this profile");
}
