// tests/tables_data/handlers/delete_table_data_test.rs

use rstest::{fixture, rstest};
use sqlx::{PgPool, Row};
use std::collections::HashMap;
use std::sync::Arc;
use tokio::sync::{mpsc, Mutex};
use serde_json::json;
use chrono::Utc;
use futures::future::join_all;
use prost_types::{value::Kind, Value};
use rand::Rng;
use rand::distr::Alphanumeric; // Corrected import

// Common imports from other modules
use common::proto::komp_ac::table_definition::{
    PostTableDefinitionRequest, ColumnDefinition as TableColumnDefinition, TableLink,
};
use common::proto::komp_ac::tables_data::{
    DeleteTableDataRequest, DeleteTableDataResponse, PostTableDataRequest, PutTableDataRequest,
};
use server::indexer::IndexCommand;
use server::table_definition::handlers::post_table_definition;
use server::tables_data::handlers::{delete_table_data, post_table_data, put_table_data};
use crate::common::setup_test_db;

lazy_static::lazy_static! {
    static ref TEST_MUTEX: Arc<Mutex<()>> = Arc::new(Mutex::new(()));
}

// ========= Test Helpers =========

fn generate_unique_id() -> String {
    rand::rng() // Corrected function call
        .sample_iter(&Alphanumeric)
        .take(8)
        .map(char::from)
        .collect::<String>()
        .to_lowercase()
}

fn string_to_proto_value(s: &str) -> Value {
    Value {
        kind: Some(Kind::StringValue(s.to_string())),
    }
}

// ========= Fixtures =========

#[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
}

#[fixture]
async fn existing_profile(#[future] pool: PgPool) -> (PgPool, String, i64) {
    let pool = pool.await;
    let profile_name = format!("testprofile_{}", Utc::now().timestamp_nanos_opt().unwrap_or_default());
    
    // FIX: The table is `schemas`, not `profiles`.
    let profile = sqlx::query!(
        "INSERT INTO schemas (name) VALUES ($1) RETURNING id",
        profile_name
    )
    .fetch_one(&pool)
    .await
    .unwrap();
    (pool, profile_name, profile.id)
}

#[fixture]
async fn existing_table(
    #[future] existing_profile: (PgPool, String, i64),
) -> (PgPool, String, i64, String) {
    let (pool, profile_name, schema_id) = existing_profile.await;
    let table_name = format!("test_table_{}", Utc::now().timestamp_nanos_opt().unwrap_or_default());

    // Use post_table_definition instead of manual table creation
    let table_def_request = PostTableDefinitionRequest {
        profile_name: profile_name.clone(),
        table_name: table_name.clone(),
        columns: vec![
            TableColumnDefinition {
                name: "test_data".into(),
                field_type: "text".into(),
            }
        ],
        indexes: vec![],
        links: vec![],
    };

    post_table_definition(&pool, table_def_request).await.unwrap();
    (pool, profile_name, schema_id, table_name)
}

#[fixture]
async fn existing_record(
    #[future] existing_table: (PgPool, String, i64, String),
) -> (PgPool, String, String, i64) {
    let (pool, profile_name, _schema_id, table_name) = existing_table.await;
    
    let mut data = HashMap::new();
    data.insert("test_data".to_string(), string_to_proto_value("Test Record"));
    
    let post_req = PostTableDataRequest {
        profile_name: profile_name.clone(),
        table_name: table_name.clone(),
        data,
    };
    
    let (indexer_tx, _indexer_rx) = mpsc::channel(1);
    let response = post_table_data(&pool, post_req, &indexer_tx).await.unwrap();
    
    (pool, profile_name, table_name, response.inserted_id)
}

#[fixture]
async fn existing_deleted_record(
    #[future] existing_table: (PgPool, String, i64, String),
) -> (PgPool, String, String, i64) {
    let (pool, profile_name, _schema_id, table_name) = existing_table.await;
    
    // First create a record
    let mut data = HashMap::new();
    data.insert("test_data".to_string(), string_to_proto_value("Test Deleted Record"));
    
    let post_req = PostTableDataRequest {
        profile_name: profile_name.clone(),
        table_name: table_name.clone(),
        data,
    };
    
    let (indexer_tx, _indexer_rx) = mpsc::channel(1);
    let response = post_table_data(&pool, post_req, &indexer_tx).await.unwrap();
    let record_id = response.inserted_id;
    
    // Then delete it
    let delete_req = DeleteTableDataRequest {
        profile_name: profile_name.clone(),
        table_name: table_name.clone(),
        record_id,
    };
    delete_table_data(&pool, delete_req).await.unwrap();
    
    (pool, profile_name, table_name, record_id)
}

// New fixture for advanced tests
#[derive(Clone)]
struct AdvancedDeleteContext {
    pool: PgPool,
    profile_name: String,
    category_table: String,
    product_table: String,
    indexer_tx: mpsc::Sender<IndexCommand>,
    indexer_rx: Arc<tokio::sync::Mutex<mpsc::Receiver<IndexCommand>>>,
}

#[fixture]
async fn advanced_delete_context() -> AdvancedDeleteContext {
    let pool = setup_test_db().await;
    let unique_id = generate_unique_id();
    let profile_name = format!("adv_del_profile_{}", unique_id);
    let category_table = format!("categories_adv_del_{}", unique_id);
    let product_table = format!("products_adv_del_{}", unique_id);

    let category_def = PostTableDefinitionRequest {
        profile_name: profile_name.clone(),
        table_name: category_table.clone(),
        columns: vec![TableColumnDefinition { name: "name".into(), field_type: "text".into() }],
        links: vec![], indexes: vec![],
    };
    post_table_definition(&pool, category_def).await.unwrap();

    let product_def = PostTableDefinitionRequest {
        profile_name: profile_name.clone(),
        table_name: product_table.clone(),
        columns: vec![TableColumnDefinition { name: "name".into(), field_type: "text".into() }],
        links: vec![TableLink { linked_table_name: category_table.clone(), required: true }],
        indexes: vec![],
    };
    post_table_definition(&pool, product_def).await.unwrap();

    let (tx, rx) = mpsc::channel(100);
    AdvancedDeleteContext {
        pool, profile_name, category_table, product_table,
        indexer_tx: tx,
        indexer_rx: Arc::new(tokio::sync::Mutex::new(rx)),
    }
}

// ========= Basic Tests (from your original file) =========

#[rstest]
#[tokio::test]
async fn test_delete_table_data_success(
    #[future] existing_record: (PgPool, String, String, i64),
) {
    let (pool, profile_name, table_name, record_id) = existing_record.await;
    let request = DeleteTableDataRequest {
        profile_name: profile_name.clone(),
        table_name: table_name.clone(),
        record_id,
    };
    let response = delete_table_data(&pool, request).await.unwrap();
    assert!(response.success);

    let query = format!("SELECT deleted FROM \"{}\".\"{}\" WHERE id = $1", profile_name, table_name);
    let row = sqlx::query(&query).bind(record_id).fetch_one(&pool).await.unwrap();
    assert!(row.get::<bool, _>("deleted"));
}

#[rstest]
#[tokio::test]
async fn test_delete_table_data_profile_not_found(#[future] pool: PgPool) {
    let pool = pool.await;
    let request = DeleteTableDataRequest {
        profile_name: "NonExistentProfile".to_string(),
        table_name: "test_table".to_string(),
        record_id: 1,
    };
    let result = delete_table_data(&pool, request).await;
    assert!(result.is_err());
    assert_eq!(result.unwrap_err().code(), tonic::Code::NotFound);
}

#[rstest]
#[tokio::test]
async fn test_delete_table_data_table_not_found(
    #[future] existing_profile: (PgPool, String, i64),
) {
    let (pool, profile_name, _) = existing_profile.await;
    let request = DeleteTableDataRequest {
        profile_name,
        table_name: "non_existent_table".to_string(),
        record_id: 1,
    };
    let result = delete_table_data(&pool, request).await;
    assert!(result.is_err());
    assert_eq!(result.unwrap_err().code(), tonic::Code::NotFound);
}

#[rstest]
#[tokio::test]
async fn test_delete_table_data_record_not_found(
    #[future] existing_table: (PgPool, String, i64, String),
) {
    let (pool, profile_name, _, table_name) = existing_table.await;
    let request = DeleteTableDataRequest {
        profile_name,
        table_name: table_name.clone(),
        record_id: 9999,
    };
    let response = delete_table_data(&pool, request).await.unwrap();
    assert!(!response.success);
}

#[rstest]
#[tokio::test]
async fn test_delete_table_data_already_deleted(
    #[future] existing_deleted_record: (PgPool, String, String, i64),
) {
    let (pool, profile_name, table_name, record_id) = existing_deleted_record.await;
    let request = DeleteTableDataRequest {
        profile_name,
        table_name: table_name.clone(),
        record_id,
    };
    let response = delete_table_data(&pool, request).await.unwrap();
    assert!(!response.success);
}

#[rstest]
#[tokio::test]
async fn test_delete_table_data_database_error(#[future] closed_pool: PgPool) {
    let closed_pool = closed_pool.await;
    let request = DeleteTableDataRequest {
        profile_name: "test".to_string(),
        table_name: "test".to_string(),
        record_id: 1,
    };
    let result = delete_table_data(&closed_pool, request).await;
    assert!(result.is_err());
    assert_eq!(result.unwrap_err().code(), tonic::Code::Internal);
}

// Include the new, more advanced tests
include!("delete_table_data_test2.rs");
include!("delete_table_data_test3.rs");
