// tests/tables_data/handlers/delete_table_data_test3.rs

// ========================================================================
// Input Validation and Edge Cases
// ========================================================================

#[rstest]
#[tokio::test]
async fn test_delete_with_negative_record_id(
    #[future] existing_table: (PgPool, String, i64, String),
) {
    let (pool, profile_name, _, table_name) = existing_table.await;
    let request = DeleteTableDataRequest {
        profile_name,
        table_name,
        record_id: -1,
    };
    let response = delete_table_data(&pool, request).await.unwrap();
    assert!(!response.success, "Delete with negative ID should fail gracefully");
}

#[rstest]
#[tokio::test]
async fn test_delete_with_zero_record_id(
    #[future] existing_table: (PgPool, String, i64, String),
) {
    let (pool, profile_name, _, table_name) = existing_table.await;
    let request = DeleteTableDataRequest {
        profile_name,
        table_name,
        record_id: 0,
    };
    let response = delete_table_data(&pool, request).await.unwrap();
    assert!(!response.success, "Delete with zero ID should fail gracefully");
}

#[rstest]
#[tokio::test]
async fn test_delete_with_max_int64_record_id(
    #[future] existing_table: (PgPool, String, i64, String),
) {
    let (pool, profile_name, _, table_name) = existing_table.await;
    let request = DeleteTableDataRequest {
        profile_name,
        table_name,
        record_id: i64::MAX,
    };
    let response = delete_table_data(&pool, request).await.unwrap();
    assert!(!response.success, "Delete with max int64 ID should fail gracefully");
}

// ========================================================================
// Malformed Input Handling
// ========================================================================

#[rstest]
#[tokio::test]
async fn test_delete_with_empty_profile_name(#[future] pool: PgPool) {
    let pool = pool.await;
    let request = DeleteTableDataRequest {
        profile_name: "".to_string(),
        table_name: "test_table".to_string(),
        record_id: 1,
    };
    let result = delete_table_data(&pool, request).await;
    assert!(result.is_err(), "Empty profile name should be rejected");
    assert_eq!(result.unwrap_err().code(), tonic::Code::NotFound);
}

#[rstest]
#[tokio::test]
async fn test_delete_with_whitespace_only_profile_name(#[future] pool: PgPool) {
    let pool = pool.await;
    let request = DeleteTableDataRequest {
        profile_name: "   ".to_string(),
        table_name: "test_table".to_string(),
        record_id: 1,
    };
    let result = delete_table_data(&pool, request).await;
    assert!(result.is_err(), "Whitespace-only profile name should be rejected");
    assert_eq!(result.unwrap_err().code(), tonic::Code::NotFound);
}

#[rstest]
#[tokio::test]
async fn test_delete_with_empty_table_name(
    #[future] existing_profile: (PgPool, String, i64),
) {
    let (pool, profile_name, _) = existing_profile.await;
    let request = DeleteTableDataRequest {
        profile_name,
        table_name: "".to_string(),
        record_id: 1,
    };
    let result = delete_table_data(&pool, request).await;
    assert!(result.is_err(), "Empty table name should be rejected");
    assert_eq!(result.unwrap_err().code(), tonic::Code::NotFound);
}

#[rstest]
#[tokio::test]
async fn test_delete_with_sql_injection_attempt(
    #[future] existing_profile: (PgPool, String, i64),
) {
    let (pool, profile_name, _) = existing_profile.await;
    let request = DeleteTableDataRequest {
        profile_name,
        table_name: "test'; DROP TABLE users; --".to_string(),
        record_id: 1,
    };
    let result = delete_table_data(&pool, request).await;
    assert!(result.is_err(), "SQL injection attempt should be rejected");
    assert_eq!(result.unwrap_err().code(), tonic::Code::NotFound);
}

// ========================================================================
// Data Integrity Verification Tests
// ========================================================================

#[rstest]
#[tokio::test]
async fn test_delete_only_affects_target_record(
    #[future] existing_table: (PgPool, String, i64, String),
) {
    // Arrange: Create multiple records
    let (pool, profile_name, _, table_name) = existing_table.await;
    
    let mut record_ids = Vec::new();
    for i in 0..5 {
        let query = format!(
            "INSERT INTO \"{}\".\"{}\" (deleted) VALUES (false) RETURNING id",
            profile_name, table_name
        );
        let row = sqlx::query(&query).fetch_one(&pool).await.unwrap();
        let id: i64 = row.get("id");
        record_ids.push(id);
    }
    
    let target_id = record_ids[2]; // Delete the middle record
    
    // Act: Delete one specific record
    let request = DeleteTableDataRequest {
        profile_name: profile_name.clone(),
        table_name: table_name.clone(),
        record_id: target_id,
    };
    let response = delete_table_data(&pool, request).await.unwrap();
    assert!(response.success);
    
    // Assert: Verify only the target record is deleted
    for &id in &record_ids {
        let query = format!(
            "SELECT deleted FROM \"{}\".\"{}\" WHERE id = $1",
            profile_name, table_name
        );
        let row = sqlx::query(&query).bind(id).fetch_one(&pool).await.unwrap();
        let is_deleted: bool = row.get("deleted");
        
        if id == target_id {
            assert!(is_deleted, "Target record should be marked as deleted");
        } else {
            assert!(!is_deleted, "Non-target records should remain undeleted");
        }
    }
}

#[rstest]
#[tokio::test]
async fn test_delete_preserves_all_other_fields(
    #[future] advanced_delete_context: AdvancedDeleteContext,
) {
    // Arrange: Create a record with rich data
    let context = advanced_delete_context.await;
    let mut category_data = HashMap::new();
    category_data.insert("name".to_string(), string_to_proto_value("Preserve 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;
    
    // Capture state before deletion
    let before_query = format!(
        "SELECT id, name, deleted, created_at FROM \"{}\".\"{}\" WHERE id = $1",
        context.profile_name, context.category_table
    );
    let before_row = sqlx::query(&before_query)
        .bind(category_id)
        .fetch_one(&context.pool)
        .await
        .unwrap();
    
    let before_id: i64 = before_row.get("id");
    let before_name: String = before_row.get("name");
    let before_deleted: bool = before_row.get("deleted");
    let before_created_at: chrono::DateTime<chrono::Utc> = before_row.get("created_at");
    
    // Act: Delete the 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: Verify only 'deleted' field changed
    let after_query = format!(
        "SELECT id, name, deleted, created_at FROM \"{}\".\"{}\" WHERE id = $1",
        context.profile_name, context.category_table
    );
    let after_row = sqlx::query(&after_query)
        .bind(category_id)
        .fetch_one(&context.pool)
        .await
        .unwrap();
    
    let after_id: i64 = after_row.get("id");
    let after_name: String = after_row.get("name");
    let after_deleted: bool = after_row.get("deleted");
    let after_created_at: chrono::DateTime<chrono::Utc> = after_row.get("created_at");
    
    assert_eq!(before_id, after_id, "ID should not change");
    assert_eq!(before_name, after_name, "Name should not change");
    assert_eq!(before_created_at, after_created_at, "Created timestamp should not change");
    assert_eq!(before_deleted, false, "Record should initially be not deleted");
    assert_eq!(after_deleted, true, "Record should be marked as deleted after operation");
}

#[rstest]
#[tokio::test]
async fn test_delete_count_verification(
    #[future] existing_table: (PgPool, String, i64, String),
) {
    // Arrange: Create records and count them
    let (pool, profile_name, _, table_name) = existing_table.await;
    
    // Create 3 records
    let mut record_ids = Vec::new();
    for _ in 0..3 {
        let query = format!(
            "INSERT INTO \"{}\".\"{}\" (deleted) VALUES (false) RETURNING id",
            profile_name, table_name
        );
        let row = sqlx::query(&query).fetch_one(&pool).await.unwrap();
        let id: i64 = row.get("id");
        record_ids.push(id);
    }
    
    // Verify initial count
    let count_query = format!(
        "SELECT COUNT(*) as total, COUNT(*) FILTER (WHERE deleted = false) as active FROM \"{}\".\"{}\"",
        profile_name, table_name
    );
    let count_row = sqlx::query(&count_query).fetch_one(&pool).await.unwrap();
    let initial_total: i64 = count_row.get("total");
    let initial_active: i64 = count_row.get("active");
    
    // Act: Delete one record
    let request = DeleteTableDataRequest {
        profile_name: profile_name.clone(),
        table_name: table_name.clone(),
        record_id: record_ids[0],
    };
    let response = delete_table_data(&pool, request).await.unwrap();
    assert!(response.success);
    
    // Assert: Verify counts after deletion
    let final_count_row = sqlx::query(&count_query).fetch_one(&pool).await.unwrap();
    let final_total: i64 = final_count_row.get("total");
    let final_active: i64 = final_count_row.get("active");
    
    assert_eq!(initial_total, final_total, "Total record count should not change (soft delete)");
    assert_eq!(initial_active - 1, final_active, "Active record count should decrease by 1");
}

// ========================================================================
// Multiple Operations Sequence Testing
// ========================================================================

#[rstest]
#[tokio::test]
async fn test_delete_then_post_same_data(
    #[future] advanced_delete_context: AdvancedDeleteContext,
) {
    // Arrange: Create and 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("Reusable Name"));
    
    let category_req = PostTableDataRequest {
        profile_name: context.profile_name.clone(),
        table_name: context.category_table.clone(),
        data: category_data.clone(),
    };
    let first_res = post_table_data(&context.pool, category_req, &context.indexer_tx).await.unwrap();
    let first_id = first_res.inserted_id;
    
    let delete_req = DeleteTableDataRequest {
        profile_name: context.profile_name.clone(),
        table_name: context.category_table.clone(),
        record_id: first_id,
    };
    delete_table_data(&context.pool, delete_req).await.unwrap();
    
    // Act: Try to POST the same data again
    let second_req = PostTableDataRequest {
        profile_name: context.profile_name.clone(),
        table_name: context.category_table.clone(),
        data: category_data,
    };
    let second_res = post_table_data(&context.pool, second_req, &context.indexer_tx).await.unwrap();
    
    // Assert: Should succeed with a new ID
    assert!(second_res.success);
    assert_ne!(first_id, second_res.inserted_id, "New record should have different ID");
    
    // Verify both records exist in database
    let count_query = format!(
        "SELECT COUNT(*) as total FROM \"{}\".\"{}\" WHERE name = 'Reusable Name'",
        context.profile_name, context.category_table
    );
    let count: i64 = sqlx::query_scalar(&count_query).fetch_one(&context.pool).await.unwrap();
    assert_eq!(count, 2, "Should have 2 records with same name (one deleted, one active)");
}

#[rstest]
#[tokio::test]
async fn test_multiple_deletes_then_recreate_pattern(
    #[future] advanced_delete_context: AdvancedDeleteContext,
) {
    // Test a realistic pattern: create, delete, recreate multiple times
    let context = advanced_delete_context.await;
    let mut all_ids = Vec::new();
    
    for i in 0..3 {
        // Create
        let mut category_data = HashMap::new();
        category_data.insert("name".to_string(), string_to_proto_value(&format!("Cycle Name {}", i)));
        
        let create_req = PostTableDataRequest {
            profile_name: context.profile_name.clone(),
            table_name: context.category_table.clone(),
            data: category_data,
        };
        let create_res = post_table_data(&context.pool, create_req, &context.indexer_tx).await.unwrap();
        all_ids.push(create_res.inserted_id);
        
        // Delete immediately
        let delete_req = DeleteTableDataRequest {
            profile_name: context.profile_name.clone(),
            table_name: context.category_table.clone(),
            record_id: create_res.inserted_id,
        };
        let delete_res = delete_table_data(&context.pool, delete_req).await.unwrap();
        assert!(delete_res.success);
    }
    
    // Verify all records are marked as deleted
    for &id in &all_ids {
        let query = format!(
            "SELECT deleted FROM \"{}\".\"{}\" WHERE id = $1",
            context.profile_name, context.category_table
        );
        let is_deleted: bool = sqlx::query_scalar(&query)
            .bind(id)
            .fetch_one(&context.pool)
            .await
            .unwrap();
        assert!(is_deleted, "Record {} should be deleted", id);
    }
}

// ========================================================================
// Performance and Stress Tests
// ========================================================================

#[rstest]
#[tokio::test]
async fn test_delete_performance_with_many_records(
    #[future] advanced_delete_context: AdvancedDeleteContext,
) {
    // Arrange: Create many records
    let context = advanced_delete_context.await;
    let record_count = 100; // Adjust based on test environment
    let mut record_ids = Vec::new();
    
    for i in 0..record_count {
        let mut category_data = HashMap::new();
        category_data.insert("name".to_string(), string_to_proto_value(&format!("Perf Test {}", i)));
        
        let create_req = PostTableDataRequest {
            profile_name: context.profile_name.clone(),
            table_name: context.category_table.clone(),
            data: category_data,
        };
        let create_res = post_table_data(&context.pool, create_req, &context.indexer_tx).await.unwrap();
        record_ids.push(create_res.inserted_id);
    }
    
    // Act: Delete a record from the middle (worst case for performance)
    let target_id = record_ids[record_count / 2];
    let start_time = std::time::Instant::now();
    
    let delete_req = DeleteTableDataRequest {
        profile_name: context.profile_name.clone(),
        table_name: context.category_table.clone(),
        record_id: target_id,
    };
    let delete_res = delete_table_data(&context.pool, delete_req).await.unwrap();
    
    let elapsed = start_time.elapsed();
    
    // Assert: Operation should succeed and be reasonably fast
    assert!(delete_res.success);
    assert!(elapsed.as_millis() < 1000, "Delete should complete within 1 second even with {} records", record_count);
}

#[rstest]
#[tokio::test]
async fn test_rapid_sequential_deletes(
    #[future] advanced_delete_context: AdvancedDeleteContext,
) {
    // Arrange: Create multiple records
    let context = advanced_delete_context.await;
    let mut record_ids = Vec::new();
    
    for i in 0..10 {
        let mut category_data = HashMap::new();
        category_data.insert("name".to_string(), string_to_proto_value(&format!("Rapid Delete {}", i)));
        
        let create_req = PostTableDataRequest {
            profile_name: context.profile_name.clone(),
            table_name: context.category_table.clone(),
            data: category_data,
        };
        let create_res = post_table_data(&context.pool, create_req, &context.indexer_tx).await.unwrap();
        record_ids.push(create_res.inserted_id);
    }
    
    // Act: Delete all records rapidly in sequence
    let start_time = std::time::Instant::now();
    for &record_id in &record_ids {
        let delete_req = DeleteTableDataRequest {
            profile_name: context.profile_name.clone(),
            table_name: context.category_table.clone(),
            record_id,
        };
        let delete_res = delete_table_data(&context.pool, delete_req).await.unwrap();
        assert!(delete_res.success, "Delete of record {} should succeed", record_id);
    }
    let elapsed = start_time.elapsed();
    
    // Assert: All deletes should complete in reasonable time
    assert!(elapsed.as_millis() < 5000, "10 sequential deletes should complete within 5 seconds");
    
    // Verify all records are deleted
    let count_query = format!(
        "SELECT COUNT(*) FILTER (WHERE deleted = true) as deleted_count FROM \"{}\".\"{}\"",
        context.profile_name, context.category_table
    );
    let deleted_count: i64 = sqlx::query_scalar(&count_query).fetch_one(&context.pool).await.unwrap();
    assert_eq!(deleted_count as usize, record_ids.len(), "All records should be marked as deleted");
}

// ========================================================================
// Error Message Quality and Handling Tests
// ========================================================================

#[rstest]
#[tokio::test]
async fn test_error_messages_are_descriptive(#[future] pool: PgPool) {
    let pool = pool.await;
    
    // Test profile not found error
    let request = DeleteTableDataRequest {
        profile_name: "NonExistentProfile123".to_string(),
        table_name: "test_table".to_string(),
        record_id: 1,
    };
    let result = delete_table_data(&pool, request).await;
    assert!(result.is_err());
    let error = result.unwrap_err();
    assert_eq!(error.code(), tonic::Code::NotFound);
    assert_eq!(error.message(), "Profile not found");
}

#[rstest]
#[tokio::test]
async fn test_table_not_found_error_message(
    #[future] existing_profile: (PgPool, String, i64),
) {
    let (pool, profile_name, _) = existing_profile.await;
    let request = DeleteTableDataRequest {
        profile_name,
        table_name: "definitely_does_not_exist_12345".to_string(),
        record_id: 1,
    };
    let result = delete_table_data(&pool, request).await;
    assert!(result.is_err());
    let error = result.unwrap_err();
    assert_eq!(error.code(), tonic::Code::NotFound);
    assert_eq!(error.message(), "Table not found in profile");
}

// ========================================================================
// Database State Consistency Tests
// ========================================================================

#[rstest]
#[tokio::test]
async fn test_delete_maintains_foreign_key_constraints(
    #[future] advanced_delete_context: AdvancedDeleteContext,
) {
    // This test ensures that soft deletes don't interfere with FK constraint validation
    let context = advanced_delete_context.await;
    
    // Create category
    let mut category_data = HashMap::new();
    category_data.insert("name".to_string(), string_to_proto_value("FK 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;
    
    // Create product referencing the category
    let mut product_data = HashMap::new();
    product_data.insert("name".to_string(), string_to_proto_value("FK Test Product"));
    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();
    
    // Soft delete the category
    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);
    
    // The product should still exist and reference the soft-deleted category
    let fk_query = format!(
        "SELECT \"{}_id\" FROM \"{}\".\"{}\" WHERE id = $1",
        context.category_table, context.profile_name, context.product_table
    );
    let fk_value: i64 = sqlx::query_scalar(&fk_query)
        .bind(product_res.inserted_id)
        .fetch_one(&context.pool)
        .await
        .unwrap();
    
    assert_eq!(fk_value, category_id, "Foreign key should still point to soft-deleted category");
}
