import { Database, QueryResult, RunResult, SQLiteValue } from 'node-sqlite3-wasm';
import System from "../System.js";

export interface BookCharactersTable extends Record<string, SQLiteValue> {
    character_id: string;
    book_id: string;
    user_id: string;
    first_name: string;
    last_name: string | null;
    nickname: string | null;
    age: string | null;
    gender: string | null;
    species: string | null;
    nationality: string | null;
    status: string | null;
    category: string;
    title: string | null;
    image: string | null;
    role: string | null;
    biography: string | null;
    history: string | null;
    speech_pattern: string | null;
    catchphrase: string | null;
    residence: string | null;
    notes: string | null;
    color: string | null;
    last_update: number;
}

export interface SyncedCharacterResult extends Record<string, SQLiteValue> {
    character_id: string;
    book_id: string;
    first_name: string;
    last_update: number;
}

export interface SyncedCharacterAttributeResult extends Record<string, SQLiteValue> {
    attr_id: string;
    character_id: string;
    attribute_name: string;
    last_update: number;
}

export interface BookCharactersAttributesTable extends Record<string, SQLiteValue> {
    attr_id: string;
    character_id: string;
    user_id: string;
    attribute_name: string;
    attribute_value: string;
    last_update: number;
}

export interface CharacterResult extends Record<string, SQLiteValue> {
    character_id: string;
    first_name: string;
    last_name: string;
    nickname: string | null;
    age: string | null;
    gender: string | null;
    species: string | null;
    nationality: string | null;
    status: string | null;
    title: string;
    category: string;
    image: string;
    role: string;
    biography: string;
    history: string;
    speech_pattern: string | null;
    catchphrase: string | null;
    residence: string | null;
    notes: string | null;
    color: string | null;
    series_character_id: string | null;
}

export interface AttributeResult extends Record<string, SQLiteValue> {
    attr_id: string;
    attribute_name: string;
    attribute_value: string;
}

export interface CompleteCharacterResult extends Record<string, SQLiteValue> {
    character_id: string;
    first_name: string;
    last_name: string;
    nickname: string | null;
    age: string | null;
    gender: string | null;
    species: string | null;
    nationality: string | null;
    status: string | null;
    category: string;
    title: string;
    role: string;
    biography: string;
    history: string;
    speech_pattern: string | null;
    catchphrase: string | null;
    residence: string | null;
    notes: string | null;
    color: string | null;
    attribute_name: string;
    attribute_value: string;
}

export default class CharacterRepo {
    /**
     * Fetches all characters for a specific book and user.
     * @param userId - The unique identifier of the user
     * @param bookId - The unique identifier of the book
     * @param lang - The language for error messages ('fr' or 'en')
     * @returns An array of character results
     */
    public static fetchCharacters(userId: string, bookId: string, lang: 'fr' | 'en' = 'fr'): CharacterResult[] {
        try {
            const db: Database = System.getDb();
            const query: string = '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, series_character_id FROM book_characters WHERE book_id=? AND user_id=?';
            const params: SQLiteValue[] = [bookId, userId];
            const characters: CharacterResult[] = db.all(query, params) as CharacterResult[];
            return characters;
        } catch (error: unknown) {
            if (error instanceof Error) {
                console.error(`DB Error: ${error.message}`);
                throw new Error(lang === 'fr' ? `Impossible de récupérer les personnages.` : `Unable to retrieve characters.`);
            } else {
                console.error("An unknown error occurred.");
                throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
            }
        }
    }

    /**
     * Adds a new character to the database.
     * @param userId - The unique identifier of the user
     * @param characterId - The unique identifier for the new character
     * @param characterData - Object containing all encrypted character fields
     * @param bookId - The unique identifier of the book
     * @param lang - The language for error messages ('fr' or 'en')
     * @returns The character ID if successful
     */
    public static addNewCharacter(userId: string, characterId: string, characterData: {
        firstName: string;
        lastName: string | null;
        nickname: string | null;
        age: string | null;
        gender: string | null;
        species: string | null;
        nationality: string | null;
        status: string | null;
        title: string | null;
        category: string | null;
        image: string | null;
        role: string | null;
        biography: string | null;
        history: string | null;
        speechPattern: string | null;
        catchphrase: string | null;
        residence: string | null;
        notes: string | null;
        color: string | null;
    }, bookId: string, lang: 'fr' | 'en' = 'fr', seriesCharacterId: string | null = null): string {
        let insertResult: RunResult;
        try {
            const db: Database = System.getDb();
            const query: string = seriesCharacterId
                ? 'INSERT INTO book_characters (character_id, book_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, series_character_id, last_update) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)'
                : 'INSERT INTO book_characters (character_id, book_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 (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)';
            const params: SQLiteValue[] = seriesCharacterId
                ? [characterId, bookId, userId, characterData.firstName, characterData.lastName, characterData.nickname, characterData.age, characterData.gender, characterData.species, characterData.nationality, characterData.status, characterData.category, characterData.title, characterData.image, characterData.role, characterData.biography, characterData.history, characterData.speechPattern, characterData.catchphrase, characterData.residence, characterData.notes, characterData.color, seriesCharacterId, System.timeStampInSeconds()]
                : [characterId, bookId, userId, characterData.firstName, characterData.lastName, characterData.nickname, characterData.age, characterData.gender, characterData.species, characterData.nationality, characterData.status, characterData.category, characterData.title, characterData.image, characterData.role, characterData.biography, characterData.history, characterData.speechPattern, characterData.catchphrase, characterData.residence, characterData.notes, characterData.color, System.timeStampInSeconds()];
            insertResult = db.run(query, params);
        } catch (error: unknown) {
            if (error instanceof Error) {
                console.error(`DB Error: ${error.message}`);
                throw new Error(lang === 'fr' ? `Impossible d'ajouter le personnage.` : `Unable to add character.`);
            } else {
                console.error("An unknown error occurred.");
                throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
            }
        }
        if (!insertResult || insertResult.changes === 0) {
            throw new Error(lang === 'fr' ? `Une erreur s'est produite lors de l'ajout du personnage.` : `Error adding character.`);
        }
        return characterId;
    }

    /**
     * Inserts a new attribute for a character.
     * @param attributeId - The unique identifier for the new attribute
     * @param characterId - The unique identifier of the character
     * @param userId - The unique identifier of the user
     * @param type - The attribute name/type
     * @param name - The attribute value
     * @param lang - The language for error messages ('fr' or 'en')
     * @returns The attribute ID if successful
     */
    static insertAttribute(attributeId: string, characterId: string, userId: string, type: string, name: string, lang: 'fr' | 'en' = 'fr'): string {
        let insertResult: RunResult;
        try {
            const db: Database = System.getDb();
            const query: string = 'INSERT INTO book_characters_attributes (attr_id, character_id, user_id, attribute_name, attribute_value, last_update) VALUES (?,?,?,?,?,?)';
            const params: SQLiteValue[] = [attributeId, characterId, userId, type, name, System.timeStampInSeconds()];
            insertResult = db.run(query, params);
        } catch (error: unknown) {
            if (error instanceof Error) {
                console.error(`DB Error: ${error.message}`);
                throw new Error(lang === 'fr' ? `Impossible d'ajouter l'attribut.` : `Unable to add attribute.`);
            } else {
                console.error("An unknown error occurred.");
                throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
            }
        }
        if (!insertResult || insertResult.changes === 0) {
            throw new Error(lang === 'fr' ? `Une erreur s'est produite lors de l'ajout de l'attribut.` : `Error adding attribute.`);
        }
        return attributeId;
    }

    /**
     * Updates an existing character's information.
     * @param userId - The unique identifier of the user
     * @param id - The unique identifier of the character to update
     * @param characterData - Object containing all encrypted character fields
     * @param lastUpdate - The timestamp of the last update
     * @param lang - The language for error messages ('fr' or 'en')
     * @returns True if the update was successful, false otherwise
     */
    static updateCharacter(userId: string, id: string, characterData: {
        firstName: string;
        lastName: string | null;
        nickname: string | null;
        age: string | null;
        gender: string | null;
        species: string | null;
        nationality: string | null;
        status: string | null;
        title: string | null;
        category: string | null;
        image: string | null;
        role: string | null;
        biography: string | null;
        history: string | null;
        speechPattern: string | null;
        catchphrase: string | null;
        residence: string | null;
        notes: string | null;
        color: string | null;
    }, lastUpdate: number, lang: 'fr' | 'en' = 'fr', seriesCharacterId: string | null = null): boolean {
        try {
            const db: Database = System.getDb();
            const query: string = seriesCharacterId !== null
                ? 'UPDATE book_characters SET first_name=?, last_name=?, nickname=?, age=?, gender=?, species=?, nationality=?, status=?, title=?, category=?, image=?, role=?, biography=?, history=?, speech_pattern=?, catchphrase=?, residence=?, notes=?, color=?, series_character_id=?, last_update=? WHERE character_id=? AND user_id=?'
                : 'UPDATE book_characters SET first_name=?, last_name=?, nickname=?, age=?, gender=?, species=?, nationality=?, status=?, title=?, category=?, image=?, role=?, biography=?, history=?, speech_pattern=?, catchphrase=?, residence=?, notes=?, color=?, last_update=? WHERE character_id=? AND user_id=?';
            const params: SQLiteValue[] = seriesCharacterId !== null
                ? [characterData.firstName, characterData.lastName, characterData.nickname, characterData.age, characterData.gender, characterData.species, characterData.nationality, characterData.status, characterData.title, characterData.category, characterData.image, characterData.role, characterData.biography, characterData.history, characterData.speechPattern, characterData.catchphrase, characterData.residence, characterData.notes, characterData.color, seriesCharacterId, lastUpdate, id, userId]
                : [characterData.firstName, characterData.lastName, characterData.nickname, characterData.age, characterData.gender, characterData.species, characterData.nationality, characterData.status, characterData.title, characterData.category, characterData.image, characterData.role, characterData.biography, characterData.history, characterData.speechPattern, characterData.catchphrase, characterData.residence, characterData.notes, characterData.color, lastUpdate, id, userId];
            const updateResult: RunResult = db.run(query, params);
            return updateResult.changes > 0;
        } catch (error: unknown) {
            if (error instanceof Error) {
                console.error(`DB Error: ${error.message}`);
                throw new Error(lang === 'fr' ? `Impossible de mettre à jour le personnage.` : `Unable to update character.`);
            } else {
                console.error("An unknown error occurred.");
                throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
            }
        }
    }

    /**
     * Deletes a character and all its related data (attributes) from the database.
     * @param userId - The unique identifier of the user
     * @param characterId - The unique identifier of the character to delete
     * @param lang - The language for error messages ('fr' or 'en')
     * @returns True if the deletion was successful, false otherwise
     */
    static deleteCharacter(userId: string, characterId: string, lang: 'fr' | 'en' = 'fr'): boolean {
        try {
            const db: Database = System.getDb();
            const deleteAttributesQuery: string = 'DELETE FROM `book_characters_attributes` WHERE `character_id`=? AND `user_id`=?';
            db.run(deleteAttributesQuery, [characterId, userId]);
            const deleteCharacterQuery: string = 'DELETE FROM `book_characters` WHERE `character_id`=? AND `user_id`=?';
            const result: RunResult = db.run(deleteCharacterQuery, [characterId, userId]);
            return result.changes > 0;
        } catch (error: unknown) {
            if (error instanceof Error) {
                console.error(`DB Error: ${error.message}`);
                throw new Error(lang === 'fr' ? `Impossible de supprimer le personnage.` : `Unable to delete character.`);
            } else {
                console.error("An unknown error occurred.");
                throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
            }
        }
    }

    /**
     * Deletes a character attribute from the database.
     * @param userId - The unique identifier of the user
     * @param attributeId - The unique identifier of the attribute to delete
     * @param lang - The language for error messages ('fr' or 'en')
     * @returns True if the deletion was successful, false otherwise
     */
    static deleteAttribute(userId: string, attributeId: string, lang: 'fr' | 'en' = 'fr'): boolean {
        try {
            const db: Database = System.getDb();
            const query: string = 'DELETE FROM `book_characters_attributes` WHERE `attr_id`=? AND `user_id`=?';
            const params: SQLiteValue[] = [attributeId, userId];
            const deleteResult: RunResult = db.run(query, params);
            return deleteResult.changes > 0;
        } catch (error: unknown) {
            if (error instanceof Error) {
                console.error(`DB Error: ${error.message}`);
                throw new Error(lang === 'fr' ? `Impossible de supprimer l'attribut.` : `Unable to delete attribute.`);
            } else {
                console.error("An unknown error occurred.");
                throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
            }
        }
    }

    /**
     * Fetches all attributes for a specific character.
     * @param characterId - The unique identifier of the character
     * @param userId - The unique identifier of the user
     * @param lang - The language for error messages ('fr' or 'en')
     * @returns An array of attribute results
     */
    static fetchAttributes(characterId: string, userId: string, lang: 'fr' | 'en' = 'fr'): AttributeResult[] {
        try {
            const db: Database = System.getDb();
            const query: string = 'SELECT attr_id, attribute_name, attribute_value FROM book_characters_attributes WHERE character_id=? AND user_id=?';
            const params: SQLiteValue[] = [characterId, userId];
            const attributes: AttributeResult[] = db.all(query, params) as AttributeResult[];
            return attributes;
        } catch (error: unknown) {
            if (error instanceof Error) {
                console.error(`DB Error: ${error.message}`);
                throw new Error(lang === 'fr' ? `Impossible de récupérer les attributs.` : `Unable to retrieve attributes.`);
            } else {
                console.error("An unknown error occurred.");
                throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
            }
        }
    }

    /**
     * Fetches complete character information including attributes, optionally filtered by character IDs.
     * @param userId - The unique identifier of the user
     * @param bookId - The unique identifier of the book
     * @param tags - An optional array of character IDs to filter by
     * @param lang - The language for error messages ('fr' or 'en')
     * @returns An array of complete character results with attributes
     */
    static fetchCompleteCharacters(userId: string, bookId: string, tags: string[], lang: 'fr' | 'en' = 'fr'): CompleteCharacterResult[] {
        try {
            const db: Database = System.getDb();
            let query: string = 'SELECT charac.character_id, first_name, last_name, nickname, age, gender, species, nationality, status, category, title, role, biography, history, speech_pattern, catchphrase, residence, notes, color, attribute_name, attribute_value FROM book_characters AS charac LEFT JOIN book_characters_attributes AS attr ON charac.character_id=attr.character_id WHERE charac.user_id=? AND charac.book_id=?';
            let params: SQLiteValue[] = [userId, bookId];
            if (tags && tags.length > 0) {
                const placeholders: string = tags.map((): string => '?').join(',');
                query += ` AND charac.character_id IN (${placeholders})`;
                params.push(...tags);
            }
            const characters: CompleteCharacterResult[] = db.all(query, params) as CompleteCharacterResult[];
            if (characters.length === 0) {
                throw new Error(lang === 'fr' ? `Aucun personnage complet trouvé.` : `No complete characters found.`);
            }
            return characters;
        } catch (error: unknown) {
            if (error instanceof Error) {
                console.error(`DB Error: ${error.message}`);
                throw new Error(lang === 'fr' ? `Impossible de récupérer les personnages complets.` : `Unable to retrieve complete characters.`);
            } else {
                console.error("An unknown error occurred.");
                throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
            }
        }
    }

    /**
     * Updates an existing character attribute.
     * @param userId - The unique identifier of the user
     * @param characterAttributeId - The unique identifier of the attribute to update
     * @param attributeName - The new attribute name
     * @param attributeValue - The new attribute value
     * @param lastUpdate - The timestamp of the last update
     * @param lang - The language for error messages ('fr' or 'en')
     * @returns True if the update was successful, false otherwise
     */
    static updateCharacterAttribute(userId: string, characterAttributeId: string, attributeName: string, attributeValue: string, lastUpdate: number, lang: "fr" | "en"): boolean {
        try {
            const db: Database = System.getDb();
            const query: string = 'UPDATE book_characters_attributes SET attribute_name=?, attribute_value=?, last_update=? WHERE attr_id=? AND user_id=?';
            const params: SQLiteValue[] = [attributeName, attributeValue, lastUpdate, characterAttributeId, userId];
            const updateResult: RunResult = db.run(query, params);
            return updateResult.changes > 0;
        } catch (error: unknown) {
            if (error instanceof Error) {
                console.error(`DB Error: ${error.message}`);
                throw new Error(lang === 'fr' ? `Impossible de mettre à jour l'attribut du personnage.` : `Unable to update character attribute.`);
            } else {
                console.error("An unknown error occurred.");
                throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
            }
        }
    }

    /**
     * Checks if a character exists in the database.
     * @param userId - The unique identifier of the user
     * @param characterId - The unique identifier of the character to check
     * @param lang - The language for error messages ('fr' or 'en')
     * @returns True if the character exists, false otherwise
     */
    static isCharacterExist(userId: string, characterId: string, lang: "fr" | "en"): boolean {
        try {
            const db: Database = System.getDb();
            const query: string = 'SELECT 1 FROM `book_characters` WHERE `character_id`=? AND `user_id`=?';
            const params: SQLiteValue[] = [characterId, userId];
            const character: QueryResult | null = db.get(query, params) || null;
            return character !== null;
        } catch (error: unknown) {
            if (error instanceof Error) {
                console.error(`DB Error: ${error.message}`);
                throw new Error(lang === 'fr' ? `Impossible de vérifier l'existence du personnage.` : `Unable to check character existence.`);
            } else {
                console.error("An unknown error occurred.");
                throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
            }
        }
    }

    /**
     * Checks if a character attribute exists in the database.
     * @param userId - The unique identifier of the user
     * @param characterAttributeId - The unique identifier of the attribute to check
     * @param lang - The language for error messages ('fr' or 'en')
     * @returns True if the attribute exists, false otherwise
     */
    static isCharacterAttributeExist(userId: string, characterAttributeId: string, lang: "fr" | "en"): boolean {
        try {
            const db: Database = System.getDb();
            const query: string = 'SELECT 1 FROM `book_characters_attributes` WHERE `attr_id`=? AND `user_id`=?';
            const params: SQLiteValue[] = [characterAttributeId, userId];
            const attribute: QueryResult | null = db.get(query, params) || null;
            return attribute !== null;
        } catch (error: unknown) {
            if (error instanceof Error) {
                console.error(`DB Error: ${error.message}`);
                throw new Error(lang === 'fr' ? `Impossible de vérifier l'existence de l'attribut du personnage.` : `Unable to check character attribute existence.`);
            } else {
                console.error("An unknown error occurred.");
                throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
            }
        }
    }

    /**
     * Fetches all characters for a specific book asynchronously.
     * @param userId - The unique identifier of the user
     * @param bookId - The unique identifier of the book
     * @param lang - The language for error messages ('fr' or 'en')
     * @returns A promise resolving to an array of book characters
     */
    static async fetchBookCharacters(userId: string, bookId: string, lang: 'fr' | 'en'): Promise<BookCharactersTable[]> {
        try {
            const db: Database = System.getDb();
            const query: string = `SELECT character_id, book_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
                FROM book_characters WHERE user_id=? AND book_id=?`;
            const params: SQLiteValue[] = [userId, bookId];
            const characters: BookCharactersTable[] = db.all(query, params) as BookCharactersTable[];
            return characters;
        } catch (error: unknown) {
            if (error instanceof Error) {
                console.error(`DB Error: ${error.message}`);
                throw new Error(lang === 'fr' ? `Impossible de récupérer les personnages.` : `Unable to retrieve characters.`);
            } else {
                throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
            }
        }
    }

    /**
     * Fetches all attributes for a specific character asynchronously.
     * @param userId - The unique identifier of the user
     * @param characterId - The unique identifier of the character
     * @param lang - The language for error messages ('fr' or 'en')
     * @returns A promise resolving to an array of character attributes
     */
    static async fetchBookCharactersAttributes(userId: string, characterId: string, lang: 'fr' | 'en'): Promise<BookCharactersAttributesTable[]> {
        try {
            const db: Database = System.getDb();
            const query: string = 'SELECT attr_id, character_id, user_id, attribute_name, attribute_value, last_update FROM book_characters_attributes WHERE user_id=? AND character_id=?';
            const params: SQLiteValue[] = [userId, characterId];
            const attributes: BookCharactersAttributesTable[] = db.all(query, params) as BookCharactersAttributesTable[];
            return attributes;
        } catch (error: unknown) {
            if (error instanceof Error) {
                console.error(`DB Error: ${error.message}`);
                throw new Error(lang === 'fr' ? `Impossible de récupérer les attributs des personnages.` : `Unable to retrieve character attributes.`);
            } else {
                throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
            }
        }
    }

    /**
     * Fetches all synced characters for a user.
     * @param userId - The unique identifier of the user
     * @param lang - The language for error messages ('fr' or 'en')
     * @returns An array of synced character results
     */
    static fetchSyncedCharacters(userId: string, lang: 'fr' | 'en'): SyncedCharacterResult[] {
        try {
            const db: Database = System.getDb();
            const query: string = 'SELECT character_id, book_id, first_name, last_update FROM book_characters WHERE user_id = ?';
            const params: SQLiteValue[] = [userId];
            const syncedCharacters: SyncedCharacterResult[] = db.all(query, params) as SyncedCharacterResult[];
            return syncedCharacters;
        } catch (error: unknown) {
            if (error instanceof Error) {
                console.error(`DB Error: ${error.message}`);
                throw new Error(lang === 'fr' ? `Impossible de récupérer les personnages synchronisés.` : `Unable to retrieve synced characters.`);
            } else {
                console.error("An unknown error occurred.");
                throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
            }
        }
    }

    /**
     * Fetches all synced character attributes for a user.
     * @param userId - The unique identifier of the user
     * @param lang - The language for error messages ('fr' or 'en')
     * @returns An array of synced character attribute results
     */
    static fetchSyncedCharacterAttributes(userId: string, lang: 'fr' | 'en'): SyncedCharacterAttributeResult[] {
        try {
            const db: Database = System.getDb();
            const query: string = 'SELECT attr_id, character_id, attribute_name, last_update FROM book_characters_attributes WHERE user_id = ?';
            const params: SQLiteValue[] = [userId];
            const syncedAttributes: SyncedCharacterAttributeResult[] = db.all(query, params) as SyncedCharacterAttributeResult[];
            return syncedAttributes;
        } catch (error: unknown) {
            if (error instanceof Error) {
                console.error(`DB Error: ${error.message}`);
                throw new Error(lang === 'fr' ? `Impossible de récupérer les attributs des personnages synchronisés.` : `Unable to retrieve synced character attributes.`);
            } else {
                console.error("An unknown error occurred.");
                throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
            }
        }
    }

    /**
     * Inserts a synced character into the database.
     * @param characterId - The unique identifier of the character
     * @param bookId - The unique identifier of the book
     * @param userId - The unique identifier of the user
     * @param characterData - Object containing all character fields
     * @param lastUpdate - The timestamp of the last update
     * @param lang - The language for error messages ('fr' or 'en')
     * @returns True if the insertion was successful, false otherwise
     */
    static insertSyncCharacter(characterId: string, bookId: string, userId: string, characterData: {
        firstName: string;
        lastName: string | null;
        nickname: string | null;
        age: string | null;
        gender: string | null;
        species: string | null;
        nationality: string | null;
        status: string | null;
        category: string;
        title: string | null;
        image: string | null;
        role: string | null;
        biography: string | null;
        history: string | null;
        speechPattern: string | null;
        catchphrase: string | null;
        residence: string | null;
        notes: string | null;
        color: string | null;
    }, lastUpdate: number, lang: 'fr' | 'en'): boolean {
        try {
            const db: Database = System.getDb();
            const query: string = `INSERT INTO book_characters (
                character_id, book_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 (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`;
            const params: SQLiteValue[] = [
                characterId, bookId, userId,
                characterData.firstName, characterData.lastName, characterData.nickname,
                characterData.age, characterData.gender, characterData.species,
                characterData.nationality, characterData.status, characterData.category,
                characterData.title, characterData.image, characterData.role,
                characterData.biography, characterData.history, characterData.speechPattern,
                characterData.catchphrase, characterData.residence, characterData.notes,
                characterData.color, lastUpdate
            ];
            const insertResult: RunResult = db.run(query, params);
            return insertResult.changes > 0;
        } catch (error: unknown) {
            if (error instanceof Error) {
                console.error(`DB Error: ${error.message}`);
                throw new Error(lang === 'fr' ? `Impossible d'insérer le personnage.` : `Unable to insert character.`);
            } else {
                throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
            }
        }
    }

    /**
     * Inserts a synced character attribute into the database.
     * @param attrId - The unique identifier of the attribute
     * @param characterId - The unique identifier of the character
     * @param userId - The unique identifier of the user
     * @param attributeName - The name of the attribute
     * @param attributeValue - The value of the attribute
     * @param lastUpdate - The timestamp of the last update
     * @param lang - The language for error messages ('fr' or 'en')
     * @returns True if the insertion was successful, false otherwise
     */
    static insertSyncCharacterAttribute(attrId: string, characterId: string, userId: string, attributeName: string, attributeValue: string, lastUpdate: number, lang: 'fr' | 'en'): boolean {
        try {
            const db: Database = System.getDb();
            const query: string = `INSERT INTO book_characters_attributes (attr_id, character_id, user_id, attribute_name, attribute_value, last_update)
                 VALUES (?, ?, ?, ?, ?, ?)`;
            const params: SQLiteValue[] = [attrId, characterId, userId, attributeName, attributeValue, lastUpdate];
            const insertResult: RunResult = db.run(query, params);
            return insertResult.changes > 0;
        } catch (error: unknown) {
            if (error instanceof Error) {
                console.error(`DB Error: ${error.message}`);
                throw new Error(lang === 'fr' ? `Impossible d'insérer l'attribut du personnage.` : `Unable to insert character attribute.`);
            } else {
                throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
            }
        }
    }

    /**
     * Fetches a complete character by its ID.
     * @param id - The unique identifier of the character
     * @param lang - The language for error messages ('fr' or 'en')
     * @returns A promise resolving to an array of book characters (typically one)
     */
    static async fetchCompleteCharacterById(id: string, lang: "fr" | "en"): Promise<BookCharactersTable[]> {
        try {
            const db: Database = System.getDb();
            const query: string = `SELECT character_id, book_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
                 FROM book_characters
                 WHERE character_id = ?`;
            const params: SQLiteValue[] = [id];
            const character: BookCharactersTable[] = db.all(query, params) as BookCharactersTable[];
            return character;
        } catch (error: unknown) {
            if (error instanceof Error) {
                throw new Error(lang === 'fr' ? `Impossible de récupérer le personnage complet.` : `Unable to retrieve complete character.`);
            } else {
                throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
            }
        }
    }

    /**
     * Fetches a complete character attribute by its ID.
     * @param id - The unique identifier of the attribute
     * @param lang - The language for error messages ('fr' or 'en')
     * @returns A promise resolving to an array of character attributes (typically one)
     */
    static async fetchCompleteCharacterAttributeById(id: string, lang: "fr" | "en"): Promise<BookCharactersAttributesTable[]> {
        try {
            const db: Database = System.getDb();
            const query: string = `SELECT attr_id, character_id, user_id, attribute_name, attribute_value, last_update
                 FROM book_characters_attributes
                 WHERE attr_id = ?`;
            const params: SQLiteValue[] = [id];
            const attribute: BookCharactersAttributesTable[] = db.all(query, params) as BookCharactersAttributesTable[];
            return attribute;
        } catch (error: unknown) {
            if (error instanceof Error) {
                throw new Error(lang === 'fr' ? `Impossible de récupérer l'attribut de personnage complet.` : `Unable to retrieve complete character attribute.`);
            } else {
                throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
            }
        }
    }
}
