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

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

pub struct SeriesCharacterResult {
    pub character_id: String,
    pub first_name: String,
    pub last_name: 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 title: String,
    pub category: String,
    pub image: String,
    pub role: String,
    pub biography: String,
    pub history: String,
    pub speech_pattern: Option<String>,
    pub catchphrase: Option<String>,
    pub residence: Option<String>,
    pub notes: Option<String>,
    pub color: Option<String>,
}

pub struct SeriesCharacterAttributeResult {
    pub attr_id: String,
    pub _attribute_name: String,
    pub attribute_value: String,
}

pub struct SeriesCharactersTableResult {
    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<String>,
    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,
}

pub struct SeriesCharacterAttributesTableResult {
    pub attr_id: String,
    pub character_id: String,
    pub user_id: String,
    pub attribute_name: String,
    pub attribute_value: String,
    pub last_update: i64,
}

pub struct SyncedSeriesCharacterResult {
    pub character_id: String,
    pub series_id: String,
    pub first_name: String,
    pub last_update: i64,
}

pub struct SyncedSeriesCharacterAttributeResult {
    pub attr_id: String,
    pub character_id: String,
    pub attribute_name: String,
    pub last_update: i64,
}

/// Fetches all characters for a specific series owned by the user.
/// * `conn` - Database connection
/// * `user_id` - The unique identifier of the user
/// * `series_id` - The unique identifier of the series
/// * `lang` - The language for error messages ("fr" or "en")
/// Returns an array of character results.
pub fn fetch_characters(conn: &Connection, user_id: &str, series_id: &str, lang: Lang) -> AppResult<Vec<SeriesCharacterResult>> {
    let mut statement = conn
        .prepare("SELECT character_id, first_name, last_name, nickname, age, gender, species, nationality, status, title, category, image, role, biography, history, speech_pattern, catchphrase, residence, notes, color FROM series_characters WHERE series_id = ?1 AND user_id = ?2")
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les personnages de la série.".to_string() } else { "Unable to retrieve series characters.".to_string() }))?;

    let characters = statement
        .query_map(params![series_id, user_id], |query_row| {
            Ok(SeriesCharacterResult {
                character_id: query_row.get(0)?, first_name: query_row.get(1)?,
                last_name: query_row.get(2)?, nickname: query_row.get(3)?,
                age: query_row.get(4)?, gender: query_row.get(5)?,
                species: query_row.get(6)?, nationality: query_row.get(7)?,
                status: query_row.get(8)?, title: query_row.get(9)?,
                category: query_row.get(10)?, image: query_row.get(11)?,
                role: query_row.get(12)?, biography: query_row.get(13)?,
                history: query_row.get(14)?, speech_pattern: query_row.get(15)?,
                catchphrase: query_row.get(16)?, residence: query_row.get(17)?,
                notes: query_row.get(18)?, color: query_row.get(19)?,
            })
        })
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les personnages de la série.".to_string() } else { "Unable to retrieve series characters.".to_string() }))?
        .collect::<Result<Vec<_>, _>>()
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les personnages de la série.".to_string() } else { "Unable to retrieve series characters.".to_string() }))?;

    Ok(characters)
}

/// Adds a new character to a series.
/// * `conn` - Database connection
/// * `user_id` - The unique identifier of the user
/// * `character_id` - The unique identifier of the character
/// * `encrypted_name` - The encrypted first name
/// * `encrypted_last_name` - The encrypted last name
/// * `encrypted_nickname` - The encrypted nickname
/// * `encrypted_age` - The encrypted age
/// * `encrypted_gender` - The encrypted gender
/// * `encrypted_species` - The encrypted species
/// * `encrypted_nationality` - The encrypted nationality
/// * `encrypted_status` - The encrypted status
/// * `encrypted_title` - The encrypted title
/// * `encrypted_category` - The encrypted category
/// * `encrypted_image` - The encrypted image
/// * `encrypted_role` - The encrypted role
/// * `encrypted_biography` - The encrypted biography
/// * `encrypted_history` - The encrypted history
/// * `encrypted_speech_pattern` - The encrypted speech pattern
/// * `encrypted_catchphrase` - The encrypted catchphrase
/// * `encrypted_residence` - The encrypted residence
/// * `encrypted_notes` - The encrypted notes
/// * `encrypted_color` - The encrypted color
/// * `series_id` - The unique identifier of the series
/// * `last_update` - The timestamp for the last update
/// * `lang` - The language for error messages ("fr" or "en")
/// Returns the character ID if insertion was successful.
pub fn add_new_character(
    conn: &Connection, user_id: &str, character_id: &str, encrypted_name: &str,
    encrypted_last_name: Option<&str>, encrypted_nickname: Option<&str>, encrypted_age: Option<&str>,
    encrypted_gender: Option<&str>, encrypted_species: Option<&str>, encrypted_nationality: Option<&str>,
    encrypted_status: Option<&str>, encrypted_title: Option<&str>, encrypted_category: Option<&str>,
    encrypted_image: Option<&str>, encrypted_role: Option<&str>, encrypted_biography: Option<&str>,
    encrypted_history: Option<&str>, encrypted_speech_pattern: Option<&str>, encrypted_catchphrase: Option<&str>,
    encrypted_residence: Option<&str>, encrypted_notes: Option<&str>, encrypted_color: Option<&str>,
    series_id: &str, last_update: i64, lang: Lang,
) -> AppResult<String> {
    let insert_result = conn
        .execute(
            "INSERT INTO series_characters (character_id, series_id, user_id, first_name, last_name, nickname, age, gender, species, nationality, status, category, title, image, role, biography, history, speech_pattern, catchphrase, residence, notes, color, last_update) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12, ?13, ?14, ?15, ?16, ?17, ?18, ?19, ?20, ?21, ?22, ?23)",
            params![character_id, series_id, user_id, encrypted_name, encrypted_last_name, encrypted_nickname, encrypted_age, encrypted_gender, encrypted_species, encrypted_nationality, encrypted_status, encrypted_category, encrypted_title, encrypted_image, encrypted_role, encrypted_biography, encrypted_history, encrypted_speech_pattern, encrypted_catchphrase, encrypted_residence, encrypted_notes, encrypted_color, last_update],
        )
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible d'ajouter le personnage.".to_string() } else { "Unable to add character.".to_string() }))?;

    if insert_result > 0 {
        Ok(character_id.to_string())
    } else {
        Err(AppError::Internal(if lang == Lang::Fr { "Une erreur s'est produite lors de l'ajout du personnage.".to_string() } else { "Error adding character.".to_string() }))
    }
}

/// Inserts a new attribute for a series character.
/// * `conn` - Database connection
/// * `attribute_id` - The unique identifier of the attribute
/// * `character_id` - The unique identifier of the character
/// * `user_id` - The unique identifier of the user
/// * `attribute_type` - The attribute type/name
/// * `name` - The attribute value
/// * `last_update` - The timestamp for the last update
/// * `lang` - The language for error messages ("fr" or "en")
/// Returns the attribute ID if insertion was successful.
pub fn insert_attribute(
    conn: &Connection, attribute_id: &str, character_id: &str, user_id: &str,
    attribute_type: &str, name: &str, last_update: i64, lang: Lang,
) -> AppResult<String> {
    let insert_result = conn
        .execute(
            "INSERT INTO series_characters_attributes (attr_id, character_id, user_id, attribute_name, attribute_value, last_update) VALUES (?1, ?2, ?3, ?4, ?5, ?6)",
            params![attribute_id, character_id, user_id, attribute_type, name, last_update],
        )
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible d'ajouter l'attribut.".to_string() } else { "Unable to add attribute.".to_string() }))?;

    if insert_result > 0 {
        Ok(attribute_id.to_string())
    } else {
        Err(AppError::Internal(if lang == Lang::Fr { "Une erreur s'est produite lors de l'ajout de l'attribut.".to_string() } else { "Error adding attribute.".to_string() }))
    }
}

/// Updates an existing series character's information.
/// * `conn` - Database connection
/// * `user_id` - The unique identifier of the user
/// * `character_id` - The unique identifier of the character
/// * `encrypted_name` - The encrypted first name
/// * `encrypted_last_name` - The encrypted last name
/// * `encrypted_nickname` - The encrypted nickname
/// * `encrypted_age` - The encrypted age
/// * `encrypted_gender` - The encrypted gender
/// * `encrypted_species` - The encrypted species
/// * `encrypted_nationality` - The encrypted nationality
/// * `encrypted_status` - The encrypted status
/// * `encrypted_title` - The encrypted title
/// * `encrypted_category` - The encrypted category
/// * `encrypted_image` - The encrypted image
/// * `encrypted_role` - The encrypted role
/// * `encrypted_biography` - The encrypted biography
/// * `encrypted_history` - The encrypted history
/// * `encrypted_speech_pattern` - The encrypted speech pattern
/// * `encrypted_catchphrase` - The encrypted catchphrase
/// * `encrypted_residence` - The encrypted residence
/// * `encrypted_notes` - The encrypted notes
/// * `encrypted_color` - The encrypted color
/// * `last_update` - The timestamp for the last update
/// * `lang` - The language for error messages ("fr" or "en")
/// Returns true if the update was successful, false otherwise.
pub fn update_character(
    conn: &Connection, user_id: &str, character_id: &str, encrypted_name: &str,
    encrypted_last_name: Option<&str>, encrypted_nickname: Option<&str>, encrypted_age: Option<&str>,
    encrypted_gender: Option<&str>, encrypted_species: Option<&str>, encrypted_nationality: Option<&str>,
    encrypted_status: Option<&str>, encrypted_title: Option<&str>, encrypted_category: Option<&str>,
    encrypted_image: Option<&str>, encrypted_role: Option<&str>, encrypted_biography: Option<&str>,
    encrypted_history: Option<&str>, encrypted_speech_pattern: Option<&str>, encrypted_catchphrase: Option<&str>,
    encrypted_residence: Option<&str>, encrypted_notes: Option<&str>, encrypted_color: Option<&str>,
    last_update: i64, lang: Lang,
) -> AppResult<bool> {
    let update_result = conn
        .execute(
            "UPDATE series_characters SET first_name = ?1, last_name = ?2, nickname = ?3, age = ?4, gender = ?5, species = ?6, nationality = ?7, status = ?8, title = ?9, category = ?10, image = ?11, role = ?12, biography = ?13, history = ?14, speech_pattern = ?15, catchphrase = ?16, residence = ?17, notes = ?18, color = ?19, last_update = ?20 WHERE character_id = ?21 AND user_id = ?22",
            params![encrypted_name, encrypted_last_name, encrypted_nickname, encrypted_age, encrypted_gender, encrypted_species, encrypted_nationality, encrypted_status, encrypted_title, encrypted_category, encrypted_image, encrypted_role, encrypted_biography, encrypted_history, encrypted_speech_pattern, encrypted_catchphrase, encrypted_residence, encrypted_notes, encrypted_color, last_update, character_id, user_id],
        )
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de mettre à jour le personnage.".to_string() } else { "Unable to update character.".to_string() }))?;

    Ok(update_result > 0)
}

/// Deletes a series character and all its related data via CASCADE.
/// * `conn` - Database connection
/// * `user_id` - The unique identifier of the user
/// * `character_id` - The unique identifier of the character
/// * `lang` - The language for error messages ("fr" or "en")
/// Returns true if the deletion was successful, false otherwise.
pub fn delete_character(conn: &Connection, user_id: &str, character_id: &str, lang: Lang) -> AppResult<bool> {
    // Delete attributes first
    conn.execute("DELETE FROM series_characters_attributes WHERE character_id = ?1 AND user_id = ?2", params![character_id, user_id])
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de supprimer le personnage.".to_string() } else { "Unable to delete character.".to_string() }))?;

    // Delete character
    let delete_result = conn
        .execute("DELETE FROM series_characters WHERE character_id = ?1 AND user_id = ?2", params![character_id, user_id])
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de supprimer le personnage.".to_string() } else { "Unable to delete character.".to_string() }))?;

    Ok(delete_result > 0)
}

/// Deletes an attribute from a series character.
/// * `conn` - Database connection
/// * `user_id` - The unique identifier of the user
/// * `attribute_id` - The unique identifier of the attribute
/// * `lang` - The language for error messages ("fr" or "en")
/// Returns true if the deletion was successful, false otherwise.
pub fn delete_attribute(conn: &Connection, user_id: &str, attribute_id: &str, lang: Lang) -> AppResult<bool> {
    let delete_result = conn
        .execute("DELETE FROM series_characters_attributes WHERE attr_id = ?1 AND user_id = ?2", params![attribute_id, user_id])
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de supprimer l'attribut.".to_string() } else { "Unable to delete attribute.".to_string() }))?;

    Ok(delete_result > 0)
}

/// Fetches all attributes for a specific series character.
/// * `conn` - Database connection
/// * `character_id` - The unique identifier of the character
/// * `user_id` - The unique identifier of the user
/// * `lang` - The language for error messages ("fr" or "en")
/// Returns an array of attribute results.
pub fn fetch_attributes(conn: &Connection, character_id: &str, user_id: &str, lang: Lang) -> AppResult<Vec<SeriesCharacterAttributeResult>> {
    let mut statement = conn
        .prepare("SELECT attr_id, attribute_name, attribute_value FROM series_characters_attributes WHERE character_id = ?1 AND user_id = ?2")
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les attributs.".to_string() } else { "Unable to retrieve attributes.".to_string() }))?;

    let attributes = statement
        .query_map(params![character_id, user_id], |query_row| {
            Ok(SeriesCharacterAttributeResult { attr_id: query_row.get(0)?, _attribute_name: query_row.get(1)?, attribute_value: query_row.get(2)? })
        })
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les attributs.".to_string() } else { "Unable to retrieve attributes.".to_string() }))?
        .collect::<Result<Vec<_>, _>>()
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les attributs.".to_string() } else { "Unable to retrieve attributes.".to_string() }))?;

    Ok(attributes)
}

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

    let existence_check = statement
        .query_row(params![character_id, user_id], |_query_row| Ok(true))
        .optional()
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de vérifier l'existence du personnage.".to_string() } else { "Unable to check character existence.".to_string() }))?;

    Ok(existence_check.is_some())
}

/// Fetches all characters for a series for sync.
/// * `conn` - Database connection
/// * `user_id` - The unique identifier of the user
/// * `series_id` - The unique identifier of the series
/// * `lang` - The language for error messages ("fr" or "en")
/// Returns an array of full character table results.
pub fn fetch_series_characters_table(conn: &Connection, user_id: &str, series_id: &str, lang: Lang) -> AppResult<Vec<SeriesCharactersTableResult>> {
    let mut statement = conn
        .prepare("SELECT character_id, series_id, user_id, first_name, last_name, nickname, age, gender, species, nationality, status, title, category, image, role, biography, history, speech_pattern, catchphrase, residence, notes, color, last_update FROM series_characters WHERE series_id = ?1 AND user_id = ?2")
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les personnages pour sync.".to_string() } else { "Unable to retrieve characters for sync.".to_string() }))?;

    let characters = statement
        .query_map(params![series_id, user_id], |query_row| {
            Ok(SeriesCharactersTableResult {
                character_id: query_row.get(0)?, series_id: query_row.get(1)?,
                user_id: query_row.get(2)?, first_name: query_row.get(3)?,
                last_name: query_row.get(4)?, nickname: query_row.get(5)?,
                age: query_row.get(6)?, gender: query_row.get(7)?,
                species: query_row.get(8)?, nationality: query_row.get(9)?,
                status: query_row.get(10)?, title: query_row.get(11)?,
                category: query_row.get(12)?, image: query_row.get(13)?,
                role: query_row.get(14)?, biography: query_row.get(15)?,
                history: query_row.get(16)?, speech_pattern: query_row.get(17)?,
                catchphrase: query_row.get(18)?, residence: query_row.get(19)?,
                notes: query_row.get(20)?, color: query_row.get(21)?,
                last_update: query_row.get(22)?,
            })
        })
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les personnages pour sync.".to_string() } else { "Unable to retrieve characters for sync.".to_string() }))?
        .collect::<Result<Vec<_>, _>>()
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les personnages pour sync.".to_string() } else { "Unable to retrieve characters for sync.".to_string() }))?;

    Ok(characters)
}

/// Fetches all series characters for a user for sync comparison.
/// * `conn` - Database connection
/// * `user_id` - The unique identifier of the user
/// * `lang` - The language for error messages ("fr" or "en")
/// Returns an array of synced character results.
pub fn fetch_synced_series_characters(conn: &Connection, user_id: &str, lang: Lang) -> AppResult<Vec<SyncedSeriesCharacterResult>> {
    let mut statement = conn
        .prepare("SELECT character_id, series_id, first_name, last_update FROM series_characters WHERE user_id = ?1")
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les personnages de série pour sync.".to_string() } else { "Unable to retrieve series characters for sync.".to_string() }))?;

    let characters = statement
        .query_map(params![user_id], |query_row| {
            Ok(SyncedSeriesCharacterResult { character_id: query_row.get(0)?, series_id: query_row.get(1)?, first_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 personnages de série pour sync.".to_string() } else { "Unable to retrieve series characters for sync.".to_string() }))?
        .collect::<Result<Vec<_>, _>>()
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les personnages de série pour sync.".to_string() } else { "Unable to retrieve series characters for sync.".to_string() }))?;

    Ok(characters)
}

/// Fetches all series character attributes for a user for sync comparison.
/// * `conn` - Database connection
/// * `user_id` - The unique identifier of the user
/// * `lang` - The language for error messages ("fr" or "en")
/// Returns an array of synced attribute results.
pub fn fetch_synced_series_character_attributes(conn: &Connection, user_id: &str, lang: Lang) -> AppResult<Vec<SyncedSeriesCharacterAttributeResult>> {
    let mut statement = conn
        .prepare("SELECT attr_id, character_id, attribute_name, last_update FROM series_characters_attributes WHERE user_id = ?1")
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les attributs de personnage pour sync.".to_string() } else { "Unable to retrieve character attributes for sync.".to_string() }))?;

    let attributes = statement
        .query_map(params![user_id], |query_row| {
            Ok(SyncedSeriesCharacterAttributeResult { attr_id: query_row.get(0)?, character_id: query_row.get(1)?, attribute_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 attributs de personnage pour sync.".to_string() } else { "Unable to retrieve character attributes for sync.".to_string() }))?
        .collect::<Result<Vec<_>, _>>()
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les attributs de personnage pour sync.".to_string() } else { "Unable to retrieve character attributes for sync.".to_string() }))?;

    Ok(attributes)
}

/// Inserts a series character for sync.
/// * `conn` - Database connection
/// * `character_id` - The unique identifier of the character
/// * `series_id` - The unique identifier of the series
/// * `user_id` - The unique identifier of the user
/// * `first_name` - The character's first name
/// * `last_name` - The character's last name
/// * `nickname` - The character's nickname
/// * `age` - The character's age
/// * `gender` - The character's gender
/// * `species` - The character's species
/// * `nationality` - The character's nationality
/// * `status` - The character's status
/// * `category` - The character's category
/// * `title` - The character's title
/// * `image` - The character's image
/// * `role` - The character's role
/// * `biography` - The character's biography
/// * `history` - The character's history
/// * `speech_pattern` - The character's speech pattern
/// * `catchphrase` - The character's catchphrase
/// * `residence` - The character's residence
/// * `notes` - The character's notes
/// * `color` - The character's color
/// * `last_update` - The timestamp for the last update
/// * `lang` - The language for error messages ("fr" or "en")
/// Returns true if the insertion was successful, false otherwise.
pub fn insert_sync_series_character(
    conn: &Connection, character_id: &str, series_id: &str, user_id: &str, first_name: &str,
    last_name: Option<&str>, nickname: Option<&str>, age: Option<&str>, gender: Option<&str>,
    species: Option<&str>, nationality: Option<&str>, status: Option<&str>, category: &str,
    title: Option<&str>, image: Option<&str>, role: Option<&str>, biography: Option<&str>,
    history: Option<&str>, speech_pattern: Option<&str>, catchphrase: Option<&str>,
    residence: Option<&str>, notes: Option<&str>, color: Option<&str>, last_update: i64, lang: Lang,
) -> AppResult<bool> {
    let insert_result = conn
        .execute(
            "INSERT INTO series_characters (character_id, series_id, user_id, first_name, last_name, nickname, age, gender, species, nationality, status, category, title, image, role, biography, history, speech_pattern, catchphrase, residence, notes, color, last_update) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12, ?13, ?14, ?15, ?16, ?17, ?18, ?19, ?20, ?21, ?22, ?23) ON CONFLICT(character_id) DO UPDATE SET first_name = excluded.first_name, last_name = excluded.last_name, nickname = excluded.nickname, age = excluded.age, gender = excluded.gender, species = excluded.species, nationality = excluded.nationality, status = excluded.status, category = excluded.category, title = excluded.title, image = excluded.image, role = excluded.role, biography = excluded.biography, history = excluded.history, speech_pattern = excluded.speech_pattern, catchphrase = excluded.catchphrase, residence = excluded.residence, notes = excluded.notes, color = excluded.color, last_update = excluded.last_update",
            params![character_id, series_id, user_id, first_name, last_name, nickname, age, gender, species, nationality, status, category, title, image, role, biography, history, speech_pattern, catchphrase, residence, notes, color, last_update],
        )
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible d'insérer le personnage pour sync.".to_string() } else { "Unable to insert character for sync.".to_string() }))?;

    Ok(insert_result > 0)
}

/// Updates a series character for sync.
/// * `conn` - Database connection
/// * `user_id` - The unique identifier of the user
/// * `character_id` - The unique identifier of the character
/// * `first_name` - The character's first name
/// * `last_name` - The character's last name
/// * `nickname` - The character's nickname
/// * `age` - The character's age
/// * `gender` - The character's gender
/// * `species` - The character's species
/// * `nationality` - The character's nationality
/// * `status` - The character's status
/// * `category` - The character's category
/// * `title` - The character's title
/// * `image` - The character's image
/// * `role` - The character's role
/// * `biography` - The character's biography
/// * `history` - The character's history
/// * `speech_pattern` - The character's speech pattern
/// * `catchphrase` - The character's catchphrase
/// * `residence` - The character's residence
/// * `notes` - The character's notes
/// * `color` - The character's color
/// * `last_update` - The timestamp for the last update
/// * `lang` - The language for error messages ("fr" or "en")
/// Returns true if the update was successful, false otherwise.
pub fn update_sync_series_character(
    conn: &Connection, user_id: &str, character_id: &str, first_name: &str,
    last_name: Option<&str>, nickname: Option<&str>, age: Option<&str>, gender: Option<&str>,
    species: Option<&str>, nationality: Option<&str>, status: Option<&str>, category: &str,
    title: Option<&str>, image: Option<&str>, role: Option<&str>, biography: Option<&str>,
    history: Option<&str>, speech_pattern: Option<&str>, catchphrase: Option<&str>,
    residence: Option<&str>, notes: Option<&str>, color: Option<&str>, last_update: i64, lang: Lang,
) -> AppResult<bool> {
    let update_result = conn
        .execute(
            "UPDATE series_characters SET first_name = ?1, last_name = ?2, nickname = ?3, age = ?4, gender = ?5, species = ?6, nationality = ?7, status = ?8, category = ?9, title = ?10, image = ?11, role = ?12, biography = ?13, history = ?14, speech_pattern = ?15, catchphrase = ?16, residence = ?17, notes = ?18, color = ?19, last_update = ?20 WHERE character_id = ?21 AND user_id = ?22",
            params![first_name, last_name, nickname, age, gender, species, nationality, status, category, title, image, role, biography, history, speech_pattern, catchphrase, residence, notes, color, last_update, character_id, user_id],
        )
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de mettre à jour le personnage pour sync.".to_string() } else { "Unable to update character for sync.".to_string() }))?;

    Ok(update_result > 0)
}

/// Inserts a series character attribute for sync.
/// * `conn` - Database connection
/// * `attr_id` - The unique identifier of the attribute
/// * `character_id` - The unique identifier of the character
/// * `user_id` - The unique identifier of the user
/// * `attribute_name` - The attribute name
/// * `attribute_value` - The attribute value
/// * `last_update` - The timestamp for the last update
/// * `lang` - The language for error messages ("fr" or "en")
/// Returns true if the insertion was successful, false otherwise.
pub fn insert_sync_series_character_attribute(
    conn: &Connection, attr_id: &str, character_id: &str, user_id: &str,
    attribute_name: &str, attribute_value: &str, last_update: i64, lang: Lang,
) -> AppResult<bool> {
    let insert_result = conn
        .execute(
            "INSERT INTO series_characters_attributes (attr_id, character_id, user_id, attribute_name, attribute_value, last_update) VALUES (?1, ?2, ?3, ?4, ?5, ?6) ON CONFLICT(attr_id) DO UPDATE SET attribute_name = excluded.attribute_name, attribute_value = excluded.attribute_value, last_update = excluded.last_update",
            params![attr_id, character_id, user_id, attribute_name, attribute_value, last_update],
        )
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible d'insérer l'attribut pour sync.".to_string() } else { "Unable to insert attribute for sync.".to_string() }))?;

    Ok(insert_result > 0)
}

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

    let existence_check = statement
        .query_row(params![attr_id, user_id], |_query_row| Ok(true))
        .optional()
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de vérifier l'existence de l'attribut.".to_string() } else { "Unable to check attribute existence.".to_string() }))?;

    Ok(existence_check.is_some())
}

/// Updates a series character attribute for sync.
/// * `conn` - Database connection
/// * `user_id` - The unique identifier of the user
/// * `attr_id` - The unique identifier of the attribute
/// * `attribute_name` - The attribute name
/// * `attribute_value` - The attribute value
/// * `last_update` - The timestamp for the last update
/// * `lang` - The language for error messages ("fr" or "en")
/// Returns true if the update was successful, false otherwise.
pub fn update_sync_series_character_attribute(
    conn: &Connection, user_id: &str, attr_id: &str, attribute_name: &str,
    attribute_value: &str, last_update: i64, lang: Lang,
) -> AppResult<bool> {
    let update_result = conn
        .execute(
            "UPDATE series_characters_attributes SET attribute_name = ?1, attribute_value = ?2, last_update = ?3 WHERE attr_id = ?4 AND user_id = ?5",
            params![attribute_name, attribute_value, last_update, attr_id, user_id],
        )
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de mettre à jour l'attribut pour sync.".to_string() } else { "Unable to update attribute for sync.".to_string() }))?;

    Ok(update_result > 0)
}

/// Fetches all character attributes for a series by series ID.
/// * `conn` - Database connection
/// * `user_id` - The unique identifier of the user
/// * `series_id` - The unique identifier of the series
/// * `lang` - The language for error messages ("fr" or "en")
/// Returns an array of full attribute table results.
pub fn fetch_series_character_attributes_by_series_id(conn: &Connection, user_id: &str, series_id: &str, lang: Lang) -> AppResult<Vec<SeriesCharacterAttributesTableResult>> {
    let mut statement = conn
        .prepare("SELECT sca.attr_id, sca.character_id, sca.user_id, sca.attribute_name, sca.attribute_value, sca.last_update FROM series_characters_attributes sca INNER JOIN series_characters sc ON sca.character_id = sc.character_id WHERE sc.series_id = ?1 AND sc.user_id = ?2")
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les attributs par série.".to_string() } else { "Unable to retrieve attributes by series.".to_string() }))?;

    let attributes = statement
        .query_map(params![series_id, user_id], |query_row| {
            Ok(SeriesCharacterAttributesTableResult {
                attr_id: query_row.get(0)?, character_id: query_row.get(1)?,
                user_id: query_row.get(2)?, attribute_name: query_row.get(3)?,
                attribute_value: query_row.get(4)?, last_update: query_row.get(5)?,
            })
        })
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les attributs par série.".to_string() } else { "Unable to retrieve attributes by series.".to_string() }))?
        .collect::<Result<Vec<_>, _>>()
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les attributs par série.".to_string() } else { "Unable to retrieve attributes by series.".to_string() }))?;

    Ok(attributes)
}

/// Checks if a series character exists (alias for is_character_exist).
/// * `conn` - Database connection
/// * `user_id` - The unique identifier of the user
/// * `character_id` - The unique identifier of the character
/// * `lang` - The language for error messages ("fr" or "en")
/// Returns true if the character exists, false otherwise.
pub fn series_character_exists(conn: &Connection, user_id: &str, character_id: &str, lang: Lang) -> AppResult<bool> {
    is_character_exist(conn, user_id, character_id, lang)
}

/// Checks if a series character attribute exists (alias for is_attribute_exist).
/// * `conn` - Database connection
/// * `user_id` - The unique identifier of the user
/// * `attr_id` - The unique identifier of the attribute
/// * `lang` - The language for error messages ("fr" or "en")
/// Returns true if the attribute exists, false otherwise.
pub fn series_character_attribute_exists(conn: &Connection, user_id: &str, attr_id: &str, lang: Lang) -> AppResult<bool> {
    is_attribute_exist(conn, user_id, attr_id, lang)
}
