// tests/tables_data/handlers/put_table_data_test5.rs

// ========================================================================
// MISSING TEST SCENARIOS REPLICATED FROM POST TESTS
// ========================================================================

// Fixture to provide a closed database pool, simulating a connection error.
// This is needed for the database error test.
#[fixture]
async fn closed_test_context() -> TestContext {
    let mut context = test_context().await;
    context.pool.close().await;
    context
}

// Test 1: Ensure that an update fails gracefully when the database is unavailable.
#[rstest]
#[tokio::test]
async fn test_update_table_data_database_error(
    #[future] closed_test_context: TestContext,
) {
    // Arrange
    let context = closed_test_context.await;
    // The record ID doesn't matter as the connection is already closed.
    let record_id = 1;

    // Act
    let mut update_data = HashMap::new();
    update_data.insert(
        "firma".to_string(),
        string_to_proto_value("This will fail"),
    );
    let request = PutTableDataRequest {
        profile_name: context.profile_name.clone(),
        table_name: context.table_name.clone(),
        id: record_id,
        data: update_data,
    };
    let result =
        put_table_data(&context.pool, request, &context.indexer_tx).await;

    // Assert
    assert!(result.is_err());
    assert_eq!(result.unwrap_err().code(), tonic::Code::Internal);
}

// Test 2: Ensure that updating a required foreign key to NULL is not allowed.
// This uses the `foreign_key_update_test_context` from `put_table_data_test3.rs`.
#[rstest]
#[tokio::test]
async fn test_update_required_foreign_key_to_null_fails(
    #[future]
    foreign_key_update_test_context: ForeignKeyUpdateTestContext,
) {
    let context = foreign_key_update_test_context.await;

    // Arrange: Create a category and a product linked to it.
    let mut category_data = HashMap::new();
    category_data
        .insert("name".to_string(), string_to_proto_value("Test Category"));
    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,
        &context.indexer_tx,
    )
    .await
    .unwrap();
    let category_id = category_response.inserted_id;

    let mut product_data = HashMap::new();
    product_data
        .insert("name".to_string(), string_to_proto_value("Test Product"));
    product_data.insert(
        format!("{}_id", context.category_table),
        Value { kind: Some(Kind::NumberValue(category_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, &context.indexer_tx)
            .await
            .unwrap();
    let product_id = product_response.inserted_id;

    // Act: Attempt to update the product's required foreign key to NULL.
    let mut update_data = HashMap::new();
    update_data.insert(
        format!("{}_id", context.category_table),
        Value { kind: Some(Kind::NullValue(0)) },
    );

    let update_request = PutTableDataRequest {
        profile_name: context.profile_name.clone(),
        table_name: context.product_table.clone(),
        id: product_id,
        data: update_data,
    };

    let result =
        put_table_data(&context.pool, update_request, &context.indexer_tx)
            .await;

    // Assert: The operation should fail due to a database constraint.
    assert!(
        result.is_err(),
        "Update of required foreign key to NULL should fail"
    );
    let err = result.unwrap_err();
    // The database will likely return a NOT NULL violation, which our handler
    // wraps as an Internal error.
    assert_eq!(err.code(), tonic::Code::Internal);
    assert!(err.message().contains("Update failed"));
}

// tests/tables_data/handlers/put_table_data_test6.rs

// ========================================================================
// MISSING DATA TYPE VALIDATION TESTS FOR PUT HANDLER
// ========================================================================

// Note: These tests are replicated from post_table_data_test3.rs to ensure
// the PUT handler has the same level of type validation coverage as the POST handler.

#[rstest]
#[tokio::test]
async fn test_update_type_mismatch_string_for_integer(
    #[future] data_type_test_context: DataTypeTestContext,
) {
    // Arrange
    let context = data_type_test_context.await;
    let record_id = create_initial_data_type_record(&context).await;

    // Act: Attempt to update an integer column with a string value.
    let mut update_data = HashMap::new();
    update_data.insert(
        "my_bigint".to_string(),
        create_string_value("not-an-integer"),
    );

    let request = PutTableDataRequest {
        profile_name: context.profile_name.clone(),
        table_name: context.table_name.clone(),
        id: record_id,
        data: update_data,
    };

    let result =
        put_table_data(&context.pool, request, &context.indexer_tx).await;

    // Assert
    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_update_type_mismatch_number_for_boolean(
    #[future] data_type_test_context: DataTypeTestContext,
) {
    // Arrange
    let context = data_type_test_context.await;
    let record_id = create_initial_data_type_record(&context).await;

    // Act: Attempt to update a boolean column with a number value.
    let mut update_data = HashMap::new();
    update_data.insert("my_bool".to_string(), create_number_value(1.0));

    let request = PutTableDataRequest {
        profile_name: context.profile_name.clone(),
        table_name: context.table_name.clone(),
        id: record_id,
        data: update_data,
    };

    let result =
        put_table_data(&context.pool, request, &context.indexer_tx).await;

    // Assert
    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_update_with_various_valid_timestamp_formats(
    #[future] data_type_test_context: DataTypeTestContext,
) {
    // Arrange
    let context = data_type_test_context.await;
    let record_id = create_initial_data_type_record(&context).await;

    let valid_timestamps = vec![
        "2025-06-24T18:30:00Z",
        "2023-01-01T00:00:00+00:00",
        "2024-02-29T12:00:00.123456Z",
        "1999-12-31T23:59:59.999Z",
    ];

    for timestamp_str in valid_timestamps {
        // Act
        let mut update_data = HashMap::new();
        update_data.insert(
            "my_timestamp".to_string(),
            create_string_value(timestamp_str),
        );

        let request = PutTableDataRequest {
            profile_name: context.profile_name.clone(),
            table_name: context.table_name.clone(),
            id: record_id,
            data: update_data,
        };

        let result =
            put_table_data(&context.pool, request, &context.indexer_tx).await;

        // Assert
        assert!(
            result.is_ok(),
            "Update should succeed for valid timestamp format: {}",
            timestamp_str
        );

        // Verify the value was stored correctly
        let stored_timestamp: chrono::DateTime<chrono::Utc> =
            sqlx::query_scalar(&format!(
                r#"SELECT my_timestamp FROM "{}"."{}" WHERE id = $1"#,
                context.profile_name, context.table_name
            ))
            .bind(record_id)
            .fetch_one(&context.pool)
            .await
            .unwrap();

        let expected_timestamp =
            chrono::DateTime::parse_from_rfc3339(timestamp_str)
                .unwrap()
                .with_timezone(&chrono::Utc);
        assert_eq!(stored_timestamp, expected_timestamp);
    }
}
