// tests/tables_data/handlers/delete_table_data_test2.rs

// ========================================================================
// Foreign Key Integrity Tests
// ========================================================================

#[rstest]
#[tokio::test]
async fn test_soft_delete_does_not_break_foreign_key_references(
    #[future] advanced_delete_context: AdvancedDeleteContext,
) {
    // Arrange: Create a category and a product that links to it.
    let context = advanced_delete_context.await;
    let mut category_data = HashMap::new();
    category_data.insert("name".to_string(), string_to_proto_value("Electronics"));
    let category_req = PostTableDataRequest {
        profile_name: context.profile_name.clone(),
        table_name: context.category_table.clone(),
        data: category_data,
    };
    let category_res = post_table_data(&context.pool, category_req, &context.indexer_tx).await.unwrap();
    let category_id = category_res.inserted_id;

    let mut product_data = HashMap::new();
    product_data.insert("name".to_string(), string_to_proto_value("Laptop"));
    product_data.insert(
        format!("{}_id", context.category_table),
        Value { kind: Some(Kind::NumberValue(category_id as f64)) },
    );
    let product_req = PostTableDataRequest {
        profile_name: context.profile_name.clone(),
        table_name: context.product_table.clone(),
        data: product_data,
    };
    let product_res = post_table_data(&context.pool, product_req, &context.indexer_tx).await.unwrap();
    let product_id = product_res.inserted_id;

    // Act: Soft-delete the category record.
    let delete_req = DeleteTableDataRequest {
        profile_name: context.profile_name.clone(),
        table_name: context.category_table.clone(),
        record_id: category_id,
    };
    let delete_res = delete_table_data(&context.pool, delete_req).await.unwrap();
    assert!(delete_res.success);

    // Assert: The product record still exists and its foreign key still points to the (now soft-deleted) category ID.
    let query = format!(
        r#"SELECT "{}_id" FROM "{}"."{}" WHERE id = $1"#,
        context.category_table, context.profile_name, context.product_table
    );
    let fk_id: i64 = sqlx::query_scalar(&query)
        .bind(product_id)
        .fetch_one(&context.pool)
        .await
        .unwrap();

    assert_eq!(fk_id, category_id, "Foreign key reference should remain intact after soft delete.");
}

// ========================================================================
// Indexer Integration Tests
// ========================================================================

#[rstest]
#[tokio::test]
async fn test_delete_does_not_send_indexer_command(
    #[future] advanced_delete_context: AdvancedDeleteContext,
) {
    // Arrange
    let context = advanced_delete_context.await;
    let mut category_data = HashMap::new();
    category_data.insert("name".to_string(), string_to_proto_value("Test Category"));
    let category_req = PostTableDataRequest {
        profile_name: context.profile_name.clone(),
        table_name: context.category_table.clone(),
        data: category_data,
    };
    let category_res = post_table_data(&context.pool, category_req, &context.indexer_tx).await.unwrap();
    let category_id = category_res.inserted_id;

    // Drain the create command from the channel
    let _ = context.indexer_rx.lock().await.recv().await;

    // Act
    let delete_req = DeleteTableDataRequest {
        profile_name: context.profile_name.clone(),
        table_name: context.category_table.clone(),
        record_id: category_id,
    };
    
    let delete_res = delete_table_data(&context.pool, delete_req).await.unwrap();
    assert!(delete_res.success);

    // Assert: Check that NO command was sent. This verifies current behavior.
    let recv_result = tokio::time::timeout(
        std::time::Duration::from_millis(50),
        context.indexer_rx.lock().await.recv()
    ).await;

    assert!(recv_result.is_err(), "Expected no indexer command to be sent on delete, but one was received.");
}

// ========================================================================
// Concurrency and State Mismatch Tests
// ========================================================================

#[rstest]
#[tokio::test]
async fn test_concurrent_deletes_on_same_record(
    #[future] advanced_delete_context: AdvancedDeleteContext,
) {
    // Arrange
    let context = advanced_delete_context.await;
    let mut category_data = HashMap::new();
    category_data.insert("name".to_string(), string_to_proto_value("Concurrent Delete Test"));
    let category_req = PostTableDataRequest {
        profile_name: context.profile_name.clone(),
        table_name: context.category_table.clone(),
        data: category_data,
    };
    let category_res = post_table_data(&context.pool, category_req, &context.indexer_tx).await.unwrap();
    let category_id = category_res.inserted_id;

    // Act: Spawn multiple tasks to delete the same record.
    let mut tasks = vec![];
    for _ in 0..5 {
        let pool = context.pool.clone();
        let req = DeleteTableDataRequest {
            profile_name: context.profile_name.clone(),
            table_name: context.category_table.clone(),
            record_id: category_id,
        };
        tasks.push(tokio::spawn(async move {
            delete_table_data(&pool, req).await
        }));
    }
    let results = join_all(tasks).await;

    // Assert: Exactly one delete should succeed, the rest should fail (softly).
    let success_count = results.iter().filter(|res|
        res.is_ok() && res.as_ref().unwrap().is_ok() && res.as_ref().unwrap().as_ref().unwrap().success
    ).count();

    assert_eq!(success_count, 1, "Exactly one concurrent delete operation should succeed.");
}

#[rstest]
#[tokio::test]
async fn test_delete_fails_if_physical_table_is_missing(
    #[future] advanced_delete_context: AdvancedDeleteContext,
) {
    // Arrange: Create definitions, then manually drop the physical table to create a state mismatch.
    let context = advanced_delete_context.await;
    let qualified_table = format!("\"{}\".\"{}\"", context.profile_name, context.category_table);
    sqlx::query(&format!("DROP TABLE {} CASCADE", qualified_table))
        .execute(&context.pool)
        .await
        .unwrap();

    // Act: Attempt to delete a record from the logically-defined but physically-absent table.
    let delete_req = DeleteTableDataRequest {
        profile_name: context.profile_name.clone(),
        table_name: context.category_table.clone(),
        record_id: 1, // ID doesn't matter
    };
    let result = delete_table_data(&context.pool, delete_req).await;

    // Assert: The operation should fail with the specific internal error for a missing relation.
    assert!(result.is_err());
    let err = result.unwrap_err();
    assert_eq!(err.code(), tonic::Code::Internal);
    assert!(
        err.message().contains("is defined but does not physically exist"),
        "Error message should indicate a state mismatch."
    );
}

// ========================================================================
// Interaction with Other Endpoints
// ========================================================================

#[rstest]
#[tokio::test]
async fn test_update_succeeds_on_soft_deleted_record(
    #[future] advanced_delete_context: AdvancedDeleteContext,
) {
    // Arrange: Create and then soft-delete a record.
    let context = advanced_delete_context.await;
    let mut category_data = HashMap::new();
    category_data.insert("name".to_string(), string_to_proto_value("Original Name"));
    let category_req = PostTableDataRequest {
        profile_name: context.profile_name.clone(),
        table_name: context.category_table.clone(),
        data: category_data,
    };
    let category_res = post_table_data(&context.pool, category_req, &context.indexer_tx).await.unwrap();
    let category_id = category_res.inserted_id;

    let delete_req = DeleteTableDataRequest {
        profile_name: context.profile_name.clone(),
        table_name: context.category_table.clone(),
        record_id: category_id,
    };
    delete_table_data(&context.pool, delete_req).await.unwrap();

    // Act: Attempt to update the soft-deleted record using the PUT handler.
    let mut update_data = HashMap::new();
    update_data.insert("name".to_string(), string_to_proto_value("Updated After Delete"));
    let put_req = PutTableDataRequest {
        profile_name: context.profile_name.clone(),
        table_name: context.category_table.clone(),
        id: category_id,
        data: update_data,
    };
    let put_result = put_table_data(&context.pool, put_req, &context.indexer_tx).await;

    // Assert: This test is crucial as it verifies your requirement to "freeze operations".
    // Currently, the PUT handler does NOT check the deleted flag, so it will succeed.
    // This test documents that behavior. To make it fail, you would need to add a check
    // in `put_table_data` to see if the record is already deleted.
    assert!(put_result.is_ok(), "PUT should succeed on a soft-deleted record (current behavior).");
    let put_res = put_result.unwrap();
    assert!(put_res.success);

    // Verify the name was updated, but the record remains marked as deleted.
    let row = sqlx::query(&format!(
        r#"SELECT name, deleted FROM "{}"."{}" WHERE id = $1"#,
        context.profile_name, context.category_table
    ))
    .bind(category_id)
    .fetch_one(&context.pool)
    .await
    .unwrap();

    let name: String = row.get("name");
    let deleted: bool = row.get("deleted");

    assert_eq!(name, "Updated After Delete");
    assert!(deleted, "Record should remain soft-deleted after an update.");
}
