// tests/tables_data/handlers/post_table_data_test3.rs

// ========================================================================
// ADDITIONAL HELPER FUNCTIONS FOR TEST3
// ========================================================================

// Helper to create different Value types
fn create_string_value(s: &str) -> Value {
    Value { kind: Some(Kind::StringValue(s.to_string())) }
}

fn create_number_value(n: f64) -> Value {
    Value { kind: Some(Kind::NumberValue(n)) }
}

fn create_bool_value(b: bool) -> Value {
    Value { kind: Some(Kind::BoolValue(b)) }
}

fn create_null_value() -> Value {
    Value { kind: Some(Kind::NullValue(0)) }
}

// ========================================================================
// FIXTURES AND CONTEXT SETUP FOR ADVANCED TESTS
// ========================================================================

#[derive(Clone)]
struct DataTypeTestContext {
    pool: PgPool,
    profile_name: String,
    table_name: String,
}

#[derive(Clone)]
struct ForeignKeyTestContext {
    pool: PgPool,
    profile_name: String,
    category_table: String,
    product_table: String,
    order_table: String,
}

// Create a table with various data types for comprehensive testing
async fn create_data_type_test_table(pool: &PgPool, table_name: &str, profile_name: &str) -> Result<(), tonic::Status> {
    let table_def_request = PostTableDefinitionRequest {
        profile_name: profile_name.into(),
        table_name: table_name.into(),
        columns: vec![
            TableColumnDefinition { name: "my_text".into(), field_type: "text".into() },
            TableColumnDefinition { name: "my_bool".into(), field_type: "boolean".into() },
            TableColumnDefinition { name: "my_timestamp".into(), field_type: "timestamp".into() },
            TableColumnDefinition { name: "my_bigint".into(), field_type: "integer".into() },
            TableColumnDefinition { name: "my_money".into(), field_type: "decimal(19,4)".into() },
            TableColumnDefinition { name: "my_date".into(), field_type: "date".into() },
            TableColumnDefinition { name: "my_decimal".into(), field_type: "decimal(10,2)".into() },
        ],
        indexes: vec![],
        links: vec![],
    };

    post_table_definition(pool, table_def_request).await?;
    Ok(())
}

// Create foreign key test tables (category -> product -> order)
async fn create_foreign_key_test_tables(pool: &PgPool, profile_name: &str, category_table: &str, product_table: &str, order_table: &str) -> Result<(), tonic::Status> {
    // Create category table first (no dependencies)
    let category_def = PostTableDefinitionRequest {
        profile_name: profile_name.into(),
        table_name: category_table.into(),
        columns: vec![
            TableColumnDefinition { name: "name".into(), field_type: "text".into() },
            TableColumnDefinition { name: "description".into(), field_type: "text".into() },
        ],
        indexes: vec![],
        links: vec![],
    };
    post_table_definition(pool, category_def).await?;

    // Create product table with required link to category
    let product_def = PostTableDefinitionRequest {
        profile_name: profile_name.into(),
        table_name: product_table.into(),
        columns: vec![
            TableColumnDefinition { name: "name".into(), field_type: "text".into() },
            TableColumnDefinition { name: "price".into(), field_type: "decimal(10,2)".into() },
        ],
        indexes: vec![],
        links: vec![
            TableLink { linked_table_name: category_table.into(), required: true },
        ],
    };
    post_table_definition(pool, product_def).await?;

    // Create order table with required link to product and optional link to category
    let order_def = PostTableDefinitionRequest {
        profile_name: profile_name.into(),
        table_name: order_table.into(),
        columns: vec![
            TableColumnDefinition { name: "quantity".into(), field_type: "integer".into() },
            TableColumnDefinition { name: "notes".into(), field_type: "text".into() },
        ],
        indexes: vec![],
        links: vec![
            TableLink { linked_table_name: product_table.into(), required: true },
            TableLink { linked_table_name: category_table.into(), required: false }, // Optional link
        ],
    };
    post_table_definition(pool, order_def).await?;

    Ok(())
}

#[fixture]
async fn data_type_test_context() -> DataTypeTestContext {
    let pool = setup_test_db().await;
    let unique_id = generate_unique_id();
    let profile_name = format!("dtype_profile_{}", unique_id);
    let table_name = format!("dtype_table_{}", unique_id);

    create_data_type_test_table(&pool, &table_name, &profile_name).await
        .expect("Failed to create data type test table");

    DataTypeTestContext { pool, profile_name, table_name }
}

#[fixture]
async fn foreign_key_test_context() -> ForeignKeyTestContext {
    let pool = setup_test_db().await;
    let unique_id = generate_unique_id();
    let profile_name = format!("fk_profile_{}", unique_id);
    let category_table = format!("category_{}", unique_id);
    let product_table = format!("product_{}", unique_id);
    let order_table = format!("order_{}", unique_id);

    create_foreign_key_test_tables(&pool, &profile_name, &category_table, &product_table, &order_table).await
        .expect("Failed to create foreign key test tables");

    ForeignKeyTestContext { pool, profile_name, category_table, product_table, order_table }
}

// ========================================================================
// DATA TYPE VALIDATION TESTS
// ========================================================================


#[rstest]
#[tokio::test]
async fn test_correct_data_types_success(#[future] data_type_test_context: DataTypeTestContext) {
    let context = data_type_test_context.await;
    let indexer_tx = create_test_indexer_channel().await;
    let mut data = HashMap::new();
    data.insert("my_text".into(), create_string_value("Test String"));
    data.insert("my_bool".into(), create_bool_value(true));
    data.insert("my_timestamp".into(), create_string_value("2024-01-15T10:30:00Z"));
    data.insert("my_bigint".into(), create_number_value(42.0));
    data.insert("my_money".into(), create_string_value("123.45")); // Use string for decimal
    data.insert("my_decimal".into(), create_string_value("999.99")); // Use string for decimal
    let request = PostTableDataRequest {
        profile_name: context.profile_name.clone(),
        table_name: context.table_name.clone(),
        data,
    };
    let response = post_table_data(&context.pool, request, &indexer_tx).await.unwrap();
    assert!(response.success);
    assert!(response.inserted_id > 0);

    // Verify data was stored correctly
    let query = format!(
        r#"SELECT my_text, my_bool, my_timestamp, my_bigint FROM "{}"."{}" WHERE id = $1"#,
        context.profile_name, context.table_name
    );
    let row = sqlx::query(&query)
        .bind(response.inserted_id)
        .fetch_one(&context.pool)
        .await
        .unwrap();

    let stored_text: String = row.get("my_text");
    let stored_bool: bool = row.get("my_bool");
    // Change this based on your actual column type in the schema:
    // If my_bigint is defined as "integer" in table definition, use i32:
    let stored_bigint: i32 = row.get("my_bigint");
    // If my_bigint is defined as "biginteger" or "bigint" in table definition, use i64:
    // let stored_bigint: i64 = row.get("my_bigint");

    assert_eq!(stored_text, "Test String");
    assert_eq!(stored_bool, true);
    assert_eq!(stored_bigint, 42);
}

#[rstest]
#[tokio::test]
async fn test_type_mismatch_string_for_boolean(#[future] data_type_test_context: DataTypeTestContext) {
    let context = data_type_test_context.await;
    let indexer_tx = create_test_indexer_channel().await;

    let mut data = HashMap::new();
    data.insert("my_text".into(), create_string_value("Required field"));
    data.insert("my_bool".into(), create_string_value("true")); // String instead of boolean

    let request = PostTableDataRequest {
        profile_name: context.profile_name.clone(),
        table_name: context.table_name.clone(),
        data,
    };

    let result = post_table_data(&context.pool, request, &indexer_tx).await;
    assert!(result.is_err());

    if let Err(err) = result {
        assert_eq!(err.code(), tonic::Code::InvalidArgument);
        assert!(err.message().contains("Expected boolean for column 'my_bool'"));
    }
}

#[rstest]
#[tokio::test]
async fn test_type_mismatch_string_for_integer(#[future] data_type_test_context: DataTypeTestContext) {
    let context = data_type_test_context.await;
    let indexer_tx = create_test_indexer_channel().await;

    let mut data = HashMap::new();
    data.insert("my_text".into(), create_string_value("Required field"));
    data.insert("my_bigint".into(), create_string_value("42")); // String instead of number

    let request = PostTableDataRequest {
        profile_name: context.profile_name.clone(),
        table_name: context.table_name.clone(),
        data,
    };

    let result = post_table_data(&context.pool, request, &indexer_tx).await;
    assert!(result.is_err());

    if let Err(err) = result {
        assert_eq!(err.code(), tonic::Code::InvalidArgument);
        assert!(err.message().contains("Expected number for column 'my_bigint'"));
    }
}

#[rstest]
#[tokio::test]
async fn test_type_mismatch_number_for_boolean(#[future] data_type_test_context: DataTypeTestContext) {
    let context = data_type_test_context.await;
    let indexer_tx = create_test_indexer_channel().await;

    let mut data = HashMap::new();
    data.insert("my_text".into(), create_string_value("Required field"));
    data.insert("my_bool".into(), create_number_value(1.0)); // Number instead of boolean

    let request = PostTableDataRequest {
        profile_name: context.profile_name.clone(),
        table_name: context.table_name.clone(),
        data,
    };

    let result = post_table_data(&context.pool, request, &indexer_tx).await;
    assert!(result.is_err());

    if let Err(err) = result {
        assert_eq!(err.code(), tonic::Code::InvalidArgument);
        assert!(err.message().contains("Expected boolean for column 'my_bool'"));
    }
}

#[rstest]
#[tokio::test]
async fn test_decimal_requires_string_not_number(#[future] data_type_test_context: DataTypeTestContext) {
    let context = data_type_test_context.await;
    let indexer_tx = create_test_indexer_channel().await;

    let mut data = HashMap::new();
    data.insert("my_text".into(), create_string_value("Required field"));
    data.insert("my_money".into(), create_number_value(123.45)); // Number instead of string for decimal

    let request = PostTableDataRequest {
        profile_name: context.profile_name.clone(),
        table_name: context.table_name.clone(),
        data,
    };

    let result = post_table_data(&context.pool, request, &indexer_tx).await;
    assert!(result.is_err());

    if let Err(err) = result {
        assert_eq!(err.code(), tonic::Code::InvalidArgument);
        assert!(err.message().contains("Expected a string representation for decimal column 'my_money'"));
    }
}

#[rstest]
#[tokio::test]
async fn test_invalid_timestamp_format(#[future] data_type_test_context: DataTypeTestContext) {
    let context = data_type_test_context.await;
    let indexer_tx = create_test_indexer_channel().await;

    let mut data = HashMap::new();
    data.insert("my_text".into(), create_string_value("Required field"));
    data.insert("my_timestamp".into(), create_string_value("not-a-date"));

    let request = PostTableDataRequest {
        profile_name: context.profile_name.clone(),
        table_name: context.table_name.clone(),
        data,
    };

    let result = post_table_data(&context.pool, request, &indexer_tx).await;
    assert!(result.is_err());

    if let Err(err) = result {
        assert_eq!(err.code(), tonic::Code::InvalidArgument);
        assert!(err.message().contains("Invalid timestamp for my_timestamp"));
    }
}

#[rstest]
#[tokio::test]
async fn test_float_for_integer_field(#[future] data_type_test_context: DataTypeTestContext) {
    let context = data_type_test_context.await;
    let indexer_tx = create_test_indexer_channel().await;

    let mut data = HashMap::new();
    data.insert("my_text".into(), create_string_value("Required field"));
    data.insert("my_bigint".into(), create_number_value(123.45)); // Float for integer field

    let request = PostTableDataRequest {
        profile_name: context.profile_name.clone(),
        table_name: context.table_name.clone(),
        data,
    };

    let result = post_table_data(&context.pool, request, &indexer_tx).await;
    assert!(result.is_err());

    if let Err(err) = result {
        assert_eq!(err.code(), tonic::Code::InvalidArgument);
        assert!(err.message().contains("Expected integer for column 'my_bigint', but got a float"));
    }
}

#[rstest]
#[tokio::test]
async fn test_valid_timestamp_formats(#[future] data_type_test_context: DataTypeTestContext) {
    let context = data_type_test_context.await;
    let indexer_tx = create_test_indexer_channel().await;

    let valid_timestamps = vec![
        "2024-01-15T10:30:00Z",
        "2024-01-15T10:30:00+00:00",
        "2024-01-15T10:30:00.123Z",
        "2024-12-31T23:59:59Z",
        "1970-01-01T00:00:00Z", // Unix epoch
    ];

    for (i, timestamp) in valid_timestamps.into_iter().enumerate() {
        let mut data = HashMap::new();
        data.insert("my_text".into(), create_string_value(&format!("Test {}", i)));
        data.insert("my_timestamp".into(), create_string_value(timestamp));

        let request = PostTableDataRequest {
            profile_name: context.profile_name.clone(),
            table_name: context.table_name.clone(),
            data,
        };

        let result = post_table_data(&context.pool, request, &indexer_tx).await;
        assert!(result.is_ok(), "Failed for timestamp: {}", timestamp);
    }
}

#[rstest]
#[tokio::test]
async fn test_boundary_integer_values(#[future] data_type_test_context: DataTypeTestContext) {
    let context = data_type_test_context.await;
    let indexer_tx = create_test_indexer_channel().await;

    // Use safer boundary values that don't have f64 precision issues
    let boundary_values = vec![
        0.0,
        1.0,
        -1.0,
        2147483647.0,  // i32::MAX (for INTEGER columns)
        -2147483648.0, // i32::MIN (for INTEGER columns)
    ];

    for (i, value) in boundary_values.into_iter().enumerate() {
        let mut data = HashMap::new();
        data.insert("my_text".into(), create_string_value(&format!("Boundary test {}", i)));
        data.insert("my_bigint".into(), create_number_value(value));
        let request = PostTableDataRequest {
            profile_name: context.profile_name.clone(),
            table_name: context.table_name.clone(),
            data,
        };
        let result = post_table_data(&context.pool, request, &indexer_tx).await;
        assert!(result.is_ok(), "Failed for boundary value: {}", value);
    }
}

#[rstest]
#[tokio::test]
async fn test_null_values_for_all_types(#[future] data_type_test_context: DataTypeTestContext) {
    let context = data_type_test_context.await;
    let indexer_tx = create_test_indexer_channel().await;

    let mut data = HashMap::new();
    data.insert("my_text".into(), create_string_value("Required for test"));
    data.insert("my_bool".into(), create_null_value());
    data.insert("my_timestamp".into(), create_null_value());
    data.insert("my_bigint".into(), create_null_value());
    data.insert("my_money".into(), create_null_value());

    let request = PostTableDataRequest {
        profile_name: context.profile_name.clone(),
        table_name: context.table_name.clone(),
        data,
    };

    let response = post_table_data(&context.pool, request, &indexer_tx).await.unwrap();
    assert!(response.success);

    // Verify nulls were stored correctly
    let query = format!(
        r#"SELECT my_bool, my_timestamp, my_bigint FROM "{}"."{}" WHERE id = $1"#,
        context.profile_name, context.table_name
    );

    let row = sqlx::query(&query)
        .bind(response.inserted_id)
        .fetch_one(&context.pool)
        .await
        .unwrap();

    let stored_bool: Option<bool> = row.get("my_bool");
    let stored_timestamp: Option<chrono::DateTime<Utc>> = row.get("my_timestamp");
    let stored_bigint: Option<i64> = row.get("my_bigint");

    assert!(stored_bool.is_none());
    assert!(stored_timestamp.is_none());
    assert!(stored_bigint.is_none());
}

// ========================================================================
// FOREIGN KEY CONSTRAINT TESTS
// ========================================================================

#[rstest]
#[tokio::test]
async fn test_insert_with_valid_foreign_key(#[future] foreign_key_test_context: ForeignKeyTestContext) {
    let context = foreign_key_test_context.await;
    let indexer_tx = create_test_indexer_channel().await;

    // First, insert a category
    let mut category_data = HashMap::new();
    category_data.insert("name".into(), create_string_value("Electronics"));
    category_data.insert("description".into(), create_string_value("Electronic devices"));

    let category_request = PostTableDataRequest {
        profile_name: context.profile_name.clone(),
        table_name: context.category_table.clone(),
        data: category_data,
    };

    let category_response = post_table_data(&context.pool, category_request, &indexer_tx).await.unwrap();
    let category_id = category_response.inserted_id;

    // Now insert a product with the valid category_id
    let mut product_data = HashMap::new();
    product_data.insert("name".into(), create_string_value("Laptop"));
    product_data.insert("price".into(), create_string_value("999.99")); // Use string for decimal
    product_data.insert(format!("{}_id", context.category_table), create_number_value(category_id as f64));

    let product_request = PostTableDataRequest {
        profile_name: context.profile_name.clone(),
        table_name: context.product_table.clone(),
        data: product_data,
    };

    let result = post_table_data(&context.pool, product_request, &indexer_tx).await;
    assert!(result.is_ok(), "Insert with valid foreign key should succeed");
}

#[rstest]
#[tokio::test]
async fn test_insert_with_nonexistent_foreign_key(#[future] foreign_key_test_context: ForeignKeyTestContext) {
    let context = foreign_key_test_context.await;
    let indexer_tx = create_test_indexer_channel().await;

    // Try to insert product with non-existent category_id
    let mut product_data = HashMap::new();
    product_data.insert("name".into(), create_string_value("Laptop"));
    product_data.insert("price".into(), create_string_value("999.99"));
    product_data.insert(format!("{}_id", context.category_table), create_number_value(99999.0)); // Non-existent ID

    let product_request = PostTableDataRequest {
        profile_name: context.profile_name.clone(),
        table_name: context.product_table.clone(),
        data: product_data,
    };

    let result = post_table_data(&context.pool, product_request, &indexer_tx).await;
    assert!(result.is_err(), "Insert with non-existent foreign key should fail");

    if let Err(err) = result {
        assert_eq!(err.code(), tonic::Code::Internal);
        assert!(err.message().contains("Insert failed"));
    }
}

#[rstest]
#[tokio::test]
async fn test_insert_with_null_required_foreign_key(#[future] foreign_key_test_context: ForeignKeyTestContext) {
    let context = foreign_key_test_context.await;
    let indexer_tx = create_test_indexer_channel().await;

    // Try to insert product without category_id (required foreign key)
    let mut product_data = HashMap::new();
    product_data.insert("name".into(), create_string_value("Laptop"));
    product_data.insert("price".into(), create_string_value("999.99"));
    // Intentionally omit category_id

    let product_request = PostTableDataRequest {
        profile_name: context.profile_name.clone(),
        table_name: context.product_table.clone(),
        data: product_data,
    };

    let result = post_table_data(&context.pool, product_request, &indexer_tx).await;
    assert!(result.is_err(), "Insert without required foreign key should fail");

    if let Err(err) = result {
        assert_eq!(err.code(), tonic::Code::Internal);
        assert!(err.message().contains("Insert failed"));
    }
}

#[rstest]
#[tokio::test]
async fn test_insert_with_null_optional_foreign_key(#[future] foreign_key_test_context: ForeignKeyTestContext) {
    let context = foreign_key_test_context.await;
    let indexer_tx = create_test_indexer_channel().await;

    // First create a category and product for the required FK
    let mut category_data = HashMap::new();
    category_data.insert("name".into(), create_string_value("Electronics"));

    let category_request = PostTableDataRequest {
        profile_name: context.profile_name.clone(),
        table_name: context.category_table.clone(),
        data: category_data,
    };

    let category_response = post_table_data(&context.pool, category_request, &indexer_tx).await.unwrap();

    let mut product_data = HashMap::new();
    product_data.insert("name".into(), create_string_value("Laptop"));
    product_data.insert("price".into(), create_string_value("999.99"));
    product_data.insert(format!("{}_id", context.category_table), create_number_value(category_response.inserted_id as f64));

    let product_request = PostTableDataRequest {
        profile_name: context.profile_name.clone(),
        table_name: context.product_table.clone(),
        data: product_data,
    };

    let product_response = post_table_data(&context.pool, product_request, &indexer_tx).await.unwrap();

    // Now insert order with required product_id but without optional category_id
    let mut order_data = HashMap::new();
    order_data.insert("quantity".into(), create_number_value(2.0));
    order_data.insert("notes".into(), create_string_value("Test order"));
    order_data.insert(format!("{}_id", context.product_table), create_number_value(product_response.inserted_id as f64));
    // Intentionally omit optional category_id

    let order_request = PostTableDataRequest {
        profile_name: context.profile_name.clone(),
        table_name: context.order_table.clone(),
        data: order_data,
    };

    let result = post_table_data(&context.pool, order_request, &indexer_tx).await;
    assert!(result.is_ok(), "Insert with NULL optional foreign key should succeed");
}

#[rstest]
#[tokio::test]
async fn test_multiple_foreign_keys_scenario(#[future] foreign_key_test_context: ForeignKeyTestContext) {
    let context = foreign_key_test_context.await;
    let indexer_tx = create_test_indexer_channel().await;

    // Create category
    let mut category_data = HashMap::new();
    category_data.insert("name".into(), create_string_value("Books"));

    let category_request = PostTableDataRequest {
        profile_name: context.profile_name.clone(),
        table_name: context.category_table.clone(),
        data: category_data,
    };

    let category_response = post_table_data(&context.pool, category_request, &indexer_tx).await.unwrap();

    // Create product
    let mut product_data = HashMap::new();
    product_data.insert("name".into(), create_string_value("Programming Book"));
    product_data.insert("price".into(), create_string_value("49.99"));
    product_data.insert(format!("{}_id", context.category_table), create_number_value(category_response.inserted_id as f64));

    let product_request = PostTableDataRequest {
        profile_name: context.profile_name.clone(),
        table_name: context.product_table.clone(),
        data: product_data,
    };

    let product_response = post_table_data(&context.pool, product_request, &indexer_tx).await.unwrap();

    // Create order with both foreign keys
    let mut order_data = HashMap::new();
    order_data.insert("quantity".into(), create_number_value(3.0));
    order_data.insert("notes".into(), create_string_value("Bulk order"));
    order_data.insert(format!("{}_id", context.product_table), create_number_value(product_response.inserted_id as f64));
    order_data.insert(format!("{}_id", context.category_table), create_number_value(category_response.inserted_id as f64));

    let order_request = PostTableDataRequest {
        profile_name: context.profile_name.clone(),
        table_name: context.order_table.clone(),
        data: order_data,
    };

    let result = post_table_data(&context.pool, order_request, &indexer_tx).await;
    assert!(result.is_ok(), "Insert with multiple valid foreign keys should succeed");

    // Verify the data was inserted correctly
    let product_id_col = format!("{}_id", context.product_table);
    let category_id_col = format!("{}_id", context.category_table);

    let query = format!(
        r#"SELECT quantity, "{}", "{}" FROM "{}"."{}" WHERE id = $1"#,
        product_id_col, category_id_col, context.profile_name, context.order_table
    );

    let row = sqlx::query(&query)
        .bind(result.unwrap().inserted_id)
        .fetch_one(&context.pool)
        .await
        .unwrap();

    // Fix: quantity is defined as "integer" in the foreign key test context, so use i32
    let quantity: i32 = row.get("quantity");
    let stored_product_id: i64 = row.get(product_id_col.as_str());
    let stored_category_id: Option<i64> = row.get(category_id_col.as_str());

    assert_eq!(quantity, 3);
    assert_eq!(stored_product_id, product_response.inserted_id);
    assert_eq!(stored_category_id.unwrap(), category_response.inserted_id);
}

// ========================================================================
// ADDITIONAL EDGE CASE TESTS
// ========================================================================

#[rstest]
#[tokio::test]
async fn test_extremely_large_decimal_numbers(#[future] data_type_test_context: DataTypeTestContext) {
    let context = data_type_test_context.await;
    let indexer_tx = create_test_indexer_channel().await;

    let large_decimals = vec![
        "1000000000.0000",
        "999999999999.99",
        "-999999999999.99",
        "0.0001",
    ];

    for (i, decimal_str) in large_decimals.into_iter().enumerate() {
        let mut data = HashMap::new();
        data.insert("my_text".into(), create_string_value(&format!("Large decimal test {}", i)));
        data.insert("my_money".into(), create_string_value(decimal_str));

        let request = PostTableDataRequest {
            profile_name: context.profile_name.clone(),
            table_name: context.table_name.clone(),
            data,
        };

        let result = post_table_data(&context.pool, request, &indexer_tx).await;
        assert!(result.is_ok(), "Failed for large decimal: {}", decimal_str);
    }
}

#[rstest]
#[tokio::test]
async fn test_boolean_edge_cases(#[future] data_type_test_context: DataTypeTestContext) {
    let context = data_type_test_context.await;
    let indexer_tx = create_test_indexer_channel().await;

    let boolean_values = vec![true, false];

    for (i, bool_val) in boolean_values.into_iter().enumerate() {
        let mut data = HashMap::new();
        data.insert("my_text".into(), create_string_value(&format!("Boolean test {}", i)));
        data.insert("my_bool".into(), create_bool_value(bool_val));

        let request = PostTableDataRequest {
            profile_name: context.profile_name.clone(),
            table_name: context.table_name.clone(),
            data,
        };

        let response = post_table_data(&context.pool, request, &indexer_tx).await.unwrap();

        // Verify boolean was stored correctly
        let query = format!(
            r#"SELECT my_bool FROM "{}"."{}" WHERE id = $1"#,
            context.profile_name, context.table_name
        );

        let stored_bool: bool = sqlx::query_scalar::<_, bool>(&query)
            .bind(response.inserted_id)
            .fetch_one(&context.pool)
            .await
            .unwrap();

        assert_eq!(stored_bool, bool_val);
    }
}

#[rstest]
#[tokio::test]
async fn test_decimal_precision_handling(#[future] data_type_test_context: DataTypeTestContext) {
    let context = data_type_test_context.await;
    let indexer_tx = create_test_indexer_channel().await;

    let decimal_values = vec![
        "0.01",
        "99.99", 
        "123.45",
        "999.99",
        "-123.45",
    ];

    for (i, decimal_val) in decimal_values.into_iter().enumerate() {
        let mut data = HashMap::new();
        data.insert("my_text".into(), create_string_value(&format!("Decimal test {}", i)));
        data.insert("my_decimal".into(), create_string_value(decimal_val));

        let request = PostTableDataRequest {
            profile_name: context.profile_name.clone(),
            table_name: context.table_name.clone(),
            data,
        };

        let result = post_table_data(&context.pool, request, &indexer_tx).await;
        assert!(result.is_ok(), "Failed for decimal value: {}", decimal_val);
    }
}

#[rstest]
#[tokio::test]
async fn test_invalid_decimal_string_formats(#[future] data_type_test_context: DataTypeTestContext) {
    let context = data_type_test_context.await;
    let indexer_tx = create_test_indexer_channel().await;

    let invalid_decimals = vec![
        "not-a-number",
        "123.45.67", 
        "abc123",
        "",
        "  ",
    ];

    for (i, invalid_decimal) in invalid_decimals.into_iter().enumerate() {
        let mut data = HashMap::new();
        data.insert("my_text".into(), create_string_value(&format!("Invalid decimal test {}", i)));
        data.insert("my_decimal".into(), create_string_value(invalid_decimal));

        let request = PostTableDataRequest {
            profile_name: context.profile_name.clone(),
            table_name: context.table_name.clone(),
            data,
        };

        let result = post_table_data(&context.pool, request, &indexer_tx).await;
        
        if invalid_decimal.trim().is_empty() {
            // Empty strings should be treated as NULL and succeed
            assert!(result.is_ok(), "Empty string should be treated as NULL for: {}", invalid_decimal);
        } else {
            // Invalid decimal strings should fail
            assert!(result.is_err(), "Should fail for invalid decimal: {}", invalid_decimal);
            if let Err(err) = result {
                assert_eq!(err.code(), tonic::Code::InvalidArgument);
                assert!(err.message().contains("Invalid decimal string format"));
            }
        }
    }
}


#[rstest]
#[tokio::test]
async fn test_mixed_null_and_valid_data(#[future] data_type_test_context: DataTypeTestContext) {
    let context = data_type_test_context.await;
    let indexer_tx = create_test_indexer_channel().await;
    let mut data = HashMap::new();
    data.insert("my_text".into(), create_string_value("Mixed data test"));
    data.insert("my_bool".into(), create_bool_value(true));
    data.insert("my_timestamp".into(), create_null_value());
    data.insert("my_bigint".into(), create_number_value(42.0));
    data.insert("my_money".into(), create_null_value());
    let request = PostTableDataRequest {
        profile_name: context.profile_name.clone(),
        table_name: context.table_name.clone(),
        data,
    };
    let response = post_table_data(&context.pool, request, &indexer_tx).await.unwrap();
    assert!(response.success);

    // Verify mixed null and valid data was stored correctly
    let query = format!(
        r#"SELECT my_text, my_bool, my_timestamp, my_bigint, my_money FROM "{}"."{}" WHERE id = $1"#,
        context.profile_name, context.table_name
    );
    let row = sqlx::query(&query)
        .bind(response.inserted_id)
        .fetch_one(&context.pool)
        .await
        .unwrap();

    let stored_text: String = row.get("my_text");
    let stored_bool: bool = row.get("my_bool");
    let stored_timestamp: Option<DateTime<Utc>> = row.get("my_timestamp");
    // Change this based on your actual column type in the schema:
    // If my_bigint is defined as "integer" in table definition, use i32:
    let stored_bigint: i32 = row.get("my_bigint");
    // If my_bigint is defined as "biginteger" or "bigint" in table definition, use i64:
    // let stored_bigint: i64 = row.get("my_bigint");
    let stored_money: Option<Decimal> = row.get("my_money");

    assert_eq!(stored_text, "Mixed data test");
    assert_eq!(stored_bool, true);
    assert!(stored_timestamp.is_none());
    assert_eq!(stored_bigint, 42);
    assert!(stored_money.is_none());
}
