use serde::{Deserialize, Serialize}; use tauri::State; use crate::db::connection::DbManager; use crate::domains::tombstone::repo; use crate::domains::book::service as book_service; use crate::domains::chapter::service as chapter_service; use crate::domains::character::service as character_service; use crate::domains::location::service as location_service; use crate::domains::world::service as world_service; use crate::domains::incident::service as incident_service; use crate::domains::plotpoint::service as plotpoint_service; use crate::domains::issue::service as issue_service; use crate::domains::spell::service as spell_service; use crate::domains::series::service as series_service; use crate::domains::series_character::service as series_character_service; use crate::domains::series_location::service as series_location_service; use crate::domains::series_world::service as series_world_service; use crate::domains::series_spell::service as series_spell_service; use crate::error::{AppError, AppResult}; use crate::shared::session::SessionState; use crate::shared::types::Lang; /// Runs a tombstone deletion, ignoring NotFound errors (entity already deleted). /// Propagates all other errors (DB corruption, lock failure, etc.). fn apply_tombstone AppResult>(action: F) -> AppResult<()> { match action() { Ok(_) => Ok(()), Err(AppError::NotFound(_)) => Ok(()), Err(AppError::Internal(msg)) if msg.contains("non trouvé") || msg.contains("not found") => Ok(()), Err(e) => Err(e), } } fn get_session(session: &State) -> Result<(String, Lang), AppError> { let session_guard = session.lock().map_err(|e| AppError::Internal(format!("Session lock failed: {}", e)))?; let user_id = session_guard.get_user_id().map_err(|e| AppError::Auth(e))?.to_string(); let lang = session_guard.lang; Ok((user_id, lang)) } #[derive(Serialize)] pub struct TombstoneRecord { pub table_name: String, pub entity_id: String, pub book_id: Option, pub deleted_at: i64, } #[tauri::command] pub fn get_tombstones_since(since: i64, db: State, session: State) -> Result, AppError> { let (user_id, lang) = get_session(&session)?; let db_manager = db.lock().map_err(|e| AppError::Internal(format!("DB lock failed: {}", e)))?; let conn = db_manager.get_connection(&user_id)?; let records = repo::get_deletions_since(conn, &user_id, since, lang)?; Ok(records.into_iter().map(|record| TombstoneRecord { table_name: record.table_name, entity_id: record.entity_id, book_id: record.book_id, deleted_at: record.deleted_at, }).collect()) } #[derive(Deserialize)] pub struct TombstoneInput { pub table_name: String, pub entity_id: String, pub book_id: Option, pub deleted_at: i64, } #[tauri::command] pub fn apply_book_tombstones(tombstones: Vec, db: State, session: State) -> Result<(), AppError> { let (user_id, lang) = get_session(&session)?; let db_manager = db.lock().map_err(|e| AppError::Internal(format!("DB lock failed: {}", e)))?; let conn = db_manager.get_connection(&user_id)?; for tombstone in &tombstones { let book_id = tombstone.book_id.as_deref().unwrap_or(""); let entity_id = tombstone.entity_id.as_str(); let deleted_at = tombstone.deleted_at; match tombstone.table_name.as_str() { "erit_books" => apply_tombstone(|| book_service::remove_book(conn, &user_id, entity_id, deleted_at, lang))?, "book_chapters" => apply_tombstone(|| chapter_service::remove_chapter(conn, &user_id, book_id, entity_id, deleted_at, lang))?, "book_chapter_infos" => apply_tombstone(|| chapter_service::remove_chapter_information(conn, &user_id, book_id, entity_id, deleted_at, lang))?, "book_characters" => apply_tombstone(|| character_service::delete_character(conn, &user_id, book_id, entity_id, deleted_at, lang))?, "book_characters_attributes" => apply_tombstone(|| character_service::delete_attribute(conn, &user_id, book_id, entity_id, deleted_at, lang))?, "book_location" => apply_tombstone(|| location_service::delete_location_section(conn, &user_id, book_id, entity_id, deleted_at, lang))?, "location_element" => apply_tombstone(|| location_service::delete_location_element(conn, &user_id, book_id, entity_id, deleted_at, lang))?, "location_sub_element" => apply_tombstone(|| location_service::delete_location_sub_element(conn, &user_id, book_id, entity_id, deleted_at, lang))?, "book_world_elements" => apply_tombstone(|| world_service::remove_element_from_world(conn, &user_id, book_id, entity_id, deleted_at, lang))?, "book_incidents" => apply_tombstone(|| incident_service::remove_incident(conn, &user_id, book_id, entity_id, deleted_at, lang))?, "book_plot_points" => apply_tombstone(|| plotpoint_service::remove_plot_point(conn, &user_id, book_id, entity_id, deleted_at, lang))?, "book_issues" => apply_tombstone(|| issue_service::remove_issue(conn, &user_id, book_id, entity_id, deleted_at, lang))?, "book_spells" => apply_tombstone(|| spell_service::delete_spell(conn, &user_id, book_id, entity_id, deleted_at, lang))?, "book_spell_tags" => apply_tombstone(|| spell_service::delete_spell_tag(conn, &user_id, book_id, entity_id, deleted_at, lang))?, _ => return Err(AppError::Validation(format!("Unknown tombstone table: {}", tombstone.table_name))), } } Ok(()) } #[tauri::command] pub fn apply_series_tombstones(tombstones: Vec, db: State, session: State) -> Result<(), AppError> { let (user_id, lang) = get_session(&session)?; let db_manager = db.lock().map_err(|e| AppError::Internal(format!("DB lock failed: {}", e)))?; let conn = db_manager.get_connection(&user_id)?; for tombstone in &tombstones { let entity_id = tombstone.entity_id.as_str(); let deleted_at = tombstone.deleted_at; match tombstone.table_name.as_str() { "erit_series" => apply_tombstone(|| series_service::delete_series(conn, &user_id, entity_id, deleted_at, lang))?, "series_books" => { if let Some(ref book_id) = tombstone.book_id { apply_tombstone(|| series_service::remove_book_from_series(conn, &user_id, book_id, entity_id, deleted_at, lang))?; } } "series_characters" => apply_tombstone(|| series_character_service::delete_character(conn, &user_id, entity_id, deleted_at, lang))?, "series_characters_attributes" => apply_tombstone(|| series_character_service::delete_attribute(conn, &user_id, entity_id, deleted_at, lang))?, "series_locations" => apply_tombstone(|| series_location_service::delete_location(conn, &user_id, entity_id, deleted_at, lang))?, "series_location_elements" => apply_tombstone(|| series_location_service::delete_element(conn, &user_id, entity_id, deleted_at, lang))?, "series_location_sub_elements" => apply_tombstone(|| series_location_service::delete_sub_element(conn, &user_id, entity_id, deleted_at, lang))?, "series_world_elements" => apply_tombstone(|| series_world_service::delete_element(conn, &user_id, entity_id, deleted_at, lang))?, "series_spells" => apply_tombstone(|| series_spell_service::delete_spell(conn, &user_id, entity_id, deleted_at, lang))?, "series_spell_tags" => apply_tombstone(|| series_spell_service::delete_tag(conn, &user_id, entity_id, deleted_at, lang))?, _ => return Err(AppError::Validation(format!("Unknown tombstone table: {}", tombstone.table_name))), } } Ok(()) }