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

use crate::crypto::encryption::{encrypt_data_with_user_key, decrypt_data_with_user_key};
use crate::crypto::key_manager::get_user_encryption_key;
use crate::domains::book::repo as book_repo;
use crate::domains::book::service::{
    SeriesTable, SeriesBooksTable, SeriesCharactersTable, SeriesCharacterAttributesTable,
    SeriesWorldsTable, SeriesWorldElementsTable, SeriesLocationsTable, SeriesLocationElementsTable,
    SeriesLocationSubElementsTable, SeriesSpellsTable, SeriesSpellTagsTable, CompleteSeries,
};
use crate::domains::series::repo as series_repo;
use crate::domains::series_character::repo as series_character_repo;
use crate::domains::series_location::repo as series_location_repo;
use crate::domains::series_spell::repo as series_spell_repo;
use crate::domains::series_sync::repo as series_sync_repo;
use crate::domains::series_world::repo as series_world_repo;
use crate::error::AppResult;
use crate::helpers::timestamp_in_seconds;
use crate::shared::types::Lang;

// ===== STRUCTS =====

#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SeriesSyncUploadPayload {
    pub sync_type: String,
    pub book_element_id: String,
    pub field: String,
    pub value: String,
}

#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
pub struct SeriesSyncResult {
    pub success: bool,
    pub updated_count: usize,
}

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

/// Uploads a field value from a book element to its linked series element,
/// then propagates the change to all other book elements linked to the same series element.
pub fn upload_field_to_series(conn: &Connection, user_id: &str, payload: &SeriesSyncUploadPayload, lang: Lang) -> AppResult<SeriesSyncResult> {
    let sync_type: &str = &payload.sync_type;
    let book_element_id: &str = &payload.book_element_id;
    let field: &str = &payload.field;
    let value: &str = &payload.value;

    // 1. Get the series element ID linked to the book element
    let series_element_id: Option<String> = get_series_link(conn, user_id, sync_type, book_element_id, lang)?;
    if series_element_id.is_none() {
        return Ok(SeriesSyncResult { success: false, updated_count: 0 });
    }
    let series_element_id: String = series_element_id.unwrap();

    // 2. Encrypt the value
    let user_key: String = get_user_encryption_key(user_id)?;
    let encrypted_value: String = encrypt_data_with_user_key(value, &user_key)?;

    // 3. Map the frontend field name to the database column name
    let db_column: String = map_field_to_db_column(sync_type, field);

    // 4. Update the series element
    let last_update: i64 = timestamp_in_seconds();
    let series_updated: bool = update_series_element(conn, user_id, sync_type, &series_element_id, &db_column, &encrypted_value, last_update, lang)?;
    if !series_updated {
        return Ok(SeriesSyncResult { success: false, updated_count: 0 });
    }

    // 5. Map series field to book field (they may differ)
    let book_field: String = map_series_field_to_book_field(sync_type, &db_column);

    // 6. Update all linked book elements
    let updated_count: usize = update_linked_book_elements(conn, user_id, sync_type, &series_element_id, &book_field, &encrypted_value, last_update, lang)?;

    Ok(SeriesSyncResult { success: true, updated_count })
}

/// Gets the series element ID linked to a book element.
fn get_series_link(conn: &Connection, user_id: &str, sync_type: &str, book_element_id: &str, lang: Lang) -> AppResult<Option<String>> {
    match sync_type {
        "character" => series_sync_repo::get_character_series_link(conn, user_id, book_element_id, lang),
        "world" => series_sync_repo::get_world_series_link(conn, user_id, book_element_id, lang),
        "location" => series_sync_repo::get_location_series_link(conn, user_id, book_element_id, lang),
        "spell" => series_sync_repo::get_spell_series_link(conn, user_id, book_element_id, lang),
        _ => Ok(None),
    }
}

/// Maps frontend field names to database column names.
fn map_field_to_db_column(sync_type: &str, field: &str) -> String {
    match sync_type {
        "character" => match field {
            "firstName" => "first_name".to_string(),
            "lastName" => "last_name".to_string(),
            "speechPattern" => "speech_pattern".to_string(),
            _ => field.to_string(),
        },
        "location" => match field {
            "locName" => "loc_name".to_string(),
            _ => field.to_string(),
        },
        "spell" => match field {
            "powerLevel" => "power_level".to_string(),
            _ => field.to_string(),
        },
        _ => field.to_string(),
    }
}

/// Updates a field in the series element.
fn update_series_element(conn: &Connection, user_id: &str, sync_type: &str, series_element_id: &str, field: &str, encrypted_value: &str, last_update: i64, lang: Lang) -> AppResult<bool> {
    match sync_type {
        "character" => series_sync_repo::update_series_character_field(conn, user_id, series_element_id, field, encrypted_value, last_update, lang),
        "world" => series_sync_repo::update_series_world_field(conn, user_id, series_element_id, field, encrypted_value, last_update, lang),
        "location" => series_sync_repo::update_series_location_field(conn, user_id, series_element_id, field, encrypted_value, last_update, lang),
        "spell" => series_sync_repo::update_series_spell_field(conn, user_id, series_element_id, field, encrypted_value, last_update, lang),
        _ => Ok(false),
    }
}

/// Updates all book elements linked to a series element.
fn update_linked_book_elements(conn: &Connection, user_id: &str, sync_type: &str, series_element_id: &str, field: &str, encrypted_value: &str, last_update: i64, lang: Lang) -> AppResult<usize> {
    match sync_type {
        "character" => series_sync_repo::update_linked_book_characters_field(conn, user_id, series_element_id, field, encrypted_value, last_update, lang),
        "world" => series_sync_repo::update_linked_book_worlds_field(conn, user_id, series_element_id, field, encrypted_value, last_update, lang),
        "location" => series_sync_repo::update_linked_book_locations_field(conn, user_id, series_element_id, field, encrypted_value, last_update, lang),
        "spell" => series_sync_repo::update_linked_book_spells_field(conn, user_id, series_element_id, field, encrypted_value, last_update, lang),
        _ => Ok(0),
    }
}

/// Maps series field names to book field names (they may differ).
fn map_series_field_to_book_field(sync_type: &str, series_field: &str) -> String {
    match sync_type {
        "location" => match series_field {
            "name" => "loc_name".to_string(),
            _ => series_field.to_string(),
        },
        _ => series_field.to_string(),
    }
}

// ===== SYNC METHODS =====

/// Gets a complete series with all data decrypted for upload to server.
pub fn get_complete_series_for_upload(conn: &Connection, user_id: &str, series_id: &str, lang: Lang) -> AppResult<CompleteSeries> {
    let user_key: String = get_user_encryption_key(user_id)?;

    // Fetch all series data
    let series_data = series_repo::fetch_series_table_for_sync(conn, user_id, series_id, lang)?;
    let series_books_data = series_repo::fetch_series_books_table(conn, series_id, lang)?;
    let characters_data = series_character_repo::fetch_series_characters_table(conn, user_id, series_id, lang)?;
    let character_attributes_data = series_character_repo::fetch_series_character_attributes_by_series_id(conn, user_id, series_id, lang)?;
    let worlds_data = series_world_repo::fetch_series_worlds_table(conn, user_id, series_id, lang)?;
    let world_elements_data = series_world_repo::fetch_series_world_elements_by_series_id(conn, user_id, series_id, lang)?;
    let locations_data = series_location_repo::fetch_series_locations_table(conn, user_id, series_id, lang)?;
    let location_elements_data = series_location_repo::fetch_series_location_elements_by_series_id(conn, user_id, series_id, lang)?;
    let location_sub_elements_data = series_location_repo::fetch_series_location_sub_elements_by_series_id(conn, user_id, series_id, lang)?;
    let spells_data = series_spell_repo::fetch_series_spells_table(conn, user_id, series_id, lang)?;
    let spell_tags_data = series_spell_repo::fetch_series_spell_tags_table(conn, user_id, series_id, lang)?;

    // Decrypt series
    let mut series: Vec<SeriesTable> = Vec::with_capacity(series_data.len());
    for s in series_data {
        series.push(SeriesTable {
            series_id: s.series_id,
            user_id: s.user_id,
            name: decrypt_data_with_user_key(&s.name, &user_key)?,
            hashed_name: s.hashed_name,
            description: if let Some(ref val) = s.description { Some(decrypt_data_with_user_key(val, &user_key)?) } else { None },
            cover_image: if let Some(ref val) = s.cover_image { Some(decrypt_data_with_user_key(val, &user_key)?) } else { None },
            last_update: s.last_update,
        });
    }

    // Series books (no encryption)
    let mut series_books: Vec<SeriesBooksTable> = Vec::with_capacity(series_books_data.len());
    for sb in series_books_data {
        series_books.push(SeriesBooksTable {
            series_id: sb.series_id,
            book_id: sb.book_id,
            book_order: sb.book_order,
            last_update: sb.last_update,
        });
    }

    // Decrypt characters
    let mut series_characters: Vec<SeriesCharactersTable> = Vec::with_capacity(characters_data.len());
    for c in characters_data {
        let decrypted_age: Option<i64> = if let Some(ref val) = c.age {
            let decrypted: String = decrypt_data_with_user_key(val, &user_key)?;
            decrypted.parse::<i64>().ok()
        } else {
            None
        };
        series_characters.push(SeriesCharactersTable {
            character_id: c.character_id,
            series_id: c.series_id,
            user_id: c.user_id,
            first_name: decrypt_data_with_user_key(&c.first_name, &user_key)?,
            last_name: if let Some(ref val) = c.last_name { Some(decrypt_data_with_user_key(val, &user_key)?) } else { None },
            nickname: if let Some(ref val) = c.nickname { Some(decrypt_data_with_user_key(val, &user_key)?) } else { None },
            age: decrypted_age,
            gender: if let Some(ref val) = c.gender { Some(decrypt_data_with_user_key(val, &user_key)?) } else { None },
            species: if let Some(ref val) = c.species { Some(decrypt_data_with_user_key(val, &user_key)?) } else { None },
            nationality: if let Some(ref val) = c.nationality { Some(decrypt_data_with_user_key(val, &user_key)?) } else { None },
            status: if let Some(ref val) = c.status { Some(decrypt_data_with_user_key(val, &user_key)?) } else { None },
            title: if let Some(ref val) = c.title { Some(decrypt_data_with_user_key(val, &user_key)?) } else { None },
            category: decrypt_data_with_user_key(&c.category, &user_key)?,
            image: if let Some(ref val) = c.image { Some(decrypt_data_with_user_key(val, &user_key)?) } else { None },
            role: if let Some(ref val) = c.role { Some(decrypt_data_with_user_key(val, &user_key)?) } else { None },
            biography: if let Some(ref val) = c.biography { Some(decrypt_data_with_user_key(val, &user_key)?) } else { None },
            history: if let Some(ref val) = c.history { Some(decrypt_data_with_user_key(val, &user_key)?) } else { None },
            speech_pattern: if let Some(ref val) = c.speech_pattern { Some(decrypt_data_with_user_key(val, &user_key)?) } else { None },
            catchphrase: if let Some(ref val) = c.catchphrase { Some(decrypt_data_with_user_key(val, &user_key)?) } else { None },
            residence: if let Some(ref val) = c.residence { Some(decrypt_data_with_user_key(val, &user_key)?) } else { None },
            notes: if let Some(ref val) = c.notes { Some(decrypt_data_with_user_key(val, &user_key)?) } else { None },
            color: if let Some(ref val) = c.color { Some(decrypt_data_with_user_key(val, &user_key)?) } else { None },
            last_update: c.last_update,
        });
    }

    // Decrypt character attributes
    let mut series_character_attributes: Vec<SeriesCharacterAttributesTable> = Vec::with_capacity(character_attributes_data.len());
    for a in character_attributes_data {
        series_character_attributes.push(SeriesCharacterAttributesTable {
            attr_id: a.attr_id,
            character_id: a.character_id,
            user_id: a.user_id,
            attribute_name: decrypt_data_with_user_key(&a.attribute_name, &user_key)?,
            attribute_value: decrypt_data_with_user_key(&a.attribute_value, &user_key)?,
            last_update: a.last_update,
        });
    }

    // Decrypt worlds
    let mut series_worlds: Vec<SeriesWorldsTable> = Vec::with_capacity(worlds_data.len());
    for w in worlds_data {
        series_worlds.push(SeriesWorldsTable {
            world_id: w.world_id,
            series_id: w.series_id,
            user_id: w.user_id,
            name: decrypt_data_with_user_key(&w.name, &user_key)?,
            hashed_name: w.hashed_name,
            history: if let Some(ref val) = w.history { Some(decrypt_data_with_user_key(val, &user_key)?) } else { None },
            politics: if let Some(ref val) = w.politics { Some(decrypt_data_with_user_key(val, &user_key)?) } else { None },
            economy: if let Some(ref val) = w.economy { Some(decrypt_data_with_user_key(val, &user_key)?) } else { None },
            religion: if let Some(ref val) = w.religion { Some(decrypt_data_with_user_key(val, &user_key)?) } else { None },
            languages: if let Some(ref val) = w.languages { Some(decrypt_data_with_user_key(val, &user_key)?) } else { None },
            last_update: w.last_update,
        });
    }

    // Decrypt world elements
    let mut series_world_elements: Vec<SeriesWorldElementsTable> = Vec::with_capacity(world_elements_data.len());
    for e in world_elements_data {
        series_world_elements.push(SeriesWorldElementsTable {
            element_id: e.element_id,
            world_id: e.world_id,
            user_id: e.user_id,
            element_type: e.element_type,
            name: decrypt_data_with_user_key(&e.name, &user_key)?,
            original_name: e.original_name,
            description: if let Some(ref val) = e.description { Some(decrypt_data_with_user_key(val, &user_key)?) } else { None },
            last_update: e.last_update,
        });
    }

    // Decrypt locations
    let mut series_locations: Vec<SeriesLocationsTable> = Vec::with_capacity(locations_data.len());
    for l in locations_data {
        series_locations.push(SeriesLocationsTable {
            loc_id: l.loc_id,
            series_id: l.series_id,
            user_id: l.user_id,
            loc_name: decrypt_data_with_user_key(&l.loc_name, &user_key)?,
            loc_original_name: l.loc_original_name,
            last_update: l.last_update,
        });
    }

    // Decrypt location elements
    let mut series_location_elements: Vec<SeriesLocationElementsTable> = Vec::with_capacity(location_elements_data.len());
    for e in location_elements_data {
        series_location_elements.push(SeriesLocationElementsTable {
            element_id: e.element_id,
            location_id: e.location_id,
            user_id: e.user_id,
            element_name: decrypt_data_with_user_key(&e.element_name, &user_key)?,
            original_name: e.original_name,
            element_description: if let Some(ref val) = e.element_description { Some(decrypt_data_with_user_key(val, &user_key)?) } else { None },
            last_update: e.last_update,
        });
    }

    // Decrypt location sub-elements
    let mut series_location_sub_elements: Vec<SeriesLocationSubElementsTable> = Vec::with_capacity(location_sub_elements_data.len());
    for se in location_sub_elements_data {
        series_location_sub_elements.push(SeriesLocationSubElementsTable {
            sub_element_id: se.sub_element_id,
            element_id: se.element_id,
            user_id: se.user_id,
            sub_elem_name: decrypt_data_with_user_key(&se.sub_elem_name, &user_key)?,
            original_name: se.original_name,
            sub_elem_description: if let Some(ref val) = se.sub_elem_description { Some(decrypt_data_with_user_key(val, &user_key)?) } else { None },
            last_update: se.last_update,
        });
    }

    // Decrypt spells
    let mut series_spells: Vec<SeriesSpellsTable> = Vec::with_capacity(spells_data.len());
    for s in spells_data {
        series_spells.push(SeriesSpellsTable {
            spell_id: s.spell_id,
            series_id: s.series_id,
            user_id: s.user_id,
            name: decrypt_data_with_user_key(&s.name, &user_key)?,
            name_hash: s.name_hash,
            description: if let Some(ref description) = s.description { if description.is_empty() { String::new() } else { decrypt_data_with_user_key(description, &user_key)? } } else { String::new() },
            appearance: if let Some(ref appearance) = s.appearance { if appearance.is_empty() { String::new() } else { decrypt_data_with_user_key(appearance, &user_key)? } } else { String::new() },
            tags: if let Some(ref tags) = s.tags { if tags.is_empty() { String::new() } else { decrypt_data_with_user_key(tags, &user_key)? } } else { String::new() },
            power_level: if let Some(ref power_level) = s.power_level { if power_level.is_empty() { None } else { Some(decrypt_data_with_user_key(power_level, &user_key)?) } } else { None },
            components: if let Some(ref components) = s.components { if components.is_empty() { None } else { Some(decrypt_data_with_user_key(components, &user_key)?) } } else { None },
            limitations: if let Some(ref limitations) = s.limitations { if limitations.is_empty() { None } else { Some(decrypt_data_with_user_key(limitations, &user_key)?) } } else { None },
            notes: if let Some(ref notes) = s.notes { if notes.is_empty() { None } else { Some(decrypt_data_with_user_key(notes, &user_key)?) } } else { None },
            last_update: s.last_update,
        });
    }

    // Decrypt spell tags
    let mut series_spell_tags: Vec<SeriesSpellTagsTable> = Vec::with_capacity(spell_tags_data.len());
    for t in spell_tags_data {
        series_spell_tags.push(SeriesSpellTagsTable {
            tag_id: t.tag_id,
            series_id: t.series_id,
            user_id: t.user_id,
            name: decrypt_data_with_user_key(&t.name, &user_key)?,
            hashed_name: t.hashed_name,
            color: t.color,
            last_update: t.last_update,
        });
    }

    Ok(CompleteSeries {
        series,
        series_books,
        series_characters,
        series_character_attributes,
        series_worlds,
        series_world_elements,
        series_locations,
        series_location_elements,
        series_location_sub_elements,
        series_spells,
        series_spell_tags,
    })
}

/// Saves a complete series downloaded from the server to the local database.
/// Encrypts all data before storing.
pub fn save_complete_series(conn: &Connection, user_id: &str, complete_series: &CompleteSeries, lang: Lang) -> AppResult<bool> {
    let user_key: String = get_user_encryption_key(user_id)?;

    // Save series
    for s in &complete_series.series {
        let encrypted_name: String = encrypt_data_with_user_key(&s.name, &user_key)?;
        let encrypted_description: Option<String> = if let Some(ref val) = s.description { Some(encrypt_data_with_user_key(val, &user_key)?) } else { None };
        let encrypted_cover_image: Option<String> = if let Some(ref val) = s.cover_image { Some(encrypt_data_with_user_key(val, &user_key)?) } else { None };

        let success: bool = series_repo::insert_sync_series(conn, &s.series_id, user_id, &encrypted_name, &s.hashed_name, encrypted_description.as_deref(), encrypted_cover_image.as_deref(), s.last_update, lang)?;
        if !success { return Ok(false); }
    }

    // Save series books (only if the book exists locally)
    for sb in &complete_series.series_books {
        let book_exists: bool = book_repo::is_book_exist(conn, user_id, &sb.book_id, lang);
        if !book_exists { continue; }

        let success: bool = series_repo::insert_sync_series_book(conn, &sb.series_id, &sb.book_id, sb.book_order, sb.last_update, lang)?;
        if !success { return Ok(false); }
    }

    // Save characters
    for character in &complete_series.series_characters {
        let enc_first_name: String = encrypt_data_with_user_key(&character.first_name, &user_key)?;
        let enc_last_name: Option<String> = if let Some(ref val) = character.last_name { Some(encrypt_data_with_user_key(val, &user_key)?) } else { None };
        let enc_nickname: Option<String> = if let Some(ref val) = character.nickname { Some(encrypt_data_with_user_key(val, &user_key)?) } else { None };
        let enc_age: Option<String> = if let Some(age) = character.age { Some(encrypt_data_with_user_key(&age.to_string(), &user_key)?) } else { None };
        let enc_gender: Option<String> = if let Some(ref val) = character.gender { Some(encrypt_data_with_user_key(val, &user_key)?) } else { None };
        let enc_species: Option<String> = if let Some(ref val) = character.species { Some(encrypt_data_with_user_key(val, &user_key)?) } else { None };
        let enc_nationality: Option<String> = if let Some(ref val) = character.nationality { Some(encrypt_data_with_user_key(val, &user_key)?) } else { None };
        let enc_status: Option<String> = if let Some(ref val) = character.status { Some(encrypt_data_with_user_key(val, &user_key)?) } else { None };
        let enc_title: Option<String> = if let Some(ref val) = character.title { Some(encrypt_data_with_user_key(val, &user_key)?) } else { None };
        let enc_category: String = encrypt_data_with_user_key(&character.category, &user_key)?;
        let enc_image: Option<String> = if let Some(ref val) = character.image { Some(encrypt_data_with_user_key(val, &user_key)?) } else { None };
        let enc_role: Option<String> = if let Some(ref val) = character.role { Some(encrypt_data_with_user_key(val, &user_key)?) } else { None };
        let enc_biography: Option<String> = if let Some(ref val) = character.biography { Some(encrypt_data_with_user_key(val, &user_key)?) } else { None };
        let enc_history: Option<String> = if let Some(ref val) = character.history { Some(encrypt_data_with_user_key(val, &user_key)?) } else { None };
        let enc_speech_pattern: Option<String> = if let Some(ref val) = character.speech_pattern { Some(encrypt_data_with_user_key(val, &user_key)?) } else { None };
        let enc_catchphrase: Option<String> = if let Some(ref val) = character.catchphrase { Some(encrypt_data_with_user_key(val, &user_key)?) } else { None };
        let enc_residence: Option<String> = if let Some(ref val) = character.residence { Some(encrypt_data_with_user_key(val, &user_key)?) } else { None };
        let enc_notes: Option<String> = if let Some(ref val) = character.notes { Some(encrypt_data_with_user_key(val, &user_key)?) } else { None };
        let enc_color: Option<String> = if let Some(ref val) = character.color { Some(encrypt_data_with_user_key(val, &user_key)?) } else { None };

        let success: bool = series_character_repo::insert_sync_series_character(
            conn, &character.character_id, &character.series_id, user_id, &enc_first_name,
            enc_last_name.as_deref(), enc_nickname.as_deref(), enc_age.as_deref(), enc_gender.as_deref(),
            enc_species.as_deref(), enc_nationality.as_deref(), enc_status.as_deref(), &enc_category,
            enc_title.as_deref(), enc_image.as_deref(), enc_role.as_deref(), enc_biography.as_deref(),
            enc_history.as_deref(), enc_speech_pattern.as_deref(), enc_catchphrase.as_deref(),
            enc_residence.as_deref(), enc_notes.as_deref(), enc_color.as_deref(), character.last_update, lang,
        )?;
        if !success { return Ok(false); }
    }

    // Save character attributes
    for attr in &complete_series.series_character_attributes {
        let encrypted_name: String = encrypt_data_with_user_key(&attr.attribute_name, &user_key)?;
        let encrypted_value: String = encrypt_data_with_user_key(&attr.attribute_value, &user_key)?;

        let success: bool = series_character_repo::insert_sync_series_character_attribute(
            conn, &attr.attr_id, &attr.character_id, user_id, &encrypted_name, &encrypted_value, attr.last_update, lang,
        )?;
        if !success { return Ok(false); }
    }

    // Save worlds
    for world in &complete_series.series_worlds {
        let encrypted_name: String = encrypt_data_with_user_key(&world.name, &user_key)?;
        let encrypted_history: Option<String> = if let Some(ref val) = world.history { Some(encrypt_data_with_user_key(val, &user_key)?) } else { None };
        let encrypted_politics: Option<String> = if let Some(ref val) = world.politics { Some(encrypt_data_with_user_key(val, &user_key)?) } else { None };
        let encrypted_economy: Option<String> = if let Some(ref val) = world.economy { Some(encrypt_data_with_user_key(val, &user_key)?) } else { None };
        let encrypted_religion: Option<String> = if let Some(ref val) = world.religion { Some(encrypt_data_with_user_key(val, &user_key)?) } else { None };
        let encrypted_languages: Option<String> = if let Some(ref val) = world.languages { Some(encrypt_data_with_user_key(val, &user_key)?) } else { None };

        let success: bool = series_world_repo::insert_sync_series_world(
            conn, &world.world_id, &world.series_id, user_id, &encrypted_name, &world.hashed_name,
            encrypted_history.as_deref(), encrypted_politics.as_deref(), encrypted_economy.as_deref(),
            encrypted_religion.as_deref(), encrypted_languages.as_deref(), world.last_update, lang,
        )?;
        if !success { return Ok(false); }
    }

    // Save world elements
    for element in &complete_series.series_world_elements {
        let encrypted_name: String = encrypt_data_with_user_key(&element.name, &user_key)?;
        let encrypted_description: Option<String> = if let Some(ref val) = element.description { Some(encrypt_data_with_user_key(val, &user_key)?) } else { None };

        let success: bool = series_world_repo::insert_sync_series_world_element(
            conn, &element.element_id, &element.world_id, user_id, element.element_type,
            &encrypted_name, &element.original_name, encrypted_description.as_deref(), element.last_update, lang,
        )?;
        if !success { return Ok(false); }
    }

    // Save locations
    for location in &complete_series.series_locations {
        let encrypted_name: String = encrypt_data_with_user_key(&location.loc_name, &user_key)?;

        let success: bool = series_location_repo::insert_sync_series_location(
            conn, &location.loc_id, &location.series_id, user_id, &encrypted_name,
            &location.loc_original_name, location.last_update, lang,
        )?;
        if !success { return Ok(false); }
    }

    // Save location elements
    for element in &complete_series.series_location_elements {
        let encrypted_name: String = encrypt_data_with_user_key(&element.element_name, &user_key)?;
        let encrypted_description: Option<String> = if let Some(ref val) = element.element_description { Some(encrypt_data_with_user_key(val, &user_key)?) } else { None };

        let success: bool = series_location_repo::insert_sync_series_location_element(
            conn, &element.element_id, &element.location_id, user_id, &encrypted_name,
            &element.original_name, encrypted_description.as_deref(), element.last_update, lang,
        )?;
        if !success { return Ok(false); }
    }

    // Save location sub-elements
    for sub_element in &complete_series.series_location_sub_elements {
        let encrypted_name: String = encrypt_data_with_user_key(&sub_element.sub_elem_name, &user_key)?;
        let encrypted_description: Option<String> = if let Some(ref val) = sub_element.sub_elem_description { Some(encrypt_data_with_user_key(val, &user_key)?) } else { None };

        let success: bool = series_location_repo::insert_sync_series_location_sub_element(
            conn, &sub_element.sub_element_id, &sub_element.element_id, user_id, &encrypted_name,
            &sub_element.original_name, encrypted_description.as_deref(), sub_element.last_update, lang,
        )?;
        if !success { return Ok(false); }
    }

    // Save spells
    for spell in &complete_series.series_spells {
        let encrypted_name: String = encrypt_data_with_user_key(&spell.name, &user_key)?;
        let encrypted_description: String = encrypt_data_with_user_key(&spell.description, &user_key)?;
        let encrypted_appearance: String = encrypt_data_with_user_key(&spell.appearance, &user_key)?;
        let encrypted_tags: String = encrypt_data_with_user_key(&spell.tags, &user_key)?;
        let encrypted_power_level: Option<String> = if let Some(ref val) = spell.power_level { Some(encrypt_data_with_user_key(val, &user_key)?) } else { None };
        let encrypted_components: Option<String> = if let Some(ref val) = spell.components { Some(encrypt_data_with_user_key(val, &user_key)?) } else { None };
        let encrypted_limitations: Option<String> = if let Some(ref val) = spell.limitations { Some(encrypt_data_with_user_key(val, &user_key)?) } else { None };
        let encrypted_notes: Option<String> = if let Some(ref val) = spell.notes { Some(encrypt_data_with_user_key(val, &user_key)?) } else { None };

        let success: bool = series_spell_repo::insert_sync_series_spell(
            conn, &spell.spell_id, &spell.series_id, user_id, &encrypted_name, &spell.name_hash,
            &encrypted_description, &encrypted_appearance, &encrypted_tags,
            encrypted_power_level.as_deref(), encrypted_components.as_deref(),
            encrypted_limitations.as_deref(), encrypted_notes.as_deref(), spell.last_update, lang,
        )?;
        if !success { return Ok(false); }
    }

    // Save spell tags
    for tag in &complete_series.series_spell_tags {
        let encrypted_name: String = encrypt_data_with_user_key(&tag.name, &user_key)?;

        let success: bool = series_spell_repo::insert_sync_series_spell_tag(
            conn, &tag.tag_id, &tag.series_id, user_id, &encrypted_name, &tag.hashed_name,
            tag.color.as_deref(), tag.last_update, lang,
        )?;
        if !success { return Ok(false); }
    }

    Ok(true)
}

/// Synchronizes a series from server to client, updating existing records or inserting new ones.
pub fn sync_series_from_server_to_client(conn: &Connection, user_id: &str, complete_series: &CompleteSeries, lang: Lang) -> AppResult<bool> {
    let user_key: String = get_user_encryption_key(user_id)?;

    // Sync series
    for s in &complete_series.series {
        let encrypted_name: String = encrypt_data_with_user_key(&s.name, &user_key)?;
        let encrypted_description: Option<String> = if let Some(ref val) = s.description { Some(encrypt_data_with_user_key(val, &user_key)?) } else { None };
        let encrypted_cover_image: Option<String> = if let Some(ref val) = s.cover_image { Some(encrypt_data_with_user_key(val, &user_key)?) } else { None };

        let exists: bool = series_repo::series_exists(conn, user_id, &s.series_id, lang)?;
        if exists {
            let success: bool = series_repo::update_sync_series(conn, user_id, &s.series_id, &encrypted_name, &s.hashed_name, encrypted_description.as_deref(), encrypted_cover_image.as_deref(), s.last_update, lang)?;
            if !success { return Ok(false); }
        } else {
            let success: bool = series_repo::insert_sync_series(conn, &s.series_id, user_id, &encrypted_name, &s.hashed_name, encrypted_description.as_deref(), encrypted_cover_image.as_deref(), s.last_update, lang)?;
            if !success { return Ok(false); }
        }
    }

    // Sync series books (only if the book exists locally)
    for sb in &complete_series.series_books {
        let book_exists: bool = book_repo::is_book_exist(conn, user_id, &sb.book_id, lang);
        if !book_exists { continue; }

        let success: bool = series_repo::insert_sync_series_book(conn, &sb.series_id, &sb.book_id, sb.book_order, sb.last_update, lang)?;
        if !success { return Ok(false); }
    }

    // Sync characters
    for character in &complete_series.series_characters {
        let enc_first_name: String = encrypt_data_with_user_key(&character.first_name, &user_key)?;
        let enc_last_name: Option<String> = if let Some(ref val) = character.last_name { Some(encrypt_data_with_user_key(val, &user_key)?) } else { None };
        let enc_nickname: Option<String> = if let Some(ref val) = character.nickname { Some(encrypt_data_with_user_key(val, &user_key)?) } else { None };
        let enc_age: Option<String> = if let Some(age) = character.age { Some(encrypt_data_with_user_key(&age.to_string(), &user_key)?) } else { None };
        let enc_gender: Option<String> = if let Some(ref val) = character.gender { Some(encrypt_data_with_user_key(val, &user_key)?) } else { None };
        let enc_species: Option<String> = if let Some(ref val) = character.species { Some(encrypt_data_with_user_key(val, &user_key)?) } else { None };
        let enc_nationality: Option<String> = if let Some(ref val) = character.nationality { Some(encrypt_data_with_user_key(val, &user_key)?) } else { None };
        let enc_status: Option<String> = if let Some(ref val) = character.status { Some(encrypt_data_with_user_key(val, &user_key)?) } else { None };
        let enc_title: Option<String> = if let Some(ref val) = character.title { Some(encrypt_data_with_user_key(val, &user_key)?) } else { None };
        let enc_category: String = encrypt_data_with_user_key(&character.category, &user_key)?;
        let enc_image: Option<String> = if let Some(ref val) = character.image { Some(encrypt_data_with_user_key(val, &user_key)?) } else { None };
        let enc_role: Option<String> = if let Some(ref val) = character.role { Some(encrypt_data_with_user_key(val, &user_key)?) } else { None };
        let enc_biography: Option<String> = if let Some(ref val) = character.biography { Some(encrypt_data_with_user_key(val, &user_key)?) } else { None };
        let enc_history: Option<String> = if let Some(ref val) = character.history { Some(encrypt_data_with_user_key(val, &user_key)?) } else { None };
        let enc_speech_pattern: Option<String> = if let Some(ref val) = character.speech_pattern { Some(encrypt_data_with_user_key(val, &user_key)?) } else { None };
        let enc_catchphrase: Option<String> = if let Some(ref val) = character.catchphrase { Some(encrypt_data_with_user_key(val, &user_key)?) } else { None };
        let enc_residence: Option<String> = if let Some(ref val) = character.residence { Some(encrypt_data_with_user_key(val, &user_key)?) } else { None };
        let enc_notes: Option<String> = if let Some(ref val) = character.notes { Some(encrypt_data_with_user_key(val, &user_key)?) } else { None };
        let enc_color: Option<String> = if let Some(ref val) = character.color { Some(encrypt_data_with_user_key(val, &user_key)?) } else { None };

        let exists: bool = series_character_repo::series_character_exists(conn, user_id, &character.character_id, lang)?;
        if exists {
            let success: bool = series_character_repo::update_sync_series_character(
                conn, user_id, &character.character_id, &enc_first_name,
                enc_last_name.as_deref(), enc_nickname.as_deref(), enc_age.as_deref(), enc_gender.as_deref(),
                enc_species.as_deref(), enc_nationality.as_deref(), enc_status.as_deref(), &enc_category,
                enc_title.as_deref(), enc_image.as_deref(), enc_role.as_deref(), enc_biography.as_deref(),
                enc_history.as_deref(), enc_speech_pattern.as_deref(), enc_catchphrase.as_deref(),
                enc_residence.as_deref(), enc_notes.as_deref(), enc_color.as_deref(), character.last_update, lang,
            )?;
            if !success { return Ok(false); }
        } else {
            let success: bool = series_character_repo::insert_sync_series_character(
                conn, &character.character_id, &character.series_id, user_id, &enc_first_name,
                enc_last_name.as_deref(), enc_nickname.as_deref(), enc_age.as_deref(), enc_gender.as_deref(),
                enc_species.as_deref(), enc_nationality.as_deref(), enc_status.as_deref(), &enc_category,
                enc_title.as_deref(), enc_image.as_deref(), enc_role.as_deref(), enc_biography.as_deref(),
                enc_history.as_deref(), enc_speech_pattern.as_deref(), enc_catchphrase.as_deref(),
                enc_residence.as_deref(), enc_notes.as_deref(), enc_color.as_deref(), character.last_update, lang,
            )?;
            if !success { return Ok(false); }
        }
    }

    // Sync character attributes
    for attr in &complete_series.series_character_attributes {
        let encrypted_name: String = encrypt_data_with_user_key(&attr.attribute_name, &user_key)?;
        let encrypted_value: String = encrypt_data_with_user_key(&attr.attribute_value, &user_key)?;

        let exists: bool = series_character_repo::series_character_attribute_exists(conn, user_id, &attr.attr_id, lang)?;
        if exists {
            let success: bool = series_character_repo::update_sync_series_character_attribute(conn, user_id, &attr.attr_id, &encrypted_name, &encrypted_value, attr.last_update, lang)?;
            if !success { return Ok(false); }
        } else {
            let success: bool = series_character_repo::insert_sync_series_character_attribute(conn, &attr.attr_id, &attr.character_id, user_id, &encrypted_name, &encrypted_value, attr.last_update, lang)?;
            if !success { return Ok(false); }
        }
    }

    // Sync worlds
    for world in &complete_series.series_worlds {
        let encrypted_name: String = encrypt_data_with_user_key(&world.name, &user_key)?;
        let encrypted_history: Option<String> = if let Some(ref val) = world.history { Some(encrypt_data_with_user_key(val, &user_key)?) } else { None };
        let encrypted_politics: Option<String> = if let Some(ref val) = world.politics { Some(encrypt_data_with_user_key(val, &user_key)?) } else { None };
        let encrypted_economy: Option<String> = if let Some(ref val) = world.economy { Some(encrypt_data_with_user_key(val, &user_key)?) } else { None };
        let encrypted_religion: Option<String> = if let Some(ref val) = world.religion { Some(encrypt_data_with_user_key(val, &user_key)?) } else { None };
        let encrypted_languages: Option<String> = if let Some(ref val) = world.languages { Some(encrypt_data_with_user_key(val, &user_key)?) } else { None };

        let exists: bool = series_world_repo::series_world_exists(conn, user_id, &world.world_id, lang)?;
        if exists {
            let success: bool = series_world_repo::update_sync_series_world(
                conn, &world.world_id, user_id, &encrypted_name,
                encrypted_history.as_deref(), encrypted_politics.as_deref(), encrypted_economy.as_deref(),
                encrypted_religion.as_deref(), encrypted_languages.as_deref(), world.last_update, lang,
            )?;
            if !success { return Ok(false); }
        } else {
            let success: bool = series_world_repo::insert_sync_series_world(
                conn, &world.world_id, &world.series_id, user_id, &encrypted_name, &world.hashed_name,
                encrypted_history.as_deref(), encrypted_politics.as_deref(), encrypted_economy.as_deref(),
                encrypted_religion.as_deref(), encrypted_languages.as_deref(), world.last_update, lang,
            )?;
            if !success { return Ok(false); }
        }
    }

    // Sync world elements
    for element in &complete_series.series_world_elements {
        let encrypted_name: String = encrypt_data_with_user_key(&element.name, &user_key)?;
        let encrypted_description: Option<String> = if let Some(ref val) = element.description { Some(encrypt_data_with_user_key(val, &user_key)?) } else { None };

        let exists: bool = series_world_repo::series_world_element_exists(conn, user_id, &element.element_id, lang)?;
        if exists {
            let success: bool = series_world_repo::update_sync_series_world_element(conn, &element.element_id, user_id, &encrypted_name, encrypted_description.as_deref(), element.last_update, lang)?;
            if !success { return Ok(false); }
        } else {
            let success: bool = series_world_repo::insert_sync_series_world_element(
                conn, &element.element_id, &element.world_id, user_id, element.element_type,
                &encrypted_name, &element.original_name, encrypted_description.as_deref(), element.last_update, lang,
            )?;
            if !success { return Ok(false); }
        }
    }

    // Sync locations
    for location in &complete_series.series_locations {
        let encrypted_name: String = encrypt_data_with_user_key(&location.loc_name, &user_key)?;

        let exists: bool = series_location_repo::series_location_exists(conn, user_id, &location.loc_id, lang)?;
        if exists {
            let success: bool = series_location_repo::update_sync_series_location(conn, &location.loc_id, user_id, &encrypted_name, location.last_update, lang)?;
            if !success { return Ok(false); }
        } else {
            let success: bool = series_location_repo::insert_sync_series_location(
                conn, &location.loc_id, &location.series_id, user_id, &encrypted_name,
                &location.loc_original_name, location.last_update, lang,
            )?;
            if !success { return Ok(false); }
        }
    }

    // Sync location elements
    for element in &complete_series.series_location_elements {
        let encrypted_name: String = encrypt_data_with_user_key(&element.element_name, &user_key)?;
        let encrypted_description: Option<String> = if let Some(ref val) = element.element_description { Some(encrypt_data_with_user_key(val, &user_key)?) } else { None };

        let exists: bool = series_location_repo::series_location_element_exists(conn, user_id, &element.element_id, lang)?;
        if exists {
            let success: bool = series_location_repo::update_sync_series_location_element(conn, &element.element_id, user_id, &encrypted_name, encrypted_description.as_deref(), element.last_update, lang)?;
            if !success { return Ok(false); }
        } else {
            let success: bool = series_location_repo::insert_sync_series_location_element(
                conn, &element.element_id, &element.location_id, user_id, &encrypted_name,
                &element.original_name, encrypted_description.as_deref(), element.last_update, lang,
            )?;
            if !success { return Ok(false); }
        }
    }

    // Sync location sub-elements
    for sub_element in &complete_series.series_location_sub_elements {
        let encrypted_name: String = encrypt_data_with_user_key(&sub_element.sub_elem_name, &user_key)?;
        let encrypted_description: Option<String> = if let Some(ref val) = sub_element.sub_elem_description { Some(encrypt_data_with_user_key(val, &user_key)?) } else { None };

        let exists: bool = series_location_repo::series_location_sub_element_exists(conn, user_id, &sub_element.sub_element_id, lang)?;
        if exists {
            let success: bool = series_location_repo::update_sync_series_location_sub_element(conn, &sub_element.sub_element_id, user_id, &encrypted_name, encrypted_description.as_deref(), sub_element.last_update, lang)?;
            if !success { return Ok(false); }
        } else {
            let success: bool = series_location_repo::insert_sync_series_location_sub_element(
                conn, &sub_element.sub_element_id, &sub_element.element_id, user_id, &encrypted_name,
                &sub_element.original_name, encrypted_description.as_deref(), sub_element.last_update, lang,
            )?;
            if !success { return Ok(false); }
        }
    }

    // Sync spells
    for spell in &complete_series.series_spells {
        let encrypted_name: String = encrypt_data_with_user_key(&spell.name, &user_key)?;
        let encrypted_description: String = encrypt_data_with_user_key(&spell.description, &user_key)?;
        let encrypted_appearance: String = encrypt_data_with_user_key(&spell.appearance, &user_key)?;
        let encrypted_tags: String = encrypt_data_with_user_key(&spell.tags, &user_key)?;
        let encrypted_power_level: Option<String> = if let Some(ref val) = spell.power_level { Some(encrypt_data_with_user_key(val, &user_key)?) } else { None };
        let encrypted_components: Option<String> = if let Some(ref val) = spell.components { Some(encrypt_data_with_user_key(val, &user_key)?) } else { None };
        let encrypted_limitations: Option<String> = if let Some(ref val) = spell.limitations { Some(encrypt_data_with_user_key(val, &user_key)?) } else { None };
        let encrypted_notes: Option<String> = if let Some(ref val) = spell.notes { Some(encrypt_data_with_user_key(val, &user_key)?) } else { None };

        let exists: bool = series_spell_repo::series_spell_exists(conn, user_id, &spell.spell_id, lang)?;
        if exists {
            let success: bool = series_spell_repo::update_sync_series_spell(
                conn, &spell.spell_id, user_id, &encrypted_name, &encrypted_description,
                &encrypted_appearance, &encrypted_tags, encrypted_power_level.as_deref(),
                encrypted_components.as_deref(), encrypted_limitations.as_deref(),
                encrypted_notes.as_deref(), spell.last_update, lang,
            )?;
            if !success { return Ok(false); }
        } else {
            let success: bool = series_spell_repo::insert_sync_series_spell(
                conn, &spell.spell_id, &spell.series_id, user_id, &encrypted_name, &spell.name_hash,
                &encrypted_description, &encrypted_appearance, &encrypted_tags,
                encrypted_power_level.as_deref(), encrypted_components.as_deref(),
                encrypted_limitations.as_deref(), encrypted_notes.as_deref(), spell.last_update, lang,
            )?;
            if !success { return Ok(false); }
        }
    }

    // Sync spell tags
    for tag in &complete_series.series_spell_tags {
        let encrypted_name: String = encrypt_data_with_user_key(&tag.name, &user_key)?;

        let exists: bool = series_spell_repo::series_spell_tag_exists(conn, user_id, &tag.tag_id, lang)?;
        if exists {
            let success: bool = series_spell_repo::update_sync_series_spell_tag(conn, &tag.tag_id, user_id, &encrypted_name, tag.color.as_deref(), tag.last_update, lang)?;
            if !success { return Ok(false); }
        } else {
            let success: bool = series_spell_repo::insert_sync_series_spell_tag(
                conn, &tag.tag_id, &tag.series_id, user_id, &encrypted_name, &tag.hashed_name,
                tag.color.as_deref(), tag.last_update, lang,
            )?;
            if !success { return Ok(false); }
        }
    }

    Ok(true)
}
