// tests/tables_data/put/put_table_data_steel_decimal_test.rs

use sqlx::PgPool;
use common::proto::komp_ac::{
    table_definition::{PostTableDefinitionRequest, ColumnDefinition},
    table_script::PostTableScriptRequest,
    tables_data::{PostTableDataRequest, PutTableDataRequest},
};
use prost_types::value::Kind;
use prost_types::Value as ProtoValue;
use std::collections::HashMap;
use tokio::sync::mpsc;

use server::indexer::IndexCommand;
use server::table_definition::handlers::post_table_definition::post_table_definition;
use server::table_script::handlers::post_table_script::post_table_script;
use server::tables_data::handlers::post_table_data::post_table_data;
use server::tables_data::handlers::put_table_data::put_table_data;

async fn setup_test_schema(pool: &PgPool, profile_name: &str, table_name: &str) -> i64 {
    // Create table definition
    let request = PostTableDefinitionRequest {
        profile_name: profile_name.to_string(),
        table_name: table_name.to_string(),
        columns: vec![
            ColumnDefinition {
                name: "price".to_string(),
                field_type: "decimal(10, 2)".to_string(),
            },
            ColumnDefinition {
                name: "quantity".to_string(),
                field_type: "integer".to_string(),
            },
            ColumnDefinition {
                name: "total".to_string(),
                field_type: "decimal(10, 2)".to_string(),
            },
            ColumnDefinition {
                name: "percentage".to_string(),
                field_type: "decimal(28, 24)".to_string(),
            },
        ],
        indexes: vec![],
        links: vec![],
    };

    let response = post_table_definition(pool, request).await.unwrap();
    assert!(response.success);

    // Get table definition ID
    let schema_row = sqlx::query!("SELECT id FROM schemas WHERE name = $1", profile_name)
        .fetch_one(pool)
        .await
        .unwrap();

    let table_row = sqlx::query!(
        "SELECT id FROM table_definitions WHERE schema_id = $1 AND table_name = $2",
        schema_row.id,
        table_name
    )
    .fetch_one(pool)
    .await
    .unwrap();

    table_row.id
}

async fn create_indexer_channel() -> mpsc::Sender<IndexCommand> {
    let (tx, mut rx) = mpsc::channel(100);

    tokio::spawn(async move {
        while let Some(_) = rx.recv().await {
            // Just consume messages for testing
        }
    });

    tx
}

// Helper function to create initial record and return its ID
async fn create_initial_record(
    pool: &PgPool,
    profile_name: &str,
    table_name: &str,
    indexer_tx: &mpsc::Sender<IndexCommand>,
) -> i64 {
    println!("📝 Creating initial record for profile: {}, table: {}", profile_name, table_name);
    
    let mut data = HashMap::new();
    
    // Set different initial values based on the test case to satisfy validation scripts
    match (profile_name, table_name) {
        ("test_put_complex", "order") => {
            // For complex formula: (+ (* @price @quantity) (* (* @price @quantity) 0.08))
            // With price=10.00, quantity=1: (10*1) + (10*1*0.08) = 10 + 0.8 = 10.8
            data.insert("price".to_string(), ProtoValue { kind: Some(Kind::StringValue("10.00".to_string())) });
            data.insert("quantity".to_string(), ProtoValue { kind: Some(Kind::NumberValue(1.0)) });
            data.insert("total".to_string(), ProtoValue { kind: Some(Kind::StringValue("10.80".to_string())) }); // Fixed!
            data.insert("percentage".to_string(), ProtoValue { kind: Some(Kind::StringValue("100.00".to_string())) });
        },
        ("test_put_division", "calculation") => {
            // For division: (/ @total @price) 
            // With total=10.00, price=10.00: 10/10 = 1
            data.insert("price".to_string(), ProtoValue { kind: Some(Kind::StringValue("10.00".to_string())) });
            data.insert("quantity".to_string(), ProtoValue { kind: Some(Kind::NumberValue(1.0)) });
            data.insert("total".to_string(), ProtoValue { kind: Some(Kind::StringValue("10.00".to_string())) });
            data.insert("percentage".to_string(), ProtoValue { kind: Some(Kind::StringValue("1".to_string())) }); // Fixed!
        },
        ("test_put_errors", "error_test") => {
            // For error test: we don't want to trigger the script during creation
            // So we'll create without scripts first, then add scripts later
            data.insert("price".to_string(), ProtoValue { kind: Some(Kind::StringValue("10.00".to_string())) });
            data.insert("quantity".to_string(), ProtoValue { kind: Some(Kind::NumberValue(1.0)) });
            data.insert("total".to_string(), ProtoValue { kind: Some(Kind::StringValue("10.00".to_string())) });
            data.insert("percentage".to_string(), ProtoValue { kind: Some(Kind::StringValue("100.00".to_string())) });
        },
        _ => {
            // Default case - works for simple multiplication and most tests
            data.insert("price".to_string(), ProtoValue { kind: Some(Kind::StringValue("10.00".to_string())) });
            data.insert("quantity".to_string(), ProtoValue { kind: Some(Kind::NumberValue(1.0)) });
            data.insert("total".to_string(), ProtoValue { kind: Some(Kind::StringValue("10.00".to_string())) }); // 10*1 = 10
            data.insert("percentage".to_string(), ProtoValue { kind: Some(Kind::StringValue("100.00".to_string())) });
        }
    }

    let data_request = PostTableDataRequest {
        profile_name: profile_name.to_string(),
        table_name: table_name.to_string(),
        data,
    };

    let response = post_table_data(pool, data_request, indexer_tx).await.unwrap();
    println!("✅ Initial record created with ID: {}", response.inserted_id);
    response.inserted_id
}

#[sqlx::test]
async fn test_put_basic_arithmetic_validation_success(pool: PgPool) {
    let table_def_id = setup_test_schema(&pool, "test_put_arithmetic", "invoice").await;
    let indexer_tx = create_indexer_channel().await;

    let script_request = PostTableScriptRequest {
        table_definition_id: table_def_id,
        target_column: "total".to_string(),
        script: "(* @price @quantity)".to_string(),
        description: "Total = Price × Quantity".to_string(),
    };
    post_table_script(&pool, script_request).await.unwrap();

    let record_id = create_initial_record(&pool, "test_put_arithmetic", "invoice", &indexer_tx).await;

    let mut update_data = HashMap::new();
    update_data.insert("price".to_string(), ProtoValue {
        kind: Some(Kind::StringValue("25.50".to_string())),
    });
    update_data.insert("quantity".to_string(), ProtoValue {
        kind: Some(Kind::NumberValue(3.0)),
    });
    update_data.insert("total".to_string(), ProtoValue {
        kind: Some(Kind::StringValue("76.50".to_string())),
    });

    let put_request = PutTableDataRequest {
        profile_name: "test_put_arithmetic".to_string(),
        table_name: "invoice".to_string(),
        id: record_id,
        data: update_data,
    };

    let response = put_table_data(&pool, put_request, &indexer_tx).await.unwrap();
    assert!(response.success);
    assert_eq!(response.updated_id, record_id);
}

#[sqlx::test]
async fn test_put_basic_arithmetic_validation_failure(pool: PgPool) {
    let table_def_id = setup_test_schema(&pool, "test_put_arithmetic_fail", "invoice").await;
    let indexer_tx = create_indexer_channel().await;

    let script_request = PostTableScriptRequest {
        table_definition_id: table_def_id,
        target_column: "total".to_string(),
        script: "(* @price @quantity)".to_string(),
        description: "Total = Price × Quantity".to_string(),
    };
    post_table_script(&pool, script_request).await.unwrap();

    let record_id = create_initial_record(&pool, "test_put_arithmetic_fail", "invoice", &indexer_tx).await;

    let mut update_data = HashMap::new();
    update_data.insert("price".to_string(), ProtoValue {
        kind: Some(Kind::StringValue("25.50".to_string())),
    });
    update_data.insert("quantity".to_string(), ProtoValue {
        kind: Some(Kind::NumberValue(3.0)),
    });
    update_data.insert("total".to_string(), ProtoValue {
        kind: Some(Kind::StringValue("70.00".to_string())),
    });

    let put_request = PutTableDataRequest {
        profile_name: "test_put_arithmetic_fail".to_string(),
        table_name: "invoice".to_string(),
        id: record_id,
        data: update_data,
    };

    let result = put_table_data(&pool, put_request, &indexer_tx).await;
    assert!(result.is_err());

    let error = result.unwrap_err();
    assert_eq!(error.code(), tonic::Code::InvalidArgument);
    let msg = error.message();
    assert!(msg.contains("Validation failed for column 'total'"));
    assert!(msg.contains("Script calculated '76.50'"));
    assert!(msg.contains("but user provided '70.00'"));
}

#[sqlx::test]
async fn test_put_complex_formula_validation(pool: PgPool) {
    let table_def_id = setup_test_schema(&pool, "test_put_complex", "order").await;
    let indexer_tx = create_indexer_channel().await;

    let script_request = PostTableScriptRequest {
        table_definition_id: table_def_id,
        target_column: "total".to_string(),
        script: "(+ (* @price @quantity) (* (* @price @quantity) 0.08))".to_string(),
        description: "Total with 8% tax".to_string(),
    };
    post_table_script(&pool, script_request).await.unwrap();

    let record_id = create_initial_record(&pool, "test_put_complex", "order", &indexer_tx).await;

    let mut update_data = HashMap::new();
    update_data.insert("price".to_string(), ProtoValue {
        kind: Some(Kind::StringValue("100.00".to_string())),
    });
    update_data.insert("quantity".to_string(), ProtoValue {
        kind: Some(Kind::NumberValue(2.0)),
    });
    update_data.insert("total".to_string(), ProtoValue {
        kind: Some(Kind::StringValue("216.00".to_string())),
    });

    let put_request = PutTableDataRequest {
        profile_name: "test_put_complex".to_string(),
        table_name: "order".to_string(),
        id: record_id,
        data: update_data,
    };

    let response = put_table_data(&pool, put_request, &indexer_tx).await.unwrap();
    assert!(response.success);
}

#[sqlx::test]
async fn test_put_division_with_precision(pool: PgPool) {
    let table_def_id = setup_test_schema(&pool, "test_put_division", "calculation").await;
    let indexer_tx = create_indexer_channel().await;

    let script_request = PostTableScriptRequest {
        table_definition_id: table_def_id,
        target_column: "percentage".to_string(),
        script: "(/ @total @price)".to_string(),
        description: "Percentage = Total / Price".to_string(),
    };
    post_table_script(&pool, script_request).await.unwrap();

    let record_id = create_initial_record(&pool, "test_put_division", "calculation", &indexer_tx).await;

    let mut update_data = HashMap::new();
    update_data.insert("price".to_string(), ProtoValue {
        kind: Some(Kind::StringValue("3.00".to_string())),
    });
    update_data.insert("total".to_string(), ProtoValue {
        kind: Some(Kind::StringValue("10.00".to_string())),
    });
    update_data.insert("percentage".to_string(), ProtoValue {
        kind: Some(Kind::StringValue("3.3333333333333333333333333333".to_string())), // Updated to Steel's actual precision (28 decimal places)
    });

    let put_request = PutTableDataRequest {
        profile_name: "test_put_division".to_string(),
        table_name: "calculation".to_string(),
        id: record_id,
        data: update_data,
    };

    let response = put_table_data(&pool, put_request, &indexer_tx).await.unwrap();
    assert!(response.success);
}

#[sqlx::test]
async fn test_put_advanced_math_functions(pool: PgPool) {
    let request = PostTableDefinitionRequest {
        profile_name: "test_put_advanced_math".to_string(),
        table_name: "calculations".to_string(),
        columns: vec![
            ColumnDefinition {
                name: "input".to_string(),
                field_type: "decimal(10, 4)".to_string(),
            },
            ColumnDefinition {
                name: "square_root".to_string(),
                field_type: "decimal(28, 24)".to_string(),
            },
            ColumnDefinition {
                name: "power_result".to_string(),
                field_type: "decimal(10, 4)".to_string(),
            },
        ],
        indexes: vec![],
        links: vec![],
    };
    post_table_definition(&pool, request).await.unwrap();

    let schema_row = sqlx::query!("SELECT id FROM schemas WHERE name = $1", "test_put_advanced_math")
        .fetch_one(&pool).await.unwrap();
    let table_row = sqlx::query!(
        "SELECT id FROM table_definitions WHERE schema_id = $1 AND table_name = $2",
        schema_row.id, "calculations"
    ).fetch_one(&pool).await.unwrap();

    let indexer_tx = create_indexer_channel().await;

    let sqrt_script = PostTableScriptRequest {
        table_definition_id: table_row.id,
        target_column: "square_root".to_string(),
        script: "(sqrt @input)".to_string(),
        description: "Square root validation".to_string(),
    };
    post_table_script(&pool, sqrt_script).await.unwrap();

    let power_script = PostTableScriptRequest {
        table_definition_id: table_row.id,
        target_column: "power_result".to_string(),
        script: "(^ @input 2.0)".to_string(),
        description: "Power function validation".to_string(),
    };
    post_table_script(&pool, power_script).await.unwrap();

    let mut initial_data = HashMap::new();
    initial_data.insert("input".to_string(), ProtoValue { kind: Some(Kind::StringValue("4.0000".to_string())) });
    initial_data.insert("square_root".to_string(), ProtoValue { kind: Some(Kind::StringValue("2.0".to_string())) });
    initial_data.insert("power_result".to_string(), ProtoValue { kind: Some(Kind::StringValue("16.0000".to_string())) });
    let initial_request = PostTableDataRequest {
        profile_name: "test_put_advanced_math".to_string(),
        table_name: "calculations".to_string(),
        data: initial_data,
    };
    let record_id = post_table_data(&pool, initial_request, &indexer_tx).await.unwrap().inserted_id;

    let mut update_data = HashMap::new();
    update_data.insert("input".to_string(), ProtoValue { kind: Some(Kind::StringValue("16.0000".to_string())) });
    update_data.insert("square_root".to_string(), ProtoValue { kind: Some(Kind::StringValue("4.0".to_string())) });
    update_data.insert("power_result".to_string(), ProtoValue { kind: Some(Kind::StringValue("256.0000".to_string())) });

    let put_request = PutTableDataRequest {
        profile_name: "test_put_advanced_math".to_string(),
        table_name: "calculations".to_string(),
        id: record_id,
        data: update_data,
    };

    let response = put_table_data(&pool, put_request, &indexer_tx).await.unwrap();
    assert!(response.success);
}

#[sqlx::test]
async fn test_put_financial_calculations(pool: PgPool) {
    let request = PostTableDefinitionRequest {
        profile_name: "test_put_financial".to_string(),
        table_name: "finance".to_string(),
        columns: vec![
            ColumnDefinition { name: "principal".to_string(), field_type: "decimal(12, 2)".to_string() },
            ColumnDefinition { name: "rate".to_string(), field_type: "decimal(6, 4)".to_string() },
            ColumnDefinition { name: "time".to_string(), field_type: "decimal(4, 1)".to_string() },
            ColumnDefinition { name: "compound_result".to_string(), field_type: "decimal(12, 4)".to_string() },
            ColumnDefinition { name: "percentage_result".to_string(), field_type: "decimal(10, 2)".to_string() },
        ],
        indexes: vec![], links: vec![],
    };
    post_table_definition(&pool, request).await.unwrap();

    let schema_row = sqlx::query!("SELECT id FROM schemas WHERE name = $1", "test_put_financial").fetch_one(&pool).await.unwrap();
    let table_row = sqlx::query!("SELECT id FROM table_definitions WHERE schema_id = $1 AND table_name = $2", schema_row.id, "finance").fetch_one(&pool).await.unwrap();
    let indexer_tx = create_indexer_channel().await;

    let compound_script = PostTableScriptRequest {
        table_definition_id: table_row.id,
        target_column: "compound_result".to_string(),
        script: "(* @principal (^ (+ 1.0 @rate) @time))".to_string(),
        description: "Compound interest calculation".to_string(),
    };
    post_table_script(&pool, compound_script).await.unwrap();

    let percentage_script = PostTableScriptRequest {
        table_definition_id: table_row.id,
        target_column: "percentage_result".to_string(),
        script: "(* @principal @rate)".to_string(),
        description: "Percentage calculation".to_string(),
    };
    post_table_script(&pool, percentage_script).await.unwrap();

    let mut initial_data = HashMap::new();
    initial_data.insert("principal".to_string(), ProtoValue { kind: Some(Kind::StringValue("500.00".to_string())) });
    initial_data.insert("rate".to_string(), ProtoValue { kind: Some(Kind::StringValue("0.0300".to_string())) });
    initial_data.insert("time".to_string(), ProtoValue { kind: Some(Kind::StringValue("1.0".to_string())) });
    initial_data.insert("compound_result".to_string(), ProtoValue { kind: Some(Kind::StringValue("515.0000".to_string())) });
    initial_data.insert("percentage_result".to_string(), ProtoValue { kind: Some(Kind::StringValue("15.00".to_string())) });
    let initial_request = PostTableDataRequest {
        profile_name: "test_put_financial".to_string(),
        table_name: "finance".to_string(),
        data: initial_data,
    };
    let record_id = post_table_data(&pool, initial_request, &indexer_tx).await.unwrap().inserted_id;

    let mut update_data = HashMap::new();
    update_data.insert("principal".to_string(), ProtoValue { kind: Some(Kind::StringValue("1000.00".to_string())) });
    update_data.insert("rate".to_string(), ProtoValue { kind: Some(Kind::StringValue("0.0500".to_string())) });
    update_data.insert("time".to_string(), ProtoValue { kind: Some(Kind::StringValue("2.0".to_string())) });
    update_data.insert("compound_result".to_string(), ProtoValue { kind: Some(Kind::StringValue("1102.5000".to_string())) });
    update_data.insert("percentage_result".to_string(), ProtoValue { kind: Some(Kind::StringValue("50.00".to_string())) });

    let put_request = PutTableDataRequest {
        profile_name: "test_put_financial".to_string(),
        table_name: "finance".to_string(),
        id: record_id,
        data: update_data,
    };

    let response = put_table_data(&pool, put_request, &indexer_tx).await.unwrap();
    assert!(response.success);
}

#[sqlx::test]
async fn test_put_partial_update_with_validation(pool: PgPool) {
    let table_def_id = setup_test_schema(&pool, "test_put_partial", "invoice").await;
    let indexer_tx = create_indexer_channel().await;

    let script_request = PostTableScriptRequest {
        table_definition_id: table_def_id,
        target_column: "total".to_string(),
        script: "(* @price @quantity)".to_string(),
        description: "Total = Price × Quantity".to_string(),
    };
    post_table_script(&pool, script_request).await.unwrap();

    let record_id = create_initial_record(&pool, "test_put_partial", "invoice", &indexer_tx).await;

    // Partial update: only update quantity. The script detects this would change total
    // from 10.00 to 50.00 and requires the user to include 'total' in the update.
    let mut update_data = HashMap::new();
    update_data.insert("quantity".to_string(), ProtoValue {
        kind: Some(Kind::NumberValue(5.0)),
    });

    let put_request = PutTableDataRequest {
        profile_name: "test_put_partial".to_string(),
        table_name: "invoice".to_string(),
        id: record_id,
        data: update_data,
    };

    // This should fail because script would change total value
    let result = put_table_data(&pool, put_request, &indexer_tx).await;
    assert!(result.is_err());
    let error = result.unwrap_err();
    assert_eq!(error.code(), tonic::Code::FailedPrecondition);
    assert!(error.message().contains("Script for column 'total' was triggered"));
    assert!(error.message().contains("from '10.00' to '50.00'"));

    // Now, test a partial update that SHOULD fail validation.
    // We update quantity and provide an incorrect total.
    let mut failing_update_data = HashMap::new();
    failing_update_data.insert("quantity".to_string(), ProtoValue {
        kind: Some(Kind::NumberValue(3.0)),
    });
    failing_update_data.insert("total".to_string(), ProtoValue {
        kind: Some(Kind::StringValue("99.99".to_string())), // Wrong! Should be 10.00 * 3 = 30.00
    });

    let failing_put_request = PutTableDataRequest {
        profile_name: "test_put_partial".to_string(),
        table_name: "invoice".to_string(),
        id: record_id,
        data: failing_update_data,
    };

    let result = put_table_data(&pool, failing_put_request, &indexer_tx).await;
    assert!(result.is_err());
    let error = result.unwrap_err();
    assert_eq!(error.code(), tonic::Code::InvalidArgument);
    assert!(error.message().contains("Script calculated '30.00'"));
    assert!(error.message().contains("but user provided '99.99'"));
}

#[sqlx::test]
async fn test_put_no_data_update(pool: PgPool) {
    let _table_def_id = setup_test_schema(&pool, "test_put_empty", "invoice").await;
    let indexer_tx = create_indexer_channel().await;

    let record_id = create_initial_record(&pool, "test_put_empty", "invoice", &indexer_tx).await;

    let update_data = HashMap::new();

    let put_request = PutTableDataRequest {
        profile_name: "test_put_empty".to_string(),
        table_name: "invoice".to_string(),
        id: record_id,
        data: update_data,
    };

    let response = put_table_data(&pool, put_request, &indexer_tx).await.unwrap();
    assert!(response.success);
    assert!(response.message.contains("No fields to update"));
}

#[sqlx::test]
async fn test_put_record_not_found(pool: PgPool) {
    let _table_def_id = setup_test_schema(&pool, "test_put_not_found", "invoice").await;
    let indexer_tx = create_indexer_channel().await;

    let mut update_data = HashMap::new();
    update_data.insert("price".to_string(), ProtoValue {
        kind: Some(Kind::StringValue("100.00".to_string())),
    });

    let put_request = PutTableDataRequest {
        profile_name: "test_put_not_found".to_string(),
        table_name: "invoice".to_string(),
        id: 99999,
        data: update_data,
    };

    let result = put_table_data(&pool, put_request, &indexer_tx).await;
    assert!(result.is_err());

    let error = result.unwrap_err();
    assert_eq!(error.code(), tonic::Code::NotFound);
    assert!(error.message().contains("Record not found"));
}

#[sqlx::test]
async fn test_put_steel_script_error_handling(pool: PgPool) {
    let table_def_id = setup_test_schema(&pool, "test_put_errors", "error_test").await;
    let indexer_tx = create_indexer_channel().await;

    // First create the record WITHOUT the error script
    let record_id = create_initial_record(&pool, "test_put_errors", "error_test", &indexer_tx).await;

    // NOW add the error script after the record exists
    let script_request = PostTableScriptRequest {
        table_definition_id: table_def_id,
        target_column: "total".to_string(),
        script: "(/ @price 0.0)".to_string(),
        description: "Error test".to_string(),
    };
    post_table_script(&pool, script_request).await.unwrap();

    let mut update_data = HashMap::new();
    update_data.insert("price".to_string(), ProtoValue {
        kind: Some(Kind::StringValue("100.00".to_string())),
    });
    update_data.insert("total".to_string(), ProtoValue {
        kind: Some(Kind::StringValue("999.99".to_string())),
    });

    let put_request = PutTableDataRequest {
        profile_name: "test_put_errors".to_string(),
        table_name: "error_test".to_string(),
        id: record_id,
        data: update_data,
    };

    let result = put_table_data(&pool, put_request, &indexer_tx).await;
    assert!(result.is_err());

    let error = result.unwrap_err();
    assert_eq!(error.code(), tonic::Code::InvalidArgument);
    assert!(error.message().contains("Script execution failed"));
    assert!(error.message().contains("Division by zero"));
}

#[sqlx::test]
async fn test_decimal_precision_behavior(pool: PgPool) {
    println!("🧮 Testing decimal precision behavior with 1000/3");

    let indexer_tx = create_indexer_channel().await;

    // Create table with high precision decimal
    let request = PostTableDefinitionRequest {
        profile_name: "test_precision".to_string(),
        table_name: "precision_test".to_string(),
        columns: vec![
            ColumnDefinition {
                name: "dividend".to_string(),
                field_type: "decimal(10, 2)".to_string(),
            },
            ColumnDefinition {
                name: "divisor".to_string(),
                field_type: "decimal(10, 2)".to_string(),
            },
            ColumnDefinition {
                name: "result".to_string(),
                field_type: "decimal(28, 24)".to_string(), // High precision
            },
        ],
        indexes: vec![],
        links: vec![],
    };
    post_table_definition(&pool, request).await.unwrap();

    // Get table definition ID
    let schema_row = sqlx::query!("SELECT id FROM schemas WHERE name = $1", "test_precision")
        .fetch_one(&pool).await.unwrap();
    let table_row = sqlx::query!(
        "SELECT id FROM table_definitions WHERE schema_id = $1 AND table_name = $2",
        schema_row.id, "precision_test"
    ).fetch_one(&pool).await.unwrap();

    // Add division script
    let script_request = PostTableScriptRequest {
        table_definition_id: table_row.id,
        target_column: "result".to_string(),
        script: "(/ @dividend @divisor)".to_string(),
        description: "Division test for precision".to_string(),
    };
    post_table_script(&pool, script_request).await.unwrap();

    // Create initial record with 100/10 = 10 (simple case)
    let mut initial_data = HashMap::new();
    initial_data.insert("dividend".to_string(), ProtoValue {
        kind: Some(Kind::StringValue("100.00".to_string())),
    });
    initial_data.insert("divisor".to_string(), ProtoValue {
        kind: Some(Kind::StringValue("10.00".to_string())),
    });
    initial_data.insert("result".to_string(), ProtoValue {
        kind: Some(Kind::StringValue("10.000000000000000000000000".to_string())),
    });

    let initial_request = PostTableDataRequest {
        profile_name: "test_precision".to_string(),
        table_name: "precision_test".to_string(),
        data: initial_data,
    };
    let record_id = post_table_data(&pool, initial_request, &indexer_tx).await.unwrap().inserted_id;

    // Now test 1000/3 to see the precision
    println!("🔄 Testing 1000 ÷ 3 precision...");
    
    let mut update_data = HashMap::new();
    update_data.insert("dividend".to_string(), ProtoValue {
        kind: Some(Kind::StringValue("1000.00".to_string())),
    });
    update_data.insert("divisor".to_string(), ProtoValue {
        kind: Some(Kind::StringValue("3.00".to_string())),
    });
    // Let's put a placeholder value and see what the script actually calculates
    update_data.insert("result".to_string(), ProtoValue {
        kind: Some(Kind::StringValue("333.333333333333333333333333".to_string())),
    });

    let put_request = PutTableDataRequest {
        profile_name: "test_precision".to_string(),
        table_name: "precision_test".to_string(),
        id: record_id,
        data: update_data,
    };

    // This will likely fail, but we'll see the actual calculated value in the error
    let result = put_table_data(&pool, put_request, &indexer_tx).await;
    
    match result {
        Ok(_) => println!("✅ Test passed unexpectedly!"),
        Err(e) => {
            println!("❌ Expected failure - checking precision:");
            println!("Error message: {}", e.message());
            // The error message will show us the exact precision Steel uses
        }
    }
}

// ============================================================================
// NEW HANDLER-BASED TESTS (added alongside existing tests)
// ============================================================================

// Create table schema using the handler (like the API would)
async fn setup_test_schema_via_handler(
    pool: &PgPool,
    profile_name: &str,
    table_name: &str,
) -> Result<(), Box<dyn std::error::Error>> {
    println!("📋 Creating table schema: {}.{}", profile_name, table_name);

    let request = PostTableDefinitionRequest {
        profile_name: profile_name.to_string(),
        table_name: table_name.to_string(),
        columns: vec![
            ColumnDefinition {
                name: "price".to_string(),
                field_type: "decimal(10, 2)".to_string(),
            },
            ColumnDefinition {
                name: "quantity".to_string(),
                field_type: "integer".to_string(),
            },
            ColumnDefinition {
                name: "total".to_string(),
                field_type: "decimal(10, 2)".to_string(),
            },
            ColumnDefinition {
                name: "percentage".to_string(),
                field_type: "decimal(28, 24)".to_string(),
            },
        ],
        indexes: vec![],
        links: vec![],
    };

    let response = post_table_definition(pool, request).await?;
    assert!(response.success);
    println!("✅ Table schema created successfully");
    Ok(())
}

// Add script using the handler (like the API would)
async fn add_validation_script_via_handler(
    pool: &PgPool,
    profile_name: &str,
    table_name: &str,
    target_column: &str,
    script: &str,
) -> Result<(), Box<dyn std::error::Error>> {
    println!("📜 Adding validation script for column: {}", target_column);
    println!("Script: {}", script);

    // Get table definition ID
    let schema_row = sqlx::query!("SELECT id FROM schemas WHERE name = $1", profile_name)
        .fetch_one(pool)
        .await?;

    let table_row = sqlx::query!(
        "SELECT id FROM table_definitions WHERE schema_id = $1 AND table_name = $2",
        schema_row.id,
        table_name
    )
    .fetch_one(pool)
    .await?;

    let request = PostTableScriptRequest {
        table_definition_id: table_row.id,
        target_column: target_column.to_string(),
        script: script.to_string(),
        description: "Test validation script".to_string(),
    };

    let response = post_table_script(pool, request).await?;
    println!("✅ Validation script added with ID: {}", response.id);
    Ok(())
}

// Create initial record using the handler (like the API would)
async fn create_initial_record_via_handler(
    pool: &PgPool,
    profile_name: &str,
    table_name: &str,
    indexer_tx: &mpsc::Sender<IndexCommand>,
    price: &str,
    quantity: f64,
    total: &str,
) -> Result<i64, Box<dyn std::error::Error>> {
    println!("📝 Creating initial record with price={}, quantity={}, total={}", price, quantity, total);

    let mut data = HashMap::new();
    data.insert("price".to_string(), ProtoValue {
        kind: Some(Kind::StringValue(price.to_string())),
    });
    data.insert("quantity".to_string(), ProtoValue {
        kind: Some(Kind::NumberValue(quantity)),
    });
    data.insert("total".to_string(), ProtoValue {
        kind: Some(Kind::StringValue(total.to_string())),
    });
    data.insert("percentage".to_string(), ProtoValue {
        kind: Some(Kind::StringValue("100.00".to_string())),
    });

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

    let response = post_table_data(pool, request, indexer_tx).await?;
    assert!(response.success);
    println!("✅ Initial record created with ID: {}", response.inserted_id);
    Ok(response.inserted_id)
}

#[sqlx::test]
async fn test_put_complex_formula_validation_via_handlers(pool: PgPool) {
    println!("🚀 Starting complex formula validation test via handlers");

    let indexer_tx = create_indexer_channel().await;

    // 1. Create table via handler (simulating API call)
    setup_test_schema_via_handler(&pool, "test_put_complex_handlers", "order")
        .await
        .expect("Failed to create table schema");

    // 2. Add validation script via handler (simulating API call)
    add_validation_script_via_handler(
        &pool,
        "test_put_complex_handlers",
        "order",
        "total",
        "(+ (* @price @quantity) (* (* @price @quantity) 0.08))", // Total with 8% tax
    )
    .await
    .expect("Failed to add validation script");

    // 3. Create initial record via handler with CORRECT initial values
    // For price=10.00, quantity=1: total should be (10*1) + (10*1*0.08) = 10.80
    let record_id = create_initial_record_via_handler(
        &pool,
        "test_put_complex_handlers",
        "order",
        &indexer_tx,
        "10.00",   // price
        1.0,       // quantity  
        "10.80",   // total (correct calculation: 10 + 0.8 = 10.80)
    )
    .await
    .expect("Failed to create initial record");

    // 4. Update record via handler (simulating API call) - this should trigger validation
    println!("🔄 Attempting to update record...");
    println!("New values: price=100.00, quantity=2.0, total=216.00");
    println!("Expected calculation: (100.00 × 2) + (100.00 × 2 × 0.08) = 200 + 16 = 216.00");

    let mut update_data = HashMap::new();
    update_data.insert("price".to_string(), ProtoValue {
        kind: Some(Kind::StringValue("100.00".to_string())),
    });
    update_data.insert("quantity".to_string(), ProtoValue {
        kind: Some(Kind::NumberValue(2.0)),
    });
    update_data.insert("total".to_string(), ProtoValue {
        kind: Some(Kind::StringValue("216.00".to_string())),
    });

    let put_request = PutTableDataRequest {
        profile_name: "test_put_complex_handlers".to_string(),
        table_name: "order".to_string(),
        id: record_id,
        data: update_data,
    };

    // This should succeed if the validation works correctly
    let response = put_table_data(&pool, put_request, &indexer_tx).await;

    match response {
        Ok(resp) => {
            assert!(resp.success);
            println!("✅ Test passed! Update successful - validation worked correctly");
        }
        Err(e) => {
            println!("❌ Test failed with error: {:?}", e);
            println!("Error details: {}", e.message());
            panic!("Update failed: {:?}", e);
        }
    }
}

#[sqlx::test]
async fn test_put_basic_arithmetic_validation_via_handlers(pool: PgPool) {
    println!("🚀 Starting basic arithmetic test via handlers");

    let indexer_tx = create_indexer_channel().await;

    // Create table
    setup_test_schema_via_handler(&pool, "test_put_arithmetic_handlers", "invoice")
        .await
        .expect("Failed to create table schema");

    // Add simple multiplication script
    add_validation_script_via_handler(
        &pool,
        "test_put_arithmetic_handlers",
        "invoice",
        "total",
        "(* @price @quantity)", // Simple: Total = Price × Quantity
    )
    .await
    .expect("Failed to add validation script");

    // Create initial record with correct values
    // For price=10.00, quantity=1: total should be 10*1 = 10.00
    let record_id = create_initial_record_via_handler(
        &pool,
        "test_put_arithmetic_handlers",
        "invoice",
        &indexer_tx,
        "10.00",   // price
        1.0,       // quantity
        "10.00",   // total (correct: 10*1 = 10.00)
    )
    .await
    .expect("Failed to create initial record");

    // Update with correct calculation
    println!("🔄 Testing correct calculation: 25.50 × 3 = 76.50");

    let mut update_data = HashMap::new();
    update_data.insert("price".to_string(), ProtoValue {
        kind: Some(Kind::StringValue("25.50".to_string())),
    });
    update_data.insert("quantity".to_string(), ProtoValue {
        kind: Some(Kind::NumberValue(3.0)),
    });
    update_data.insert("total".to_string(), ProtoValue {
        kind: Some(Kind::StringValue("76.50".to_string())),
    });

    let put_request = PutTableDataRequest {
        profile_name: "test_put_arithmetic_handlers".to_string(),
        table_name: "invoice".to_string(),
        id: record_id,
        data: update_data,
    };

    let response = put_table_data(&pool, put_request, &indexer_tx).await;
    assert!(response.is_ok());
    assert!(response.unwrap().success);

    println!("✅ Basic arithmetic test passed via handlers");
}

#[sqlx::test]
async fn test_put_arithmetic_validation_failure_via_handlers(pool: PgPool) {
    println!("🚀 Starting arithmetic failure test via handlers");

    let indexer_tx = create_indexer_channel().await;

    // Create table
    setup_test_schema_via_handler(&pool, "test_put_arithmetic_fail_handlers", "invoice")
        .await
        .expect("Failed to create table schema");

    // Add validation script
    add_validation_script_via_handler(
        &pool,
        "test_put_arithmetic_fail_handlers",
        "invoice",
        "total",
        "(* @price @quantity)",
    )
    .await
    .expect("Failed to add validation script");

    // Create initial record with correct values
    let record_id = create_initial_record_via_handler(
        &pool,
        "test_put_arithmetic_fail_handlers",
        "invoice",
        &indexer_tx,
        "10.00",   // price
        1.0,       // quantity
        "10.00",   // total (correct: 10*1 = 10.00)
    )
    .await
    .expect("Failed to create initial record");

    // Update with INCORRECT calculation (should fail)
    println!("🔄 Testing incorrect calculation: 25.50 × 3 = 76.50, but providing 70.00 (should fail)");

    let mut update_data = HashMap::new();
    update_data.insert("price".to_string(), ProtoValue {
        kind: Some(Kind::StringValue("25.50".to_string())),
    });
    update_data.insert("quantity".to_string(), ProtoValue {
        kind: Some(Kind::NumberValue(3.0)),
    });
    update_data.insert("total".to_string(), ProtoValue {
        kind: Some(Kind::StringValue("70.00".to_string())), // WRONG! Should be 76.50
    });

    let put_request = PutTableDataRequest {
        profile_name: "test_put_arithmetic_fail_handlers".to_string(),
        table_name: "invoice".to_string(),
        id: record_id,
        data: update_data,
    };

    let response = put_table_data(&pool, put_request, &indexer_tx).await;

    // This should fail with validation error
    assert!(response.is_err());
    let error = response.unwrap_err();
    assert_eq!(error.code(), tonic::Code::InvalidArgument);
    let error_msg = error.message();
    assert!(error_msg.contains("Validation failed"));
    assert!(error_msg.contains("Script calculated '76.50'"));
    assert!(error_msg.contains("but user provided '70.00'"));

    println!("✅ Validation failure test passed - correctly rejected wrong calculation");
}
