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

export interface CharacterResult extends Record<string, SQLiteValue> {
    character_id: string;
    first_name: string;
    last_name: string;
    title: string;
    category: string;
    image: string;
    role: string;
    biography: string;
    history: string;
    char_meta: string;
}

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

export interface CompleteCharacterResult extends Record<string, SQLiteValue> {
    character_id: string;
    first_name: string;
    last_name: string;
    category: string;
    title: string;
    role: string;
    biography: string;
    history: string;
    char_meta: string;
    attribute_name: string;
    attribute_value: string;
    attr_meta: string;
}

export default class CharacterRepo {
    public static fetchCharacters(userId: string, bookId: string, lang: 'fr' | 'en' = 'fr'): CharacterResult[] {
        let result: CharacterResult[];
        try {
            const db: Database = System.getDb();
            result = db.all('SELECT character_id,`first_name`,`last_name`,`title`,`category`,`image`,`role`,`biography`,`history`,`char_meta` FROM `book_characters` WHERE `book_id`=? AND user_id=?', [bookId, userId]) as CharacterResult[];
        } catch (e: unknown) {
            if (e instanceof Error) {
                console.error(`DB Error: ${e.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.");
            }
        }
        return result;
    }

    public static addNewCharacter(userId: string, characterId: string, encryptedName: string, encryptedLastName: string, encryptedTitle: string, encryptedCategory: string, encryptedImage: string, encryptedRole: string, encryptedBiography: string, encryptedHistory: string, bookId: string, meta: string, lang: 'fr' | 'en' = 'fr'): string {
        let result: RunResult;
        try {
            const db: Database = System.getDb();
            result = db.run('INSERT INTO `book_characters` (character_id, book_id, user_id, first_name, last_name, category, title, image, role, biography, history, char_meta) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)', [characterId, bookId, userId, encryptedName, encryptedLastName, encryptedCategory, encryptedTitle, encryptedImage, encryptedRole, encryptedBiography, encryptedHistory, meta]);
        } catch (e: unknown) {
            if (e instanceof Error) {
                console.error(`DB Error: ${e.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 (result.changes > 0) {
            return characterId;
        } else {
            throw new Error(lang === 'fr' ? `Une erreur s'est produite lors de l'ajout du personnage.` : `Error adding character.`);
        }
    }

    static insertAttribute(attributeId: string, characterId: string, userId: string, type: string, name: string, meta: string, lang: 'fr' | 'en' = 'fr'): string {
        let result: RunResult;
        try {
            const db: Database = System.getDb();
            result = db.run('INSERT INTO `book_characters_attributes` (attr_id, character_id, user_id, attribute_name, attribute_value,attr_meta) VALUES (?,?,?,?,?,?)', [attributeId, characterId, userId, type, name, meta]);
        } catch (e: unknown) {
            if (e instanceof Error) {
                console.error(`DB Error: ${e.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 (result.changes > 0) {
            return attributeId;
        } else {
            throw new Error(lang === 'fr' ? `Une erreur s'est produite lors de l'ajout de l'attribut.` : `Error adding attribute.`);
        }
    }

    static updateCharacter(userId: string, id: number | null, encryptedName: string, encryptedLastName: string, encryptedTitle: string, encryptedCategory: string, encryptedImage: string, encryptedRole: string, encryptedBiography: string, encryptedHistory: string, meta: string, lang: 'fr' | 'en' = 'fr'): boolean {
        try {
            const db: Database = System.getDb();
            const result: RunResult = db.run('UPDATE `book_characters` SET `first_name`=?,`last_name`=?,`title`=?,`category`=?,`image`=?,`role`=?,`biography`=?,`history`=?, char_meta=? WHERE `character_id`=? AND `user_id`=?', [encryptedName, encryptedLastName, encryptedTitle, encryptedCategory, encryptedImage, encryptedRole, encryptedBiography, encryptedHistory, meta, id, userId]);
            return result.changes > 0;
        } catch (e: unknown) {
            if (e instanceof Error) {
                console.error(`DB Error: ${e.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.");
            }
        }
    }

    static deleteAttribute(userId: string, attributeId: string, lang: 'fr' | 'en' = 'fr'): boolean {
        try {
            const db: Database = System.getDb();
            const result: RunResult = db.run('DELETE FROM `book_characters_attributes` WHERE `attr_id`=? AND `user_id`=?', [attributeId, userId]);
            return result.changes > 0;
        } catch (e: unknown) {
            if (e instanceof Error) {
                console.error(`DB Error: ${e.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.");
            }
        }
    }

    static fetchAttributes(characterId: string, userId: string, lang: 'fr' | 'en' = 'fr'): AttributeResult[] {
        let result: AttributeResult[];
        try {
            const db: Database = System.getDb();
            result = db.all('SELECT attr_id, attribute_name, attribute_value, attr_meta FROM `book_characters_attributes` WHERE `character_id`=? AND `user_id`=?', [characterId, userId]) as AttributeResult[];
        } catch (e: unknown) {
            if (e instanceof Error) {
                console.error(`DB Error: ${e.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.");
            }
        }
        return result;
    }

    static fetchCompleteCharacters(userId: string, bookId: string, tags: string[], lang: 'fr' | 'en' = 'fr'): CompleteCharacterResult[] {
        let result: CompleteCharacterResult[];
        try {
            const db: Database = System.getDb();
            let query: string = 'SELECT charac.`character_id`,`first_name`,`last_name`,`category`,`title`,`role`,`biography`,`history`,`char_meta`,`attribute_name`,`attribute_value`,attr_meta 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 values: any[] = [userId, bookId];
            if (tags && tags.length > 0) {
                const placeholders: string = tags.map((): string => '?').join(',');
                query += ` AND charac.character_id IN (${placeholders})`;
                values.push(...tags);
            }
            result = db.all(query, values) as CompleteCharacterResult[];
        } catch (e: unknown) {
            if (e instanceof Error) {
                console.error(`DB Error: ${e.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.");
            }
        }
        if (result.length === 0) {
            throw new Error(lang === 'fr' ? `Aucun personnage complet trouvé.` : `No complete characters found.`);
        }
        return result;
    }
}
