use rusqlite::Connection;
use serde::{Deserialize, Serialize};

use crate::crypto::encryption::{encrypt_data_with_user_key, decrypt_data_with_user_key, hash_element};
use crate::crypto::key_manager::get_user_encryption_key;
use crate::domains::book::repo;
use crate::domains::chapter::repo as chapter_repo;
use crate::domains::chapter::service as chapter_service;
use crate::domains::tombstone::repo as tombstone_repo;
use crate::domains::user::repo as user_repo;
use crate::error::{AppError, AppResult};
use crate::helpers::{create_unique_id, timestamp_in_seconds};
use crate::shared::types::Lang;

#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
pub struct SyncedBookTools {
    pub last_update: i64,
    pub characters_enabled: bool,
    pub worlds_enabled: bool,
    pub locations_enabled: bool,
    pub spells_enabled: bool,
}

#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
pub struct BookToolsSettings {
    pub characters: bool,
    pub worlds: bool,
    pub locations: bool,
    pub spells: bool,
}

#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
pub struct BookProps {
    pub book_id: String,
    pub book_type: String,
    pub author_id: String,
    pub title: String,
    pub sub_title: String,
    pub summary: String,
    pub serie_id: i64,
    pub series_id: Option<String>,
    pub desired_release_date: String,
    pub desired_word_count: i64,
    pub word_count: i64,
    pub cover_image: String,
    pub book_meta: Option<String>,
    pub tools: Option<BookToolsSettings>,
}

pub use chapter_service::CompleteChapterContent;

#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
pub struct CompleteBookData {
    pub book_id: String,
    pub title: String,
    pub sub_title: String,
    pub summary: String,
    pub cover_image: String,
    pub user_infos: BookUserInfos,
    pub chapters: Vec<CompleteChapterContent>,
}

#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
pub struct BookUserInfos {
    pub username: String,
    pub author_name: String,
}

#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct BookSyncCompare {
    pub id: String,
    pub chapters: Vec<String>,
    pub chapter_contents: Vec<String>,
    pub chapter_infos: Vec<String>,
    pub characters: Vec<String>,
    pub character_attributes: Vec<String>,
    pub locations: Vec<String>,
    pub location_elements: Vec<String>,
    pub location_sub_elements: Vec<String>,
    pub worlds: Vec<String>,
    pub world_elements: Vec<String>,
    pub incidents: Vec<String>,
    pub plot_points: Vec<String>,
    pub issues: Vec<String>,
    pub act_summaries: Vec<String>,
    pub guide_line: bool,
    pub ai_guide_line: bool,
    #[serde(rename = "bookTools")]
    pub _book_tools: bool,
    pub spells: Vec<String>,
    pub spell_tags: Vec<String>,
}

#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
pub struct SyncedIncident {
    pub id: String,
    pub name: String,
    pub last_update: i64,
}

#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
pub struct SyncedPlotPoint {
    pub id: String,
    pub name: String,
    pub last_update: i64,
}

#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
pub struct SyncedIssue {
    pub id: String,
    pub name: String,
    pub last_update: i64,
}

#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
pub struct SyncedActSummary {
    pub id: String,
    pub last_update: i64,
}

#[derive(Serialize)]
pub struct SyncedGuideLine {
    pub last_update: i64,
}

#[derive(Serialize)]
pub struct SyncedAIGuideLine {
    pub last_update: i64,
}

#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
pub struct SyncedSpell {
    pub id: String,
    pub name: String,
    pub last_update: i64,
}

#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
pub struct SyncedSpellTag {
    pub id: String,
    pub name: String,
    pub last_update: i64,
}

#[derive(Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CompleteBook {
    pub erit_books: Vec<repo::EritBooksTable>,
    pub act_summaries: Vec<BookActSummariesTable>,
    pub ai_guide_line: Vec<BookAIGuideLineTable>,
    pub chapters: Vec<BookChaptersTable>,
    pub chapter_contents: Vec<BookChapterContentTable>,
    pub chapter_infos: Vec<BookChapterInfosTable>,
    pub characters: Vec<BookCharactersTable>,
    pub character_attributes: Vec<BookCharactersAttributesTable>,
    pub guide_line: Vec<BookGuideLineTable>,
    pub incidents: Vec<BookIncidentsTable>,
    pub issues: Vec<BookIssuesTable>,
    pub locations: Vec<BookLocationTable>,
    pub plot_points: Vec<BookPlotPointsTable>,
    pub worlds: Vec<BookWorldTable>,
    pub world_elements: Vec<BookWorldElementsTable>,
    pub location_elements: Vec<LocationElementTable>,
    pub location_sub_elements: Vec<LocationSubElementTable>,
    pub book_tools: Vec<repo::BookToolsTable>,
    pub spells: Vec<BookSpellsTable>,
    pub spell_tags: Vec<BookSpellTagsTable>,
}

#[derive(Serialize, Deserialize)]
pub struct BookActSummariesTable {
    pub act_sum_id: String,
    pub book_id: String,
    pub user_id: String,
    pub act_index: i64,
    pub last_update: i64,
    pub summary: Option<String>,
}

#[derive(Serialize, Deserialize)]
pub struct BookAIGuideLineTable {
    pub user_id: String,
    pub book_id: String,
    pub global_resume: Option<String>,
    pub themes: Option<String>,
    pub verbe_tense: Option<i64>,
    pub narrative_type: Option<i64>,
    pub langue: Option<i64>,
    pub dialogue_type: Option<i64>,
    pub tone: Option<String>,
    pub atmosphere: Option<String>,
    pub current_resume: Option<String>,
    pub last_update: i64,
}

#[derive(Serialize, Deserialize)]
pub struct BookChaptersTable {
    pub chapter_id: String,
    pub book_id: String,
    pub author_id: String,
    pub title: String,
    pub hashed_title: String,
    pub words_count: Option<i64>,
    pub chapter_order: i64,
    pub last_update: i64,
}

#[derive(Serialize, Deserialize)]
pub struct BookChapterContentTable {
    pub content_id: String,
    pub chapter_id: String,
    pub author_id: String,
    pub version: i64,
    pub content: Option<serde_json::Value>,
    pub words_count: Option<i64>,
    pub time_on_it: Option<i64>,
    pub last_update: i64,
}

#[derive(Serialize, Deserialize)]
pub struct BookChapterInfosTable {
    pub chapter_info_id: String,
    pub chapter_id: String,
    pub act_id: Option<i64>,
    pub incident_id: Option<String>,
    pub plot_point_id: Option<String>,
    pub book_id: String,
    pub author_id: String,
    pub summary: Option<String>,
    pub goal: Option<String>,
    pub last_update: i64,
}

#[derive(Serialize, Deserialize)]
pub struct BookCharactersTable {
    pub character_id: String,
    pub book_id: String,
    pub user_id: String,
    pub first_name: String,
    pub last_name: Option<String>,
    pub nickname: Option<String>,
    pub age: Option<String>,
    pub gender: Option<String>,
    pub species: Option<String>,
    pub nationality: Option<String>,
    pub status: Option<String>,
    pub category: String,
    pub title: Option<String>,
    pub image: Option<String>,
    pub role: Option<String>,
    pub biography: Option<String>,
    pub history: Option<String>,
    pub speech_pattern: Option<String>,
    pub catchphrase: Option<String>,
    pub residence: Option<String>,
    pub notes: Option<String>,
    pub color: Option<String>,
    pub last_update: i64,
}

#[derive(Serialize, Deserialize)]
pub struct BookCharactersAttributesTable {
    pub attr_id: String,
    pub character_id: String,
    pub user_id: String,
    pub attribute_name: String,
    pub attribute_value: String,
    pub last_update: i64,
}

#[derive(Serialize, Deserialize)]
pub struct BookGuideLineTable {
    pub user_id: String,
    pub book_id: String,
    pub tone: Option<String>,
    pub atmosphere: Option<String>,
    pub writing_style: Option<String>,
    pub themes: Option<String>,
    pub symbolism: Option<String>,
    pub motifs: Option<String>,
    pub narrative_voice: Option<String>,
    pub pacing: Option<String>,
    pub intended_audience: Option<String>,
    pub key_messages: Option<String>,
    pub last_update: i64,
}

#[derive(Serialize, Deserialize)]
pub struct BookIncidentsTable {
    pub incident_id: String,
    pub author_id: String,
    pub book_id: String,
    pub title: String,
    pub hashed_title: String,
    pub summary: Option<String>,
    pub last_update: i64,
}

#[derive(Serialize, Deserialize)]
pub struct BookIssuesTable {
    pub issue_id: String,
    pub author_id: String,
    pub book_id: String,
    pub name: String,
    pub hashed_issue_name: String,
    pub last_update: i64,
}

#[derive(Serialize, Deserialize)]
pub struct BookLocationTable {
    pub loc_id: String,
    pub book_id: String,
    pub user_id: String,
    pub loc_name: String,
    pub loc_original_name: String,
    pub last_update: i64,
}

#[derive(Serialize, Deserialize)]
pub struct BookPlotPointsTable {
    pub plot_point_id: String,
    pub title: String,
    pub hashed_title: String,
    pub summary: Option<String>,
    pub linked_incident_id: Option<String>,
    pub author_id: String,
    pub book_id: String,
    pub last_update: i64,
}

#[derive(Serialize, Deserialize)]
pub struct BookWorldTable {
    pub world_id: String,
    pub name: String,
    pub hashed_name: String,
    pub author_id: String,
    pub book_id: String,
    pub history: Option<String>,
    pub politics: Option<String>,
    pub economy: Option<String>,
    pub religion: Option<String>,
    pub languages: Option<String>,
    pub last_update: i64,
}

#[derive(Serialize, Deserialize)]
pub struct BookWorldElementsTable {
    pub element_id: String,
    pub world_id: String,
    pub user_id: String,
    pub element_type: i64,
    pub name: String,
    pub original_name: String,
    pub description: Option<String>,
    pub last_update: i64,
}

#[derive(Serialize, Deserialize)]
pub struct LocationElementTable {
    pub element_id: String,
    pub location: String,
    pub user_id: String,
    pub element_name: String,
    pub original_name: String,
    pub element_description: Option<String>,
    pub last_update: i64,
}

#[derive(Serialize, Deserialize)]
pub struct LocationSubElementTable {
    pub sub_element_id: String,
    pub element_id: String,
    pub user_id: String,
    pub sub_elem_name: String,
    pub original_name: String,
    pub sub_elem_description: Option<String>,
    pub last_update: i64,
}

#[derive(Serialize, Deserialize)]
pub struct BookSpellsTable {
    pub spell_id: String,
    pub book_id: String,
    pub user_id: String,
    pub name: String,
    pub name_hash: String,
    pub description: Option<String>,
    pub appearance: Option<String>,
    pub tags: Option<String>,
    pub power_level: Option<String>,
    pub components: Option<String>,
    pub limitations: Option<String>,
    pub notes: Option<String>,
    pub last_update: i64,
}

#[derive(Serialize, Deserialize)]
pub struct BookSpellTagsTable {
    pub tag_id: String,
    pub book_id: String,
    pub user_id: String,
    pub name: String,
    pub name_hash: String,
    pub color: Option<String>,
    pub last_update: i64,
}

// ===== SERIES TABLE INTERFACES (for sync) =====

#[derive(Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SeriesTable {
    pub series_id: String,
    pub user_id: String,
    pub name: String,
    pub hashed_name: String,
    pub description: Option<String>,
    pub cover_image: Option<String>,
    pub last_update: i64,
}

#[derive(Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SeriesBooksTable {
    pub series_id: String,
    pub book_id: String,
    pub book_order: i64,
    pub last_update: i64,
}

#[derive(Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SeriesCharactersTable {
    pub character_id: String,
    pub series_id: String,
    pub user_id: String,
    pub first_name: String,
    pub last_name: Option<String>,
    pub nickname: Option<String>,
    pub age: Option<i64>,
    pub gender: Option<String>,
    pub species: Option<String>,
    pub nationality: Option<String>,
    pub status: Option<String>,
    pub title: Option<String>,
    pub category: String,
    pub image: Option<String>,
    pub role: Option<String>,
    pub biography: Option<String>,
    pub history: Option<String>,
    pub speech_pattern: Option<String>,
    pub catchphrase: Option<String>,
    pub residence: Option<String>,
    pub notes: Option<String>,
    pub color: Option<String>,
    pub last_update: i64,
}

#[derive(Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SeriesCharacterAttributesTable {
    pub attr_id: String,
    pub character_id: String,
    pub user_id: String,
    pub attribute_name: String,
    pub attribute_value: String,
    pub last_update: i64,
}

#[derive(Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SeriesWorldsTable {
    pub world_id: String,
    pub series_id: String,
    pub user_id: String,
    pub name: String,
    pub hashed_name: String,
    pub history: Option<String>,
    pub politics: Option<String>,
    pub economy: Option<String>,
    pub religion: Option<String>,
    pub languages: Option<String>,
    pub last_update: i64,
}

#[derive(Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SeriesWorldElementsTable {
    pub element_id: String,
    pub world_id: String,
    pub user_id: String,
    pub element_type: i64,
    pub name: String,
    pub original_name: String,
    pub description: Option<String>,
    pub last_update: i64,
}

#[derive(Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SeriesLocationsTable {
    pub loc_id: String,
    pub series_id: String,
    pub user_id: String,
    pub loc_name: String,
    pub loc_original_name: String,
    pub last_update: i64,
}

#[derive(Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SeriesLocationElementsTable {
    pub element_id: String,
    pub location_id: String,
    pub user_id: String,
    pub element_name: String,
    pub original_name: String,
    pub element_description: Option<String>,
    pub last_update: i64,
}

#[derive(Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SeriesLocationSubElementsTable {
    pub sub_element_id: String,
    pub element_id: String,
    pub user_id: String,
    pub sub_elem_name: String,
    pub original_name: String,
    pub sub_elem_description: Option<String>,
    pub last_update: i64,
}

#[derive(Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SeriesSpellsTable {
    pub spell_id: String,
    pub series_id: String,
    pub user_id: String,
    pub name: String,
    pub name_hash: String,
    pub description: String,
    pub appearance: String,
    pub tags: String,
    pub power_level: Option<String>,
    pub components: Option<String>,
    pub limitations: Option<String>,
    pub notes: Option<String>,
    pub last_update: i64,
}

#[derive(Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SeriesSpellTagsTable {
    pub tag_id: String,
    pub series_id: String,
    pub user_id: String,
    pub name: String,
    pub hashed_name: String,
    pub color: Option<String>,
    pub last_update: i64,
}

#[derive(Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CompleteSeries {
    pub series: Vec<SeriesTable>,
    pub series_books: Vec<SeriesBooksTable>,
    pub series_characters: Vec<SeriesCharactersTable>,
    pub series_character_attributes: Vec<SeriesCharacterAttributesTable>,
    pub series_worlds: Vec<SeriesWorldsTable>,
    pub series_world_elements: Vec<SeriesWorldElementsTable>,
    pub series_locations: Vec<SeriesLocationsTable>,
    pub series_location_elements: Vec<SeriesLocationElementsTable>,
    pub series_location_sub_elements: Vec<SeriesLocationSubElementsTable>,
    pub series_spells: Vec<SeriesSpellsTable>,
    pub series_spell_tags: Vec<SeriesSpellTagsTable>,
}

// ===== SYNCED SERIES INTERFACES (lightweight, for comparison) =====

#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
pub struct SyncedSeriesBook {
    pub book_id: String,
    pub order: i64,
    pub last_update: i64,
}

#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
pub struct SyncedSeriesCharacterAttribute {
    pub id: String,
    pub name: String,
    pub last_update: i64,
}

#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
pub struct SyncedSeriesCharacter {
    pub id: String,
    pub name: String,
    pub last_update: i64,
    pub attributes: Vec<SyncedSeriesCharacterAttribute>,
}

#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
pub struct SyncedSeriesWorldElement {
    pub id: String,
    pub name: String,
    pub last_update: i64,
}

#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
pub struct SyncedSeriesWorld {
    pub id: String,
    pub name: String,
    pub last_update: i64,
    pub elements: Vec<SyncedSeriesWorldElement>,
}

#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
pub struct SyncedSeriesLocationSubElement {
    pub id: String,
    pub name: String,
    pub last_update: i64,
}

#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
pub struct SyncedSeriesLocationElement {
    pub id: String,
    pub name: String,
    pub last_update: i64,
    pub sub_elements: Vec<SyncedSeriesLocationSubElement>,
}

#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
pub struct SyncedSeriesLocation {
    pub id: String,
    pub name: String,
    pub last_update: i64,
    pub elements: Vec<SyncedSeriesLocationElement>,
}

#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
pub struct SyncedSeriesSpell {
    pub id: String,
    pub name: String,
    pub last_update: i64,
}

#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
pub struct SyncedSeriesSpellTag {
    pub id: String,
    pub name: String,
    pub last_update: i64,
}

#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
pub struct SyncedSeries {
    pub id: String,
    pub name: String,
    pub description: Option<String>,
    pub last_update: i64,
    pub books: Vec<SyncedSeriesBook>,
    pub characters: Vec<SyncedSeriesCharacter>,
    pub worlds: Vec<SyncedSeriesWorld>,
    pub locations: Vec<SyncedSeriesLocation>,
    pub spells: Vec<SyncedSeriesSpell>,
    pub spell_tags: Vec<SyncedSeriesSpellTag>,
}

// ===== FUNCTIONS =====

/// Retrieves all books for a specific user.
/// * `conn` - Database connection
/// * `user_id` - The unique identifier of the user
/// * `lang` - The language for error messages
/// Returns a list of decrypted book properties.
/// Errors if the user encryption key is not found.
pub fn get_books(conn: &Connection, user_id: &str, lang: Lang) -> AppResult<Vec<BookProps>> {
    let user_key: String = get_user_encryption_key(user_id)?;
    let books: Vec<repo::BookQuery> = repo::fetch_books(conn, user_id, lang)?;

    if books.is_empty() {
        return Ok(vec![]);
    }

    let mut book_props_list: Vec<BookProps> = Vec::with_capacity(books.len());
    for book in books {
        let decrypted_title: String = decrypt_data_with_user_key(&book.title, &user_key)?;
        let decrypted_sub_title: String = match book.sub_title.as_deref() {
            Some(sub_title) if !sub_title.is_empty() => decrypt_data_with_user_key(sub_title, &user_key)?,
            _ => String::new(),
        };
        let decrypted_summary: String = match book.summary.as_deref() {
            Some(summary) if !summary.is_empty() => decrypt_data_with_user_key(summary, &user_key)?,
            _ => String::new(),
        };

        book_props_list.push(BookProps {
            book_id: book.book_id,
            book_type: book.book_type,
            author_id: book.author_id,
            title: decrypted_title,
            sub_title: decrypted_sub_title,
            summary: decrypted_summary,
            serie_id: book.serie_id.unwrap_or(0),
            series_id: None,
            desired_release_date: book.desired_release_date.unwrap_or_default(),
            desired_word_count: book.desired_word_count.unwrap_or(0),
            word_count: book.words_count.unwrap_or(0),
            cover_image: book.cover_image.unwrap_or_default(),
            book_meta: None,
            tools: None,
        });
    }

    Ok(book_props_list)
}

/// Adds a new book to the database.
/// * `conn` - Database connection
/// * `book_id` - The unique identifier for the book (optional, will be generated if None)
/// * `user_id` - The unique identifier of the user
/// * `title` - The title of the book
/// * `sub_title` - The subtitle of the book
/// * `summary` - The summary of the book
/// * `book_type` - The type/genre of the book
/// * `serie` - The series identifier
/// * `publication_date` - The desired publication date
/// * `desired_word_count` - The target word count
/// * `lang` - The language for error messages
/// Returns the book ID.
/// Errors if a book with the same title already exists.
pub fn add_book(
    conn: &Connection, book_id: Option<&str>, user_id: &str, title: &str, sub_title: &str,
    summary: &str, book_type: &str, serie: i64, publication_date: Option<&str>,
    desired_word_count: i64, lang: Lang,
) -> AppResult<String> {
    let user_key: String = get_user_encryption_key(user_id)?;
    let encrypted_title: String = encrypt_data_with_user_key(title, &user_key)?;
    let encrypted_sub_title: String = if sub_title.is_empty() { String::new() } else { encrypt_data_with_user_key(sub_title, &user_key)? };
    let encrypted_summary: String = if summary.is_empty() { String::new() } else { encrypt_data_with_user_key(summary, &user_key)? };

    let hashed_title: String = hash_element(title);
    let hashed_sub_title: String = if sub_title.is_empty() { String::new() } else { hash_element(sub_title) };

    let book_already_exists: bool = repo::verify_book_exist(conn, &hashed_title, &hashed_sub_title, user_id, lang)?;
    if book_already_exists {
        return Err(AppError::Internal(if lang == Lang::Fr { format!("Tu as déjà un livre intitulé {} - {}.", title, sub_title) } else { format!("You already have a book named {} - {}.", title, sub_title) }));
    }

    let final_book_id: String = create_unique_id(book_id);
    let last_update: i64 = timestamp_in_seconds();
    repo::insert_book(conn, &final_book_id, user_id, &encrypted_title, &hashed_title, &encrypted_sub_title, &hashed_sub_title, &encrypted_summary, book_type, serie, publication_date, desired_word_count, last_update, lang)
}

/// Retrieves a single book by its ID.
/// * `conn` - Database connection
/// * `user_id` - The unique identifier of the user
/// * `book_id` - The unique identifier of the book
/// * `lang` - The language for error messages
/// Returns the decrypted book properties with tools settings.
pub fn get_book(conn: &Connection, user_id: &str, book_id: &str, lang: Lang) -> AppResult<BookProps> {
    let user_key: String = get_user_encryption_key(user_id)?;
    let book_data: repo::BookQuery = repo::fetch_book(conn, book_id, user_id, lang)?;
    let book_tools: Option<repo::BookToolsTable> = repo::fetch_book_tools(conn, user_id, book_id, lang)?;
    let series_id: Option<String> = repo::fetch_book_series_id(conn, book_id, lang);

    let decrypted_title: String = decrypt_data_with_user_key(&book_data.title, &user_key)?;
    let decrypted_sub_title: String = match book_data.sub_title.as_deref() {
        Some(sub_title) if !sub_title.is_empty() => decrypt_data_with_user_key(sub_title, &user_key)?,
        _ => String::new(),
    };
    let decrypted_summary: String = match book_data.summary.as_deref() {
        Some(summary) if !summary.is_empty() => decrypt_data_with_user_key(summary, &user_key)?,
        _ => String::new(),
    };

    Ok(BookProps {
        book_id: book_data.book_id,
        book_type: book_data.book_type,
        author_id: book_data.author_id,
        title: decrypted_title,
        sub_title: decrypted_sub_title,
        summary: decrypted_summary,
        serie_id: book_data.serie_id.unwrap_or(0),
        series_id,
        desired_release_date: book_data.desired_release_date.unwrap_or_default(),
        desired_word_count: book_data.desired_word_count.unwrap_or(0),
        word_count: book_data.words_count.unwrap_or(0),
        cover_image: book_data.cover_image.unwrap_or_default(),
        book_meta: None,
        tools: Some(BookToolsSettings {
            characters: book_tools.as_ref().map_or(false, |book_tools_row| book_tools_row.characters_enabled == 1),
            worlds: book_tools.as_ref().map_or(false, |book_tools_row| book_tools_row.worlds_enabled == 1),
            locations: book_tools.as_ref().map_or(false, |book_tools_row| book_tools_row.locations_enabled == 1),
            spells: book_tools.as_ref().map_or(false, |book_tools_row| book_tools_row.spells_enabled == 1),
        }),
    })
}

/// Updates basic information for a book.
/// * `conn` - Database connection
/// * `user_id` - The unique identifier of the user
/// * `title` - The new title
/// * `sub_title` - The new subtitle
/// * `summary` - The new summary
/// * `publication_date` - The new publication date
/// * `word_count` - The new word count
/// * `book_id` - The unique identifier of the book
/// * `lang` - The language for error messages
/// Returns true if the update was successful.
pub fn update_book_basic_information(
    conn: &Connection, user_id: &str, title: &str, sub_title: &str, summary: &str,
    publication_date: Option<&str>, word_count: i64, book_id: &str, lang: Lang,
) -> AppResult<bool> {
    let user_key: String = get_user_encryption_key(user_id)?;
    let encrypted_title: String = encrypt_data_with_user_key(title, &user_key)?;
    let encrypted_sub_title: String = if sub_title.is_empty() { String::new() } else { encrypt_data_with_user_key(sub_title, &user_key)? };
    let encrypted_summary: String = if summary.is_empty() { String::new() } else { encrypt_data_with_user_key(summary, &user_key)? };
    let hashed_title: String = hash_element(title);
    let hashed_sub_title: String = if sub_title.is_empty() { String::new() } else { hash_element(sub_title) };
    let last_update: i64 = timestamp_in_seconds();
    repo::update_book_basic_information(conn, user_id, &encrypted_title, &hashed_title, &encrypted_sub_title, &hashed_sub_title, &encrypted_summary, publication_date, word_count, book_id, last_update, lang)
}

/// Removes a book from the database.
/// * `conn` - Database connection
/// * `user_id` - The unique identifier of the user
/// * `book_id` - The unique identifier of the book to remove
/// * `deleted_at` - The timestamp of deletion
/// * `lang` - The language for error messages
/// Returns true if the book was removed.
pub fn remove_book(conn: &Connection, user_id: &str, book_id: &str, deleted_at: i64, lang: Lang) -> AppResult<bool> {
    let deleted: bool = repo::delete_book(conn, user_id, book_id, lang)?;
    if deleted {
        tombstone_repo::insert(conn, book_id, "erit_books", book_id, None, user_id, deleted_at, lang)?;
    }
    Ok(deleted)
}

/// Updates a book tool setting (characters, worlds, locations, or spells).
/// * `conn` - Database connection
/// * `user_id` - The unique identifier of the user
/// * `book_id` - The unique identifier of the book
/// * `tool_name` - The tool name ("characters", "worlds", "locations", "spells")
/// * `enabled` - Whether the tool is enabled
/// * `lang` - The language for error messages
/// Returns true if the update was successful.
pub fn update_book_tool_setting(conn: &Connection, user_id: &str, book_id: &str, tool_name: &str, enabled: bool, lang: Lang) -> AppResult<bool> {
    let column_name: &str = match tool_name {
        "characters" => "characters_enabled",
        "worlds" => "worlds_enabled",
        "locations" => "locations_enabled",
        "spells" => "spells_enabled",
        _ => return Err(AppError::Validation(if lang == Lang::Fr {
            format!("Outil inconnu: {}", tool_name)
        } else {
            format!("Unknown tool: {}", tool_name)
        })),
    };
    let last_update: i64 = timestamp_in_seconds();
    repo::update_book_tool_setting(conn, user_id, book_id, column_name, enabled, last_update, lang)
}

/// Retrieves complete book data including chapters and user information.
/// * `conn` - Database connection
/// * `user_id` - The unique identifier of the user
/// * `book_id` - The unique identifier of the book
/// * `lang` - The language for error messages
/// Returns the complete book data with decrypted content.
pub fn complete_book_data(conn: &Connection, user_id: &str, book_id: &str, lang: Lang) -> AppResult<CompleteBookData> {
    let book_data: repo::BookQuery = repo::fetch_book(conn, book_id, user_id, lang)?;
    let chapters: Vec<chapter_repo::ChapterBookResult> = chapter_repo::fetch_complete_book_chapters(conn, book_id, lang)?;
    let user_key: String = get_user_encryption_key(user_id)?;
    let user_infos: user_repo::UserAccountQuery = user_repo::fetch_account_information(conn, user_id, lang)?;

    let book_title: String = decrypt_data_with_user_key(&book_data.title, &user_key)?;
    let mut decrypted_chapters: Vec<CompleteChapterContent> = Vec::with_capacity(chapters.len());

    for chapter in chapters {
        let decrypted_title: String = if chapter.title.is_empty() { String::new() } else { decrypt_data_with_user_key(&chapter.title, &user_key)? };
        let decrypted_content: String = if let Some(ref content) = chapter.content { if content.is_empty() { String::new() } else { decrypt_data_with_user_key(content, &user_key)? } } else { String::new() };
        decrypted_chapters.push(CompleteChapterContent {
            id: String::new(),
            title: decrypted_title,
            content: decrypted_content,
            order: chapter.chapter_order,
            version: None,
        });
    }

    let cover_image: String = book_data.cover_image.unwrap_or_default();

    Ok(CompleteBookData {
        book_id: book_id.to_string(),
        title: book_title,
        sub_title: if let Some(ref sub_title) = book_data.sub_title { if sub_title.is_empty() { String::new() } else { decrypt_data_with_user_key(sub_title, &user_key)? } } else { String::new() },
        summary: if let Some(ref summary) = book_data.summary { if summary.is_empty() { String::new() } else { decrypt_data_with_user_key(summary, &user_key)? } } else { String::new() },
        cover_image,
        user_infos: BookUserInfos {
            username: if user_infos.username.is_empty() { String::new() } else { decrypt_data_with_user_key(&user_infos.username, &user_key)? },
            author_name: if let Some(ref author_name) = user_infos.author_name { if author_name.is_empty() { String::new() } else { decrypt_data_with_user_key(author_name, &user_key)? } } else { String::new() },
        },
        chapters: decrypted_chapters,
    })
}
