// tests/tables_data/handlers/get_table_data_count_test2.rs

#[rstest]
#[tokio::test]
async fn test_schema_with_special_characters(#[future] pool: PgPool) {
    let pool = pool.await;
    let profile_name = "test_underscore_profile";
    let table_name = "test_underscore_table";

    // Use table definition handler to create the table
    let request = PostTableDefinitionRequest {
        profile_name: profile_name.to_string(),
        table_name: table_name.to_string(),
        columns: vec![
            ColumnDefinition {
                name: "name".to_string(),
                field_type: "text".to_string(),
            }
        ],
        indexes: vec![],
        links: vec![],
    };

    post_table_definition(&pool, request).await.unwrap();

    // Insert test data
    let mut tx = pool.begin().await.unwrap();
    sqlx::query(&format!(
        r#"INSERT INTO "{}"."{}" (name) VALUES ('Test Data')"#,
        profile_name, table_name
    ))
    .execute(&mut *tx)
    .await
    .unwrap();
    tx.commit().await.unwrap();

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

    let response = get_table_data_count(&pool, request).await.unwrap();
    assert_eq!(response.count, 1);

    // Cleanup
    let schema_id = sqlx::query_scalar!(
        "SELECT id FROM schemas WHERE name = $1",
        profile_name
    )
    .fetch_one(&pool)
    .await
    .unwrap();
    
    cleanup_test_environment(&pool, schema_id, profile_name).await;
}

#[rstest]
#[tokio::test]
async fn test_large_dataset_count(#[future] pool: PgPool) {
    let pool = pool.await;
    let profile_name = format!("large_test_{}", Utc::now().timestamp_nanos_opt().unwrap_or(0));
    let table_name = "large_dataset_table";

    // Use table definition handler to create the table
    let request = PostTableDefinitionRequest {
        profile_name: profile_name.clone(),
        table_name: table_name.to_string(),
        columns: vec![
            ColumnDefinition {
                name: "value".to_string(),
                field_type: "integer".to_string(),
            }
        ],
        indexes: vec![],
        links: vec![],
    };

    post_table_definition(&pool, request).await.unwrap();

    // Insert 1000 records
    let mut tx = pool.begin().await.unwrap();
    for i in 1..=1000 {
        sqlx::query(&format!(
            r#"INSERT INTO "{}"."{}" (value) VALUES ({})"#,
            profile_name, table_name, i
        ))
        .execute(&mut *tx)
        .await
        .unwrap();
    }

    // Mark some as deleted
    sqlx::query(&format!(
        r#"UPDATE "{}"."{}" SET deleted = true WHERE value <= 100"#,
        profile_name, table_name
    ))
    .execute(&mut *tx)
    .await
    .unwrap();
    tx.commit().await.unwrap();

    let request = GetTableDataCountRequest {
        profile_name: profile_name.clone(),
        table_name: table_name.to_string(),
    };

    let response = get_table_data_count(&pool, request).await.unwrap();
    assert_eq!(response.count, 900); // 1000 - 100 deleted

    // Cleanup
    let schema_id = sqlx::query_scalar!(
        "SELECT id FROM schemas WHERE name = $1",
        profile_name
    )
    .fetch_one(&pool)
    .await
    .unwrap();
    
    cleanup_test_environment(&pool, schema_id, &profile_name).await;
}

#[rstest]
#[tokio::test]
async fn test_mixed_deleted_states(#[future] pool: PgPool) {
    let pool = pool.await;
    let profile_name = format!("mixed_test_{}", Utc::now().timestamp_nanos_opt().unwrap_or(0));
    let table_name = "mixed_states_table";

    // Use table definition handler to create the table
    let request = PostTableDefinitionRequest {
        profile_name: profile_name.clone(),
        table_name: table_name.to_string(),
        columns: vec![
            ColumnDefinition {
                name: "status".to_string(),
                field_type: "text".to_string(),
            }
        ],
        indexes: vec![],
        links: vec![],
    };

    post_table_definition(&pool, request).await.unwrap();

    // Insert various combinations
    let mut tx = pool.begin().await.unwrap();
    sqlx::query(&format!(
        r#"INSERT INTO "{}"."{}" (status, deleted) VALUES ('active', false)"#,
        profile_name, table_name
    ))
    .execute(&mut *tx)
    .await
    .unwrap();

    sqlx::query(&format!(
        r#"INSERT INTO "{}"."{}" (status, deleted) VALUES ('active', true)"#,
        profile_name, table_name
    ))
    .execute(&mut *tx)
    .await
    .unwrap();

    sqlx::query(&format!(
        r#"INSERT INTO "{}"."{}" (status, deleted) VALUES ('inactive', false)"#,
        profile_name, table_name
    ))
    .execute(&mut *tx)
    .await
    .unwrap();

    sqlx::query(&format!(
        r#"INSERT INTO "{}"."{}" (status, deleted) VALUES ('inactive', true)"#,
        profile_name, table_name
    ))
    .execute(&mut *tx)
    .await
    .unwrap();
    tx.commit().await.unwrap();

    let request = GetTableDataCountRequest {
        profile_name: profile_name.clone(),
        table_name: table_name.to_string(),
    };

    let response = get_table_data_count(&pool, request).await.unwrap();
    assert_eq!(response.count, 2); // Only non-deleted records

    // Cleanup
    let schema_id = sqlx::query_scalar!(
        "SELECT id FROM schemas WHERE name = $1",
        profile_name
    )
    .fetch_one(&pool)
    .await
    .unwrap();
    
    cleanup_test_environment(&pool, schema_id, &profile_name).await;
}

#[rstest]
#[tokio::test]
async fn test_case_sensitivity_in_names(#[future] pool: PgPool) {
    let pool = pool.await;
    let profile_name = "case_test_schema";
    let table_name = "case_test_table";

    // Use table definition handler to create the table
    let request = PostTableDefinitionRequest {
        profile_name: profile_name.to_string(),
        table_name: table_name.to_string(),
        columns: vec![
            ColumnDefinition {
                name: "data".to_string(),
                field_type: "text".to_string(),
            }
        ],
        indexes: vec![],
        links: vec![],
    };

    post_table_definition(&pool, request).await.unwrap();

    let mut tx = pool.begin().await.unwrap();
    sqlx::query(&format!(
        r#"INSERT INTO "{}"."{}" (data) VALUES ('test data')"#,
        profile_name, table_name
    ))
    .execute(&mut *tx)
    .await
    .unwrap();
    tx.commit().await.unwrap();

    // Test exact case
    let request = GetTableDataCountRequest {
        profile_name: profile_name.to_string(),
        table_name: table_name.to_string(),
    };
    let response = get_table_data_count(&pool, request).await.unwrap();
    assert_eq!(response.count, 1);

    // Test wrong case should fail
    let wrong_case_request = GetTableDataCountRequest {
        profile_name: "CASE_TEST_SCHEMA".to_string(),
        table_name: table_name.to_string(),
    };
    let wrong_case_result = get_table_data_count(&pool, wrong_case_request).await;
    assert!(wrong_case_result.is_err());
    assert_eq!(wrong_case_result.unwrap_err().code(), tonic::Code::NotFound);

    // Cleanup
    let schema_id = sqlx::query_scalar!(
        "SELECT id FROM schemas WHERE name = $1",
        profile_name
    )
    .fetch_one(&pool)
    .await
    .unwrap();
    
    cleanup_test_environment(&pool, schema_id, profile_name).await;
}

#[rstest]
#[tokio::test]
async fn test_concurrent_count_requests(#[future] pool: PgPool) {
    let pool = pool.await;
    let profile_name = format!("concurrent_test_{}", Utc::now().timestamp_nanos_opt().unwrap_or(0));
    let table_name = "concurrent_table";

    // Use table definition handler to create the table
    let request = PostTableDefinitionRequest {
        profile_name: profile_name.clone(),
        table_name: table_name.to_string(),
        columns: vec![
            ColumnDefinition {
                name: "counter".to_string(),
                field_type: "integer".to_string(),
            }
        ],
        indexes: vec![],
        links: vec![],
    };

    post_table_definition(&pool, request).await.unwrap();

    // Insert initial data
    let mut tx = pool.begin().await.unwrap();
    for i in 1..=50 {
        sqlx::query(&format!(
            r#"INSERT INTO "{}"."{}" (counter) VALUES ({})"#,
            profile_name, table_name, i
        ))
        .execute(&mut *tx)
        .await
        .unwrap();
    }
    tx.commit().await.unwrap();

    // Run multiple concurrent count requests
    let mut handles = vec![];
    for _ in 0..10 {
        let pool_clone = pool.clone();
        let profile_name_clone = profile_name.clone();
        let table_name_clone = table_name.to_string();

        let handle = tokio::spawn(async move {
            let request = GetTableDataCountRequest {
                profile_name: profile_name_clone,
                table_name: table_name_clone,
            };
            get_table_data_count(&pool_clone, request).await
        });
        handles.push(handle);
    }

    // Wait for all requests to complete
    for handle in handles {
        let response = handle.await.unwrap().unwrap();
        assert_eq!(response.count, 50);
    }

    // Cleanup
    let schema_id = sqlx::query_scalar!(
        "SELECT id FROM schemas WHERE name = $1",
        profile_name
    )
    .fetch_one(&pool)
    .await
    .unwrap();
    
    cleanup_test_environment(&pool, schema_id, &profile_name).await;
}

#[rstest]
#[tokio::test]
async fn test_table_without_physical_existence(#[future] pool: PgPool) {
    let pool = pool.await;
    let profile_name = format!("missing_table_test_{}", Utc::now().timestamp_nanos_opt().unwrap_or(0));
    let table_name = "missing_physical_table";

    // Create table definition but then manually drop the physical table
    let request = PostTableDefinitionRequest {
        profile_name: profile_name.clone(),
        table_name: table_name.to_string(),
        columns: vec![
            ColumnDefinition {
                name: "data".to_string(),
                field_type: "text".to_string(),
            }
        ],
        indexes: vec![],
        links: vec![],
    };

    post_table_definition(&pool, request).await.unwrap();

    // Manually drop the physical table while keeping the definition
    let mut tx = pool.begin().await.unwrap();
    sqlx::query(&format!(
        r#"DROP TABLE "{}"."{}" CASCADE"#,
        profile_name, table_name
    ))
    .execute(&mut *tx)
    .await
    .unwrap();
    tx.commit().await.unwrap();

    let request = GetTableDataCountRequest {
        profile_name: profile_name.clone(),
        table_name: table_name.to_string(),
    };

    let result = get_table_data_count(&pool, request).await;
    assert!(result.is_err());
    assert_eq!(result.unwrap_err().code(), tonic::Code::Internal);

    // Cleanup
    let schema_id = sqlx::query_scalar!(
        "SELECT id FROM schemas WHERE name = $1",
        profile_name
    )
    .fetch_one(&pool)
    .await
    .unwrap();
    
    cleanup_test_environment(&pool, schema_id, &profile_name).await;
}

#[rstest]
#[tokio::test]
async fn test_numeric_column_types_count(#[future] pool: PgPool) {
    let pool = pool.await;
    let profile_name = format!("numeric_test_{}", Utc::now().timestamp_nanos_opt().unwrap_or(0));
    let table_name = "numeric_types_table";

    // Use table definition handler to create the table with various numeric types
    let request = PostTableDefinitionRequest {
        profile_name: profile_name.clone(),
        table_name: table_name.to_string(),
        columns: vec![
            ColumnDefinition {
                name: "big_number".to_string(),
                field_type: "bigint".to_string(),
            },
            ColumnDefinition {
                name: "small_number".to_string(),
                field_type: "integer".to_string(),
            },
            ColumnDefinition {
                name: "decimal_number".to_string(),
                field_type: "decimal(10,2)".to_string(),
            },
            ColumnDefinition {
                name: "timestamp_col".to_string(),
                field_type: "timestamptz".to_string(),
            }
        ],
        indexes: vec![],
        links: vec![],
    };

    post_table_definition(&pool, request).await.unwrap();

    let mut tx = pool.begin().await.unwrap();
    sqlx::query(&format!(
        r#"INSERT INTO "{}"."{}" (big_number, small_number, decimal_number, timestamp_col)
           VALUES (9223372036854775807, 2147483647, 99999999.99, NOW())"#,
        profile_name, table_name
    ))
    .execute(&mut *tx)
    .await
    .unwrap();

    sqlx::query(&format!(
        r#"INSERT INTO "{}"."{}" (big_number, small_number, decimal_number, timestamp_col, deleted)
           VALUES (1, 1, 1.00, NOW(), true)"#,
        profile_name, table_name
    ))
    .execute(&mut *tx)
    .await
    .unwrap();
    tx.commit().await.unwrap();

    let request = GetTableDataCountRequest {
        profile_name: profile_name.clone(),
        table_name: table_name.to_string(),
    };

    let response = get_table_data_count(&pool, request).await.unwrap();
    assert_eq!(response.count, 1); // Only the non-deleted record

    // Cleanup
    let schema_id = sqlx::query_scalar!(
        "SELECT id FROM schemas WHERE name = $1",
        profile_name
    )
    .fetch_one(&pool)
    .await
    .unwrap();
    
    cleanup_test_environment(&pool, schema_id, &profile_name).await;
}

#[rstest]
#[tokio::test]
async fn test_indexed_columns_count(#[future] pool: PgPool) {
    let pool = pool.await;
    let profile_name = format!("index_test_{}", Utc::now().timestamp_nanos_opt().unwrap_or(0));
    let table_name = "indexed_table";

    // Use table definition handler to create the table with indexes
    let request = PostTableDefinitionRequest {
        profile_name: profile_name.clone(),
        table_name: table_name.to_string(),
        columns: vec![
            ColumnDefinition {
                name: "searchable_field".to_string(),
                field_type: "text".to_string(),
            },
            ColumnDefinition {
                name: "category".to_string(),
                field_type: "text".to_string(),
            }
        ],
        indexes: vec!["searchable_field".to_string(), "category".to_string()],
        links: vec![],
    };

    post_table_definition(&pool, request).await.unwrap();

    let mut tx = pool.begin().await.unwrap();
    for i in 1..=20 {
        sqlx::query(&format!(
            r#"INSERT INTO "{}"."{}" (searchable_field, category) VALUES ('data_{}', 'cat_{}')"#,
            profile_name, table_name, i, i % 3
        ))
        .execute(&mut *tx)
        .await
        .unwrap();
    }
    tx.commit().await.unwrap();

    let request = GetTableDataCountRequest {
        profile_name: profile_name.clone(),
        table_name: table_name.to_string(),
    };

    let response = get_table_data_count(&pool, request).await.unwrap();
    assert_eq!(response.count, 20);

    // Cleanup
    let schema_id = sqlx::query_scalar!(
        "SELECT id FROM schemas WHERE name = $1",
        profile_name
    )
    .fetch_one(&pool)
    .await
    .unwrap();
    
    cleanup_test_environment(&pool, schema_id, &profile_name).await;
}
