use rusqlite::{params, Connection, OptionalExtension};

use crate::error::{AppError, AppResult};
use crate::shared::types::Lang;

pub struct LocationQueryResult {
    pub loc_id: String,
    pub loc_name: String,
    pub element_id: Option<String>,
    pub element_name: Option<String>,
    pub element_description: Option<String>,
    pub sub_element_id: Option<String>,
    pub sub_elem_name: Option<String>,
    pub sub_elem_description: Option<String>,
    pub series_location_id: Option<String>,
}

pub struct LocationElementQueryResult {
    pub sub_element_id: Option<String>,
    pub sub_elem_name: Option<String>,
    pub sub_elem_description: Option<String>,
    pub element_id: String,
    pub element_name: String,
    pub element_description: Option<String>,
}

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,
}

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,
}

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,
}

pub struct SyncedLocationResult {
    pub loc_id: String,
    pub book_id: String,
    pub loc_name: String,
    pub last_update: i64,
}

pub struct SyncedLocationElementResult {
    pub element_id: String,
    pub location: String,
    pub element_name: String,
    pub last_update: i64,
}

pub struct SyncedLocationSubElementResult {
    pub sub_element_id: String,
    pub element_id: String,
    pub sub_elem_name: String,
    pub last_update: i64,
}

/// Retrieves all locations with their elements and sub-elements for a specific book.
/// * `conn` - Database connection
/// * `user_id` - The user's unique identifier
/// * `book_id` - The book's unique identifier
/// * `lang` - The language for error messages ("fr" or "en")
/// Returns an array of location query results with nested elements.
pub fn get_location(conn: &Connection, user_id: &str, book_id: &str, lang: Lang) -> AppResult<Vec<LocationQueryResult>> {
    let mut statement = conn
        .prepare("SELECT loc_id, loc_name, element.element_id AS element_id, element.element_name, element.element_description, sub_elem.sub_element_id AS sub_element_id, sub_elem.sub_elem_name, sub_elem.sub_elem_description, location.series_location_id FROM book_location AS location LEFT JOIN location_element AS element ON location.loc_id = element.location LEFT JOIN location_sub_element AS sub_elem ON element.element_id = sub_elem.element_id WHERE location.user_id = ?1 AND location.book_id = ?2")
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les emplacements.".to_string() } else { "Unable to retrieve locations.".to_string() }))?;

    let locations = statement
        .query_map(params![user_id, book_id], |query_row| {
            Ok(LocationQueryResult {
                loc_id: query_row.get(0)?,
                loc_name: query_row.get(1)?,
                element_id: query_row.get(2)?,
                element_name: query_row.get(3)?,
                element_description: query_row.get(4)?,
                sub_element_id: query_row.get(5)?,
                sub_elem_name: query_row.get(6)?,
                sub_elem_description: query_row.get(7)?,
                series_location_id: query_row.get(8)?,
            })
        })
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les emplacements.".to_string() } else { "Unable to retrieve locations.".to_string() }))?
        .collect::<Result<Vec<_>, _>>()
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les emplacements.".to_string() } else { "Unable to retrieve locations.".to_string() }))?;

    Ok(locations)
}

/// Inserts a new location section for a book.
/// * `conn` - Database connection
/// * `user_id` - The user's unique identifier
/// * `location_id` - The new location's unique identifier
/// * `book_id` - The book's unique identifier
/// * `encrypted_name` - The encrypted location name
/// * `original_name` - The original (unencrypted) location name
/// * `last_update` - The timestamp of the last update
/// * `series_location_id` - The series location ID (optional)
/// * `lang` - The language for error messages ("fr" or "en")
/// Returns the location ID if insertion was successful.
pub fn insert_location(
    conn: &Connection, user_id: &str, location_id: &str, book_id: &str, encrypted_name: &str,
    original_name: &str, last_update: i64, series_location_id: Option<&str>, lang: Lang,
) -> AppResult<String> {
    let insert_result = match series_location_id {
        Some(series_id) => conn.execute(
            "INSERT INTO book_location (loc_id, book_id, user_id, loc_name, loc_original_name, series_location_id, last_update) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7)",
            params![location_id, book_id, user_id, encrypted_name, original_name, series_id, last_update],
        ),
        None => conn.execute(
            "INSERT INTO book_location (loc_id, book_id, user_id, loc_name, loc_original_name, last_update) VALUES (?1, ?2, ?3, ?4, ?5, ?6)",
            params![location_id, book_id, user_id, encrypted_name, original_name, last_update],
        ),
    }
    .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible d'ajouter la section d'emplacement.".to_string() } else { "Unable to add location section.".to_string() }))?;

    if insert_result > 0 {
        Ok(location_id.to_string())
    } else {
        Err(AppError::Internal(if lang == Lang::Fr { "Une erreur s'est produite lors de l'ajout de la section d'emplacement.".to_string() } else { "Error adding location section.".to_string() }))
    }
}

/// Inserts a new location element within a location section.
/// * `conn` - Database connection
/// * `user_id` - The user's unique identifier
/// * `element_id` - The new element's unique identifier
/// * `location_id` - The parent location's unique identifier
/// * `encrypted_name` - The encrypted element name
/// * `original_name` - The original (unencrypted) element name
/// * `last_update` - The timestamp of the last update
/// * `lang` - The language for error messages ("fr" or "en")
/// Returns the element ID if insertion was successful.
pub fn insert_location_element(
    conn: &Connection, user_id: &str, element_id: &str, location_id: &str,
    encrypted_name: &str, original_name: &str, last_update: i64, lang: Lang,
) -> AppResult<String> {
    let insert_result = conn
        .execute(
            "INSERT INTO location_element (element_id, location, user_id, element_name, original_name, element_description, last_update) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7)",
            params![element_id, location_id, user_id, encrypted_name, original_name, "", last_update],
        )
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible d'ajouter l'élément d'emplacement.".to_string() } else { "Unable to add location element.".to_string() }))?;

    if insert_result > 0 {
        Ok(element_id.to_string())
    } else {
        Err(AppError::Internal(if lang == Lang::Fr { "Une erreur s'est produite lors de l'ajout de l'élément d'emplacement.".to_string() } else { "Error adding location element.".to_string() }))
    }
}

/// Inserts a new sub-element within a location element.
/// * `conn` - Database connection
/// * `user_id` - The user's unique identifier
/// * `sub_element_id` - The new sub-element's unique identifier
/// * `element_id` - The parent element's unique identifier
/// * `encrypted_name` - The encrypted sub-element name
/// * `original_name` - The original (unencrypted) sub-element name
/// * `last_update` - The timestamp of the last update
/// * `lang` - The language for error messages ("fr" or "en")
/// Returns the sub-element ID if insertion was successful.
pub fn insert_location_sub_element(
    conn: &Connection, user_id: &str, sub_element_id: &str, element_id: &str,
    encrypted_name: &str, original_name: &str, last_update: i64, lang: Lang,
) -> AppResult<String> {
    let insert_result = conn
        .execute(
            "INSERT INTO location_sub_element (sub_element_id, element_id, user_id, sub_elem_name, original_name, sub_elem_description, last_update) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7)",
            params![sub_element_id, element_id, user_id, encrypted_name, original_name, "", last_update],
        )
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible d'ajouter le sous-élément d'emplacement.".to_string() } else { "Unable to add location sub-element.".to_string() }))?;

    if insert_result > 0 {
        Ok(sub_element_id.to_string())
    } else {
        Err(AppError::Internal(if lang == Lang::Fr { "Une erreur s'est produite lors de l'ajout du sous-élément d'emplacement.".to_string() } else { "Error adding location sub-element.".to_string() }))
    }
}

/// Updates an existing location sub-element's name and description.
/// * `conn` - Database connection
/// * `user_id` - The user's unique identifier
/// * `id` - The sub-element's unique identifier
/// * `encrypted_name` - The new encrypted sub-element name
/// * `original_name` - The new original (unencrypted) sub-element name
/// * `encrypt_description` - The new encrypted description
/// * `last_update` - The timestamp of the last update
/// * `lang` - The language for error messages ("fr" or "en")
/// Returns true if the update affected at least one row.
pub fn update_location_sub_element(
    conn: &Connection, user_id: &str, id: &str, encrypted_name: &str,
    original_name: &str, encrypt_description: &str, last_update: i64, lang: Lang,
) -> AppResult<bool> {
    let update_result = conn
        .execute(
            "UPDATE location_sub_element SET sub_elem_name = ?1, original_name = ?2, sub_elem_description = ?3, last_update = ?4 WHERE sub_element_id = ?5 AND user_id = ?6",
            params![encrypted_name, original_name, encrypt_description, last_update, id, user_id],
        )
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de mettre à jour le sous-élément d'emplacement.".to_string() } else { "Unable to update location sub-element.".to_string() }))?;

    Ok(update_result > 0)
}

/// Updates an existing location element's name and description.
/// * `conn` - Database connection
/// * `user_id` - The user's unique identifier
/// * `id` - The element's unique identifier
/// * `encrypted_name` - The new encrypted element name
/// * `original_name` - The new original (unencrypted) element name
/// * `encrypted_description` - The new encrypted description
/// * `last_update` - The timestamp of the last update
/// * `lang` - The language for error messages ("fr" or "en")
/// Returns true if the update affected at least one row.
pub fn update_location_element(
    conn: &Connection, user_id: &str, id: &str, encrypted_name: &str,
    original_name: &str, encrypted_description: &str, last_update: i64, lang: Lang,
) -> AppResult<bool> {
    let update_result = conn
        .execute(
            "UPDATE location_element SET element_name = ?1, original_name = ?2, element_description = ?3, last_update = ?4 WHERE element_id = ?5 AND user_id = ?6",
            params![encrypted_name, original_name, encrypted_description, last_update, id, user_id],
        )
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de mettre à jour l'élément d'emplacement.".to_string() } else { "Unable to update location element.".to_string() }))?;

    Ok(update_result > 0)
}

/// Updates an existing location section's name.
/// * `conn` - Database connection
/// * `user_id` - The user's unique identifier
/// * `id` - The location section's unique identifier
/// * `encrypted_name` - The new encrypted location name
/// * `original_name` - The new original (unencrypted) location name
/// * `last_update` - The timestamp of the last update
/// * `lang` - The language for error messages ("fr" or "en")
/// Returns true if the update affected at least one row.
pub fn update_location_section(
    conn: &Connection, user_id: &str, id: &str, encrypted_name: &str,
    original_name: &str, last_update: i64, lang: Lang,
) -> AppResult<bool> {
    let update_result = conn
        .execute(
            "UPDATE book_location SET loc_name = ?1, loc_original_name = ?2, last_update = ?3 WHERE loc_id = ?4 AND user_id = ?5",
            params![encrypted_name, original_name, last_update, id, user_id],
        )
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de mettre à jour la section d'emplacement.".to_string() } else { "Unable to update location section.".to_string() }))?;

    Ok(update_result > 0)
}

/// Deletes a location section by its ID.
/// * `conn` - Database connection
/// * `user_id` - The user's unique identifier
/// * `location_id` - The location section's unique identifier
/// * `lang` - The language for error messages ("fr" or "en")
/// Returns true if the deletion affected at least one row.
pub fn delete_location_section(conn: &Connection, user_id: &str, location_id: &str, lang: Lang) -> AppResult<bool> {
    let delete_result = conn
        .execute("DELETE FROM book_location WHERE loc_id = ?1 AND user_id = ?2", params![location_id, user_id])
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de supprimer la section d'emplacement.".to_string() } else { "Unable to delete location section.".to_string() }))?;

    Ok(delete_result > 0)
}

/// Deletes a location element by its ID.
/// * `conn` - Database connection
/// * `user_id` - The user's unique identifier
/// * `element_id` - The element's unique identifier
/// * `lang` - The language for error messages ("fr" or "en")
/// Returns true if the deletion affected at least one row.
pub fn delete_location_element(conn: &Connection, user_id: &str, element_id: &str, lang: Lang) -> AppResult<bool> {
    let delete_result = conn
        .execute("DELETE FROM location_element WHERE element_id = ?1 AND user_id = ?2", params![element_id, user_id])
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de supprimer l'élément d'emplacement.".to_string() } else { "Unable to delete location element.".to_string() }))?;

    Ok(delete_result > 0)
}

/// Deletes a location sub-element by its ID.
/// * `conn` - Database connection
/// * `user_id` - The user's unique identifier
/// * `sub_element_id` - The sub-element's unique identifier
/// * `lang` - The language for error messages ("fr" or "en")
/// Returns true if the deletion affected at least one row.
pub fn delete_location_sub_element(conn: &Connection, user_id: &str, sub_element_id: &str, lang: Lang) -> AppResult<bool> {
    let delete_result = conn
        .execute("DELETE FROM location_sub_element WHERE sub_element_id = ?1 AND user_id = ?2", params![sub_element_id, user_id])
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de supprimer le sous-élément d'emplacement.".to_string() } else { "Unable to delete location sub-element.".to_string() }))?;

    Ok(delete_result > 0)
}

/// Fetches all location elements and sub-elements for tagging purposes.
/// * `conn` - Database connection
/// * `user_id` - The user's unique identifier
/// * `book_id` - The book's unique identifier
/// * `lang` - The language for error messages ("fr" or "en")
/// Returns an array of location elements with their sub-elements.
pub fn fetch_location_tags(conn: &Connection, user_id: &str, book_id: &str, lang: Lang) -> AppResult<Vec<LocationElementQueryResult>> {
    let mut statement = conn
        .prepare("SELECT se.sub_element_id AS sub_element_id, se.sub_elem_name, se.sub_elem_description, el.element_id AS element_id, el.element_name, el.element_description FROM location_sub_element AS se RIGHT JOIN location_element AS el ON se.element_id = el.element_id LEFT JOIN book_location AS lo ON el.location = lo.loc_id WHERE lo.book_id = ?1 AND lo.user_id = ?2")
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les tags d'emplacement.".to_string() } else { "Unable to retrieve location tags.".to_string() }))?;

    let location_tags = statement
        .query_map(params![book_id, user_id], |query_row| {
            Ok(LocationElementQueryResult {
                sub_element_id: query_row.get(0)?,
                sub_elem_name: query_row.get(1)?,
                sub_elem_description: query_row.get(2)?,
                element_id: query_row.get(3)?,
                element_name: query_row.get(4)?,
                element_description: query_row.get(5)?,
            })
        })
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les tags d'emplacement.".to_string() } else { "Unable to retrieve location tags.".to_string() }))?
        .collect::<Result<Vec<_>, _>>()
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les tags d'emplacement.".to_string() } else { "Unable to retrieve location tags.".to_string() }))?;

    Ok(location_tags)
}

/// Fetches locations by their tag IDs (element or sub-element IDs).
/// * `conn` - Database connection
/// * `user_id` - The user's unique identifier
/// * `locations` - An array of location tag IDs to search for
/// Checks if a location exists in the database.
/// * `conn` - Database connection
/// * `user_id` - The user's unique identifier
/// * `loc_id` - The location's unique identifier
/// * `lang` - The language for error messages ("fr" or "en")
/// Returns true if the location exists, false otherwise.
pub fn is_location_exist(conn: &Connection, user_id: &str, loc_id: &str, lang: Lang) -> AppResult<bool> {
    let mut statement = conn
        .prepare("SELECT 1 FROM book_location WHERE loc_id = ?1 AND user_id = ?2")
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de vérifier l'existence de l'emplacement.".to_string() } else { "Unable to check location existence.".to_string() }))?;

    let existing_location = statement
        .query_row(params![loc_id, user_id], |query_row| query_row.get::<_, i32>(0))
        .optional()
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de vérifier l'existence de l'emplacement.".to_string() } else { "Unable to check location existence.".to_string() }))?;

    Ok(existing_location.is_some())
}

/// Checks if a location element exists in the database.
/// * `conn` - Database connection
/// * `user_id` - The user's unique identifier
/// * `element_id` - The element's unique identifier
/// * `lang` - The language for error messages ("fr" or "en")
/// Returns true if the location element exists, false otherwise.
pub fn is_location_element_exist(conn: &Connection, user_id: &str, element_id: &str, lang: Lang) -> AppResult<bool> {
    let mut statement = conn
        .prepare("SELECT 1 FROM location_element WHERE element_id = ?1 AND user_id = ?2")
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de vérifier l'existence de l'élément d'emplacement.".to_string() } else { "Unable to check location element existence.".to_string() }))?;

    let existing_element = statement
        .query_row(params![element_id, user_id], |query_row| query_row.get::<_, i32>(0))
        .optional()
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de vérifier l'existence de l'élément d'emplacement.".to_string() } else { "Unable to check location element existence.".to_string() }))?;

    Ok(existing_element.is_some())
}

/// Checks if a location sub-element exists in the database.
/// * `conn` - Database connection
/// * `user_id` - The user's unique identifier
/// * `sub_element_id` - The sub-element's unique identifier
/// * `lang` - The language for error messages ("fr" or "en")
/// Returns true if the location sub-element exists, false otherwise.
pub fn is_location_sub_element_exist(conn: &Connection, user_id: &str, sub_element_id: &str, lang: Lang) -> AppResult<bool> {
    let mut statement = conn
        .prepare("SELECT 1 FROM location_sub_element WHERE sub_element_id = ?1 AND user_id = ?2")
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de vérifier l'existence du sous-élément d'emplacement.".to_string() } else { "Unable to check location sub-element existence.".to_string() }))?;

    let existing_sub_element = statement
        .query_row(params![sub_element_id, user_id], |query_row| query_row.get::<_, i32>(0))
        .optional()
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de vérifier l'existence du sous-élément d'emplacement.".to_string() } else { "Unable to check location sub-element existence.".to_string() }))?;

    Ok(existing_sub_element.is_some())
}

/// Fetches all locations for a specific book.
/// * `conn` - Database connection
/// * `user_id` - The user's unique identifier
/// * `book_id` - The book's unique identifier
/// * `lang` - The language for error messages ("fr" or "en")
/// Returns an array of book location records.
pub fn fetch_book_locations(conn: &Connection, user_id: &str, book_id: &str, lang: Lang) -> AppResult<Vec<BookLocationTable>> {
    let mut statement = conn
        .prepare("SELECT loc_id, book_id, user_id, loc_name, loc_original_name, last_update FROM book_location WHERE user_id = ?1 AND book_id = ?2")
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les lieux.".to_string() } else { "Unable to retrieve locations.".to_string() }))?;

    let book_locations = statement
        .query_map(params![user_id, book_id], |query_row| {
            Ok(BookLocationTable {
                loc_id: query_row.get(0)?, book_id: query_row.get(1)?,
                user_id: query_row.get(2)?, loc_name: query_row.get(3)?,
                loc_original_name: query_row.get(4)?, last_update: query_row.get(5)?,
            })
        })
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les lieux.".to_string() } else { "Unable to retrieve locations.".to_string() }))?
        .collect::<Result<Vec<_>, _>>()
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les lieux.".to_string() } else { "Unable to retrieve locations.".to_string() }))?;

    Ok(book_locations)
}

pub fn fetch_all_location_elements_by_book(conn: &Connection, user_id: &str, book_id: &str, lang: Lang) -> AppResult<Vec<LocationElementTable>> {
    let mut statement = conn
        .prepare("SELECT e.element_id, e.location, e.user_id, e.element_name, e.original_name, e.element_description, e.last_update FROM location_element e INNER JOIN book_location l ON e.location = l.loc_id AND e.user_id = l.user_id WHERE e.user_id = ?1 AND l.book_id = ?2")
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les éléments de lieu.".to_string() } else { "Unable to retrieve location elements.".to_string() }))?;

    let location_elements = statement
        .query_map(params![user_id, book_id], |query_row| {
            Ok(LocationElementTable {
                element_id: query_row.get(0)?, location: query_row.get(1)?,
                user_id: query_row.get(2)?, element_name: query_row.get(3)?,
                original_name: query_row.get(4)?, element_description: query_row.get(5)?,
                last_update: query_row.get(6)?,
            })
        })
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les éléments de lieu.".to_string() } else { "Unable to retrieve location elements.".to_string() }))?
        .collect::<Result<Vec<_>, _>>()
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les éléments de lieu.".to_string() } else { "Unable to retrieve location elements.".to_string() }))?;

    Ok(location_elements)
}

pub fn fetch_all_location_sub_elements_by_book(conn: &Connection, user_id: &str, book_id: &str, lang: Lang) -> AppResult<Vec<LocationSubElementTable>> {
    let mut statement = conn
        .prepare("SELECT se.sub_element_id, se.element_id, se.user_id, se.sub_elem_name, se.original_name, se.sub_elem_description, se.last_update FROM location_sub_element se INNER JOIN location_element e ON se.element_id = e.element_id AND se.user_id = e.user_id INNER JOIN book_location l ON e.location = l.loc_id AND e.user_id = l.user_id WHERE se.user_id = ?1 AND l.book_id = ?2")
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les sous-éléments de lieu.".to_string() } else { "Unable to retrieve location sub-elements.".to_string() }))?;

    let location_sub_elements = statement
        .query_map(params![user_id, book_id], |query_row| {
            Ok(LocationSubElementTable {
                sub_element_id: query_row.get(0)?, element_id: query_row.get(1)?,
                user_id: query_row.get(2)?, sub_elem_name: query_row.get(3)?,
                original_name: query_row.get(4)?, sub_elem_description: query_row.get(5)?,
                last_update: query_row.get(6)?,
            })
        })
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les sous-éléments de lieu.".to_string() } else { "Unable to retrieve location sub-elements.".to_string() }))?
        .collect::<Result<Vec<_>, _>>()
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les sous-éléments de lieu.".to_string() } else { "Unable to retrieve location sub-elements.".to_string() }))?;

    Ok(location_sub_elements)
}

/// Fetches all synced locations for a user (used for synchronization).
/// * `conn` - Database connection
/// * `user_id` - The user's unique identifier
/// * `lang` - The language for error messages ("fr" or "en")
/// Returns an array of synced location records.
pub fn fetch_synced_locations(conn: &Connection, user_id: &str, lang: Lang) -> AppResult<Vec<SyncedLocationResult>> {
    let mut statement = conn
        .prepare("SELECT loc_id, book_id, loc_name, last_update FROM book_location WHERE user_id = ?1")
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les lieux synchronisés.".to_string() } else { "Unable to retrieve synced locations.".to_string() }))?;

    let synced_locations = statement
        .query_map(params![user_id], |query_row| {
            Ok(SyncedLocationResult { loc_id: query_row.get(0)?, book_id: query_row.get(1)?, loc_name: query_row.get(2)?, last_update: query_row.get(3)? })
        })
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les lieux synchronisés.".to_string() } else { "Unable to retrieve synced locations.".to_string() }))?
        .collect::<Result<Vec<_>, _>>()
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les lieux synchronisés.".to_string() } else { "Unable to retrieve synced locations.".to_string() }))?;

    Ok(synced_locations)
}

/// Fetches all synced location elements for a user (used for synchronization).
/// * `conn` - Database connection
/// * `user_id` - The user's unique identifier
/// * `lang` - The language for error messages ("fr" or "en")
/// Returns an array of synced location element records.
pub fn fetch_synced_location_elements(conn: &Connection, user_id: &str, lang: Lang) -> AppResult<Vec<SyncedLocationElementResult>> {
    let mut statement = conn
        .prepare("SELECT element_id, location, element_name, last_update FROM location_element WHERE user_id = ?1")
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les éléments de lieu synchronisés.".to_string() } else { "Unable to retrieve synced location elements.".to_string() }))?;

    let synced_location_elements = statement
        .query_map(params![user_id], |query_row| {
            Ok(SyncedLocationElementResult { element_id: query_row.get(0)?, location: query_row.get(1)?, element_name: query_row.get(2)?, last_update: query_row.get(3)? })
        })
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les éléments de lieu synchronisés.".to_string() } else { "Unable to retrieve synced location elements.".to_string() }))?
        .collect::<Result<Vec<_>, _>>()
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les éléments de lieu synchronisés.".to_string() } else { "Unable to retrieve synced location elements.".to_string() }))?;

    Ok(synced_location_elements)
}

/// Fetches all synced location sub-elements for a user (used for synchronization).
/// * `conn` - Database connection
/// * `user_id` - The user's unique identifier
/// * `lang` - The language for error messages ("fr" or "en")
/// Returns an array of synced location sub-element records.
pub fn fetch_synced_location_sub_elements(conn: &Connection, user_id: &str, lang: Lang) -> AppResult<Vec<SyncedLocationSubElementResult>> {
    let mut statement = conn
        .prepare("SELECT sub_element_id, element_id, sub_elem_name, last_update FROM location_sub_element WHERE user_id = ?1")
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les sous-éléments de lieu synchronisés.".to_string() } else { "Unable to retrieve synced location sub-elements.".to_string() }))?;

    let synced_location_sub_elements = statement
        .query_map(params![user_id], |query_row| {
            Ok(SyncedLocationSubElementResult { sub_element_id: query_row.get(0)?, element_id: query_row.get(1)?, sub_elem_name: query_row.get(2)?, last_update: query_row.get(3)? })
        })
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les sous-éléments de lieu synchronisés.".to_string() } else { "Unable to retrieve synced location sub-elements.".to_string() }))?
        .collect::<Result<Vec<_>, _>>()
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les sous-éléments de lieu synchronisés.".to_string() } else { "Unable to retrieve synced location sub-elements.".to_string() }))?;

    Ok(synced_location_sub_elements)
}

/// Inserts a synced location from the remote server.
/// * `conn` - Database connection
/// * `loc_id` - The location's unique identifier
/// * `book_id` - The book's unique identifier
/// * `user_id` - The user's unique identifier
/// * `loc_name` - The encrypted location name
/// * `loc_original_name` - The original (unencrypted) location name
/// * `last_update` - The timestamp of the last update
/// * `lang` - The language for error messages ("fr" or "en")
/// Returns true if the insertion affected at least one row.
pub fn insert_sync_location(
    conn: &Connection, loc_id: &str, book_id: &str, user_id: &str,
    loc_name: &str, loc_original_name: &str, last_update: i64, lang: Lang,
) -> AppResult<bool> {
    let insert_result = conn
        .execute(
            "INSERT INTO book_location (loc_id, book_id, user_id, loc_name, loc_original_name, last_update) VALUES (?1, ?2, ?3, ?4, ?5, ?6)",
            params![loc_id, book_id, user_id, loc_name, loc_original_name, last_update],
        )
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible d'insérer le lieu.".to_string() } else { "Unable to insert location.".to_string() }))?;

    Ok(insert_result > 0)
}

/// Inserts a synced location element from the remote server.
/// * `conn` - Database connection
/// * `element_id` - The element's unique identifier
/// * `location` - The parent location's unique identifier
/// * `user_id` - The user's unique identifier
/// * `element_name` - The encrypted element name
/// * `original_name` - The original (unencrypted) element name
/// * `element_description` - The encrypted element description (can be null)
/// * `last_update` - The timestamp of the last update
/// * `lang` - The language for error messages ("fr" or "en")
/// Returns true if the insertion affected at least one row.
pub fn insert_sync_location_element(
    conn: &Connection, element_id: &str, location: &str, user_id: &str, element_name: &str,
    original_name: &str, element_description: Option<&str>, last_update: i64, lang: Lang,
) -> AppResult<bool> {
    let insert_result = conn
        .execute(
            "INSERT INTO location_element (element_id, location, user_id, element_name, original_name, element_description, last_update) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7)",
            params![element_id, location, user_id, element_name, original_name, element_description, last_update],
        )
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible d'insérer l'élément du lieu.".to_string() } else { "Unable to insert location element.".to_string() }))?;

    Ok(insert_result > 0)
}

/// Inserts a synced location sub-element from the remote server.
/// * `conn` - Database connection
/// * `sub_element_id` - The sub-element's unique identifier
/// * `element_id` - The parent element's unique identifier
/// * `user_id` - The user's unique identifier
/// * `sub_elem_name` - The encrypted sub-element name
/// * `original_name` - The original (unencrypted) sub-element name
/// * `sub_elem_description` - The encrypted sub-element description (can be null)
/// * `last_update` - The timestamp of the last update
/// * `lang` - The language for error messages ("fr" or "en")
/// Returns true if the insertion affected at least one row.
pub fn insert_sync_location_sub_element(
    conn: &Connection, sub_element_id: &str, element_id: &str, user_id: &str, sub_elem_name: &str,
    original_name: &str, sub_elem_description: Option<&str>, last_update: i64, lang: Lang,
) -> AppResult<bool> {
    let insert_result = conn
        .execute(
            "INSERT INTO location_sub_element (sub_element_id, element_id, user_id, sub_elem_name, original_name, sub_elem_description, last_update) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7)",
            params![sub_element_id, element_id, user_id, sub_elem_name, original_name, sub_elem_description, last_update],
        )
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible d'insérer le sous-élément du lieu.".to_string() } else { "Unable to insert location sub-element.".to_string() }))?;

    Ok(insert_result > 0)
}

/// Fetches complete location data by its ID (without user filtering).
/// * `conn` - Database connection
/// * `id` - The location's unique identifier
/// * `lang` - The language for error messages ("fr" or "en")
/// Returns an array of book location records.
pub fn fetch_complete_location_by_id(conn: &Connection, id: &str, lang: Lang) -> AppResult<Vec<BookLocationTable>> {
    let mut statement = conn
        .prepare("SELECT loc_id, book_id, user_id, loc_name, loc_original_name, last_update FROM book_location WHERE loc_id = ?1")
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer le lieu complet.".to_string() } else { "Unable to retrieve complete location.".to_string() }))?;

    let complete_location = statement
        .query_map(params![id], |query_row| {
            Ok(BookLocationTable {
                loc_id: query_row.get(0)?, book_id: query_row.get(1)?,
                user_id: query_row.get(2)?, loc_name: query_row.get(3)?,
                loc_original_name: query_row.get(4)?, last_update: query_row.get(5)?,
            })
        })
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer le lieu complet.".to_string() } else { "Unable to retrieve complete location.".to_string() }))?
        .collect::<Result<Vec<_>, _>>()
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer le lieu complet.".to_string() } else { "Unable to retrieve complete location.".to_string() }))?;

    Ok(complete_location)
}

/// Fetches complete location element data by its ID (without user filtering).
/// * `conn` - Database connection
/// * `id` - The element's unique identifier
/// * `lang` - The language for error messages ("fr" or "en")
/// Returns an array of location element records.
pub fn fetch_complete_location_element_by_id(conn: &Connection, id: &str, lang: Lang) -> AppResult<Vec<LocationElementTable>> {
    let mut statement = conn
        .prepare("SELECT element_id, location, user_id, element_name, original_name, element_description, last_update FROM location_element WHERE element_id = ?1")
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer l'élément de lieu complet.".to_string() } else { "Unable to retrieve complete location element.".to_string() }))?;

    let complete_location_element = statement
        .query_map(params![id], |query_row| {
            Ok(LocationElementTable {
                element_id: query_row.get(0)?, location: query_row.get(1)?,
                user_id: query_row.get(2)?, element_name: query_row.get(3)?,
                original_name: query_row.get(4)?, element_description: query_row.get(5)?,
                last_update: query_row.get(6)?,
            })
        })
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer l'élément de lieu complet.".to_string() } else { "Unable to retrieve complete location element.".to_string() }))?
        .collect::<Result<Vec<_>, _>>()
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer l'élément de lieu complet.".to_string() } else { "Unable to retrieve complete location element.".to_string() }))?;

    Ok(complete_location_element)
}

/// Fetches complete location sub-element data by its ID (without user filtering).
/// * `conn` - Database connection
/// * `id` - The sub-element's unique identifier
/// * `lang` - The language for error messages ("fr" or "en")
/// Returns an array of location sub-element records.
pub fn fetch_complete_location_sub_element_by_id(conn: &Connection, id: &str, lang: Lang) -> AppResult<Vec<LocationSubElementTable>> {
    let mut statement = conn
        .prepare("SELECT sub_element_id, element_id, user_id, sub_elem_name, original_name, sub_elem_description, last_update FROM location_sub_element WHERE sub_element_id = ?1")
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer le sous-élément de lieu complet.".to_string() } else { "Unable to retrieve complete location sub-element.".to_string() }))?;

    let complete_location_sub_element = statement
        .query_map(params![id], |query_row| {
            Ok(LocationSubElementTable {
                sub_element_id: query_row.get(0)?, element_id: query_row.get(1)?,
                user_id: query_row.get(2)?, sub_elem_name: query_row.get(3)?,
                original_name: query_row.get(4)?, sub_elem_description: query_row.get(5)?,
                last_update: query_row.get(6)?,
            })
        })
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer le sous-élément de lieu complet.".to_string() } else { "Unable to retrieve complete location sub-element.".to_string() }))?
        .collect::<Result<Vec<_>, _>>()
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer le sous-élément de lieu complet.".to_string() } else { "Unable to retrieve complete location sub-element.".to_string() }))?;

    Ok(complete_location_sub_element)
}

/// Updates a location section with optional name change and series link.
/// * `conn` - Database connection
/// * `user_id` - The user's unique identifier
/// * `section_id` - The section's unique identifier
/// * `encrypted_name` - The new encrypted name (optional)
/// * `original_name` - The new original name (optional)
/// * `series_location_id` - The series location ID to link (optional, None to unlink)
/// * `last_update` - The timestamp of the last update
/// * `lang` - The language for error messages ("fr" or "en")
/// Returns true if the update was successful.
pub fn update_section_with_series_link(
    conn: &Connection, user_id: &str, section_id: &str, encrypted_name: Option<&str>,
    original_name: Option<&str>, series_location_id: Option<&str>, last_update: i64, lang: Lang,
) -> AppResult<bool> {
    let mut set_clauses: Vec<String> = vec![format!("last_update={}", last_update)];
    let mut param_values: Vec<Box<dyn rusqlite::types::ToSql>> = Vec::new();

    if let (Some(enc_name), Some(orig_name)) = (encrypted_name, original_name) {
        set_clauses.push("loc_name=?".to_string());
        set_clauses.push("loc_original_name=?".to_string());
        param_values.push(Box::new(enc_name.to_string()));
        param_values.push(Box::new(orig_name.to_string()));
    }

    if let Some(series_id) = series_location_id {
        set_clauses.push("series_location_id=?".to_string());
        param_values.push(Box::new(series_id.to_string()));
    }

    param_values.push(Box::new(section_id.to_string()));
    param_values.push(Box::new(user_id.to_string()));

    let query = format!("UPDATE book_location SET {} WHERE loc_id=? AND user_id=?", set_clauses.join(", "));
    let param_refs: Vec<&dyn rusqlite::types::ToSql> = param_values.iter().map(|parameter| parameter.as_ref() as &dyn rusqlite::types::ToSql).collect();

    let update_result = conn
        .execute(&query, param_refs.as_slice())
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de mettre à jour la section d'emplacement.".to_string() } else { "Unable to update location section.".to_string() }))?;

    Ok(update_result > 0)
}
