// tests/tables_data/handlers/get_table_data_count_test.rs
use rstest::{fixture, rstest};
use tonic;
use sqlx::PgPool;
use common::proto::komp_ac::tables_data::GetTableDataCountRequest;
use common::proto::komp_ac::table_definition::{PostTableDefinitionRequest, ColumnDefinition};
use common::proto::komp_ac::table_definition::TableLink;
use server::tables_data::handlers::get_table_data_count;
use server::table_definition::handlers::post_table_definition;
use crate::common::setup_test_db;
use chrono::Utc;

#[fixture]
async fn pool() -> PgPool {
    setup_test_db().await
}

#[fixture]
async fn closed_pool(#[future] pool: PgPool) -> PgPool {
    let pool = pool.await;
    pool.close().await;
    pool
}

async fn setup_test_environment(pool: &PgPool) -> (String, String, i64) {
    // Create unique profile and table names
    let profile_name = format!("test_profile_{}", Utc::now().timestamp_nanos_opt().unwrap_or(0));
    let table_name = format!("test_table_{}", Utc::now().timestamp_nanos_opt().unwrap_or(0));

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

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

    // Get the schema_id for cleanup
    let schema_id = sqlx::query_scalar!(
        "SELECT id FROM schemas WHERE name = $1",
        profile_name
    )
    .fetch_one(pool)
    .await
    .unwrap();

    (profile_name, table_name, schema_id)
}

async fn cleanup_test_environment(pool: &PgPool, schema_id: i64, profile_name: &str) {
    let mut tx = pool.begin().await.unwrap();

    // Cleanup order matters!
    sqlx::query(&format!(r#"DROP SCHEMA IF EXISTS "{}" CASCADE"#, profile_name))
        .execute(&mut *tx)
        .await
        .unwrap();

    // Delete foreign key relationships first
    sqlx::query!(
        "DELETE FROM table_definition_links WHERE source_table_id IN (SELECT id FROM table_definitions WHERE schema_id = $1) OR linked_table_id IN (SELECT id FROM table_definitions WHERE schema_id = $1)",
        schema_id
    )
    .execute(&mut *tx)
    .await
    .unwrap();

    sqlx::query!("DELETE FROM table_definitions WHERE schema_id = $1", schema_id)
        .execute(&mut *tx)
        .await
        .unwrap();

    sqlx::query!("DELETE FROM schemas WHERE id = $1", schema_id)
        .execute(&mut *tx)
        .await
        .unwrap();

    tx.commit().await.unwrap();
}

#[rstest]
#[tokio::test]
async fn test_returns_correct_count(#[future] pool: PgPool) {
    let pool = pool.await;
    let (profile_name, table_name, schema_id) = setup_test_environment(&pool).await;

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

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

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

    cleanup_test_environment(&pool, schema_id, &profile_name).await;
}

#[rstest]
#[tokio::test]
async fn test_excludes_deleted_records(#[future] pool: PgPool) {
    let pool = pool.await;
    let (profile_name, table_name, schema_id) = setup_test_environment(&pool).await;

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

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

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

    cleanup_test_environment(&pool, schema_id, &profile_name).await;
}

#[rstest]
#[tokio::test]
async fn test_table_not_in_profile(#[future] pool: PgPool) {
    let pool = pool.await;
    let (profile_name, _, schema_id) = setup_test_environment(&pool).await;

    // Test with non-existent table
    let request = GetTableDataCountRequest {
        profile_name: profile_name.clone(),
        table_name: "non_existent_table".to_string(),
    };
    let result = get_table_data_count(&pool, request).await;
    assert!(result.is_err());
    assert_eq!(result.unwrap_err().code(), tonic::Code::NotFound);

    cleanup_test_environment(&pool, schema_id, &profile_name).await;
}

#[rstest]
#[tokio::test]
async fn test_profile_not_found(#[future] pool: PgPool) {
    let pool = pool.await;

    let request = GetTableDataCountRequest {
        profile_name: "nonexistent_profile".to_string(),
        table_name: "adresar".to_string(),
    };
    let result = get_table_data_count(&pool, request).await;

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

#[rstest]
#[tokio::test]
async fn test_database_error(#[future] closed_pool: PgPool) {
    let closed_pool = closed_pool.await;
    let request = GetTableDataCountRequest {
        profile_name: "test".to_string(),
        table_name: "test".to_string(),
    };
    let result = get_table_data_count(&closed_pool, request).await;

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

#[rstest]
#[tokio::test]
async fn test_empty_table_count(#[future] pool: PgPool) {
    let pool = pool.await;
    
    let profile_name = format!("empty_test_{}", Utc::now().timestamp_nanos_opt().unwrap_or(0));
    
    // Use table definition handler to create the table
    let request = PostTableDefinitionRequest {
        profile_name: profile_name.clone(),
        table_name: "adresar".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();

    let request = GetTableDataCountRequest {
        profile_name: profile_name.clone(),
        table_name: "adresar".to_string(),
    };
    let response = get_table_data_count(&pool, request).await.unwrap();

    assert!(response.count >= 0);
    
    // 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;
}

include!("get_table_data_count_test2.rs");
include!("get_table_data_count_test3.rs");
