import {Database, QueryResult, RunResult, SQLiteValue} from 'node-sqlite3-wasm'; import System from "../System.js"; export interface LocationQueryResult extends Record { loc_id: string; loc_name: string; element_id: string; element_name: string; element_description: string; sub_element_id: string; sub_elem_name: string; sub_elem_description: string; } export interface LocationElementQueryResult extends Record { sub_element_id: string; sub_elem_name: string; sub_elem_description: string; element_id: string; element_name: string; element_description: string; } export interface LocationByTagResult extends Record { element_name: string; element_description: string; sub_elem_name: string; sub_elem_description: string; } export interface BookLocationTable extends Record { loc_id: string; book_id: string; user_id: string; loc_name: string; loc_original_name: string; last_update: number; } export interface LocationElementTable extends Record { element_id: string; location: string; user_id: string; element_name: string; original_name: string; element_description: string | null; last_update: number; } export interface LocationSubElementTable extends Record { sub_element_id: string; element_id: string; user_id: string; sub_elem_name: string; original_name: string; sub_elem_description: string | null; last_update: number; } export interface SyncedLocationResult extends Record { loc_id: string; book_id: string; loc_name: string; last_update: number; } export interface SyncedLocationElementResult extends Record { element_id: string; location: string; element_name: string; last_update: number; } export interface SyncedLocationSubElementResult extends Record { sub_element_id: string; element_id: string; sub_elem_name: string; last_update: number; } export default class LocationRepo { static getLocation(userId: string, bookId: string, lang: 'fr' | 'en' = 'fr'): LocationQueryResult[] { let result: LocationQueryResult[]; try { const db: Database = System.getDb(); const query = '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 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=? AND location.book_id=?'; result = db.all(query, [userId, bookId]) as LocationQueryResult[]; } catch (e: unknown) { if (e instanceof Error) { console.error(`DB Error: ${e.message}`); throw new Error(lang === 'fr' ? `Impossible de récupérer les emplacements.` : `Unable to retrieve locations.`); } 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 insertLocation(userId: string, locationId: string, bookId: string, encryptedName: string, originalName: string, lang: 'fr' | 'en' = 'fr'): string { let result: RunResult; try { const db: Database = System.getDb(); result = db.run('INSERT INTO book_location (loc_id, book_id, user_id, loc_name, loc_original_name, last_update) VALUES (?, ?, ?, ?, ?, ?)', [locationId, bookId, userId, encryptedName, originalName, System.timeStampInSeconds()]); } catch (e: unknown) { if (e instanceof Error) { console.error(`DB Error: ${e.message}`); throw new Error(lang === 'fr' ? `Impossible d'ajouter la section d'emplacement.` : `Unable to add location section.`); } else { console.error("An unknown error occurred."); throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); } } if (!result || result.changes === 0) { throw new Error(lang === 'fr' ? `Une erreur s'est produite lors de l'ajout de la section d'emplacement.` : `Error adding location section.`); } return locationId; } static insertLocationElement(userId: string, elementId: string, locationId: string, encryptedName: string, originalName: string, lang: 'fr' | 'en' = 'fr'): string { let result: RunResult; try { const db: Database = System.getDb(); result = db.run('INSERT INTO location_element (element_id, location, user_id, element_name, original_name, element_description, last_update) VALUES (?,?,?,?,?,?,?)', [elementId, locationId, userId, encryptedName, originalName, '', System.timeStampInSeconds()]); } catch (e: unknown) { if (e instanceof Error) { console.error(`DB Error: ${e.message}`); throw new Error(lang === 'fr' ? `Impossible d'ajouter l'élément d'emplacement.` : `Unable to add location element.`); } else { console.error("An unknown error occurred."); throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); } } if (!result || result.changes === 0) { throw new Error(lang === 'fr' ? `Une erreur s'est produite lors de l'ajout de l'élément d'emplacement.` : `Error adding location element.`); } return elementId; } static insertLocationSubElement(userId: string, subElementId: string, elementId: string, encryptedName: string, originalName: string, lang: 'fr' | 'en' = 'fr'): string { let result: RunResult; try { const db: Database = System.getDb(); result = db.run('INSERT INTO location_sub_element (sub_element_id, element_id, user_id, sub_elem_name, original_name, sub_elem_description, last_update) VALUES (?,?,?,?,?,?,?)', [subElementId, elementId, userId, encryptedName, originalName, '', System.timeStampInSeconds()]); } catch (e: unknown) { if (e instanceof Error) { console.error(`DB Error: ${e.message}`); throw new Error(lang === 'fr' ? `Impossible d'ajouter le sous-élément d'emplacement.` : `Unable to add location sub-element.`); } else { console.error("An unknown error occurred."); throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); } } if (!result || result.changes === 0) { throw new Error(lang === 'fr' ? `Une erreur s'est produite lors de l'ajout du sous-élément d'emplacement.` : `Error adding location sub-element.`); } return subElementId; } static updateLocationSubElement(userId: string, id: string, encryptedName: string, originalName: string, encryptDescription: string, lastUpdate: number, lang: 'fr' | 'en' = 'fr'): boolean { try { const db: Database = System.getDb(); const result: RunResult = db.run('UPDATE location_sub_element SET sub_elem_name=?, original_name=?, sub_elem_description=?, last_update=? WHERE sub_element_id=? AND user_id=?', [encryptedName, originalName, encryptDescription, lastUpdate, 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 sous-élément d'emplacement.` : `Unable to update location sub-element.`); } else { console.error("An unknown error occurred."); throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); } } } static updateLocationElement(userId: string, id: string, encryptedName: string, originalName: string, encryptedDescription: string, lastUpdate: number, lang: 'fr' | 'en' = 'fr'): boolean { try { const db: Database = System.getDb(); const result: RunResult = db.run('UPDATE location_element SET element_name=?, original_name=?, element_description=?, last_update=? WHERE element_id=? AND user_id=?', [encryptedName, originalName, encryptedDescription, lastUpdate, 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 l'élément d'emplacement.` : `Unable to update location element.`); } else { console.error("An unknown error occurred."); throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); } } } static updateLocationSection(userId: string, id: string, encryptedName: string, originalName: string, lastUpdate: number, lang: 'fr' | 'en' = 'fr'): boolean { try { const db: Database = System.getDb(); const result: RunResult = db.run('UPDATE book_location SET loc_name=?, loc_original_name=?, last_update=? WHERE loc_id=? AND user_id=?', [encryptedName, originalName, lastUpdate, 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 la section d'emplacement.` : `Unable to update location section.`); } else { console.error("An unknown error occurred."); throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); } } } static deleteLocationSection(userId: string, locationId: string, lang: 'fr' | 'en' = 'fr'): boolean { try { const db: Database = System.getDb(); const result: RunResult = db.run('DELETE FROM book_location WHERE loc_id=? AND user_id=?', [locationId, 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 la section d'emplacement.` : `Unable to delete location section.`); } else { console.error("An unknown error occurred."); throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); } } } static deleteLocationElement(userId: string, elementId: string, lang: 'fr' | 'en' = 'fr'): boolean { try { const db: Database = System.getDb(); const result: RunResult = db.run('DELETE FROM location_element WHERE element_id=? AND user_id=?', [elementId, 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'élément d'emplacement.` : `Unable to delete location element.`); } else { console.error("An unknown error occurred."); throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); } } } static deleteLocationSubElement(userId: string, subElementId: string, lang: 'fr' | 'en' = 'fr'): boolean { try { const db: Database = System.getDb(); const result: RunResult = db.run('DELETE FROM location_sub_element WHERE sub_element_id=? AND user_id=?', [subElementId, 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 le sous-élément d'emplacement.` : `Unable to delete location sub-element.`); } else { console.error("An unknown error occurred."); throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); } } } static fetchLocationTags(userId: string, bookId: string, lang: 'fr' | 'en' = 'fr'): LocationElementQueryResult[] { let result: LocationElementQueryResult[]; try { const db: Database = System.getDb(); const query = '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 = ? AND lo.user_id = ?'; result = db.all(query, [bookId, userId]) as LocationElementQueryResult[]; } catch (e: unknown) { if (e instanceof Error) { console.error(`DB Error: ${e.message}`); throw new Error(lang === 'fr' ? `Impossible de récupérer les tags d'emplacement.` : `Unable to retrieve location tags.`); } 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 fetchLocationsByTags(userId: string, locations: string[], lang: 'fr' | 'en' = 'fr'): LocationByTagResult[] { if (locations.length === 0) { throw new Error(lang === 'fr' ? `Aucun tag fourni.` : `No tags provided.`); } let result: LocationByTagResult[]; try { const db: Database = System.getDb(); const locationIds: string = locations.map((): string => '?').join(','); const query: string = ` SELECT el.element_name, el.element_description, se.sub_elem_name, se.sub_elem_description FROM location_element AS el LEFT JOIN location_sub_element AS se ON el.element_id = se.element_id WHERE el.user_id = ? AND (el.element_id IN (${locationIds}) OR se.sub_element_id IN (${locationIds})) `; const values: any[] = [userId, ...locations, ...locations]; result = db.all(query, values) as LocationByTagResult[]; } catch (e: unknown) { if (e instanceof Error) { console.error(`DB Error: ${e.message}`); throw new Error(lang === 'fr' ? `Impossible de récupérer les emplacements par tags.` : `Unable to retrieve locations by tags.`); } 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 emplacement trouvé avec ces tags.` : `No locations found with these tags.`); } return result; } static isLocationExist(userId: string, locId: string,lang: "fr" | "en"): boolean { try { const db: Database = System.getDb(); const result: QueryResult | null = db.get('SELECT 1 FROM `book_location` WHERE `loc_id`=? AND `user_id`=?', [locId, userId]) || null; return result !== null; } catch (e: unknown) { if (e instanceof Error) { console.error(`DB Error: ${e.message}`); throw new Error(lang === 'fr' ? `Impossible de vérifier l'existence de l'emplacement.` : `Unable to check location existence.`); } else { console.error("An unknown error occurred."); throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); } } } static isLocationElementExist(userId: string, elementId: string,lang: "fr" | "en"): boolean { try { const db: Database = System.getDb(); const result: QueryResult | null = db.get('SELECT 1 FROM `location_element` WHERE `element_id`=? AND `user_id`=?', [elementId, userId]) || null; return result !== null; } catch (e: unknown) { if (e instanceof Error) { console.error(`DB Error: ${e.message}`); throw new Error(lang === 'fr' ? `Impossible de vérifier l'existence de l'élément d'emplacement.` : `Unable to check location element existence.`); } else { console.error("An unknown error occurred."); throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); } } } static isLocationSubElementExist(userId: string, subElementId: string,lang: "fr" | "en"): boolean { try { const db: Database = System.getDb(); const result: QueryResult | null = db.get('SELECT 1 FROM `location_sub_element` WHERE `sub_element_id`=? AND `user_id`=?', [subElementId, userId]) || null; return result !== null; } catch (e: unknown) { if (e instanceof Error) { console.error(`DB Error: ${e.message}`); throw new Error(lang === 'fr' ? `Impossible de vérifier l'existence du sous-élément d'emplacement.` : `Unable to check location sub-element existence.`); } else { console.error("An unknown error occurred."); throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); } } } static async fetchBookLocations(userId: string, bookId: string, lang: 'fr' | 'en'): Promise { try { const db: Database = System.getDb(); return db.all('SELECT loc_id, book_id, user_id, loc_name, loc_original_name, last_update FROM book_location WHERE user_id=? AND book_id=?', [userId, bookId]) as BookLocationTable[]; } catch (e: unknown) { if (e instanceof Error) { console.error(`DB Error: ${e.message}`); throw new Error(lang === 'fr' ? `Impossible de récupérer les lieux.` : `Unable to retrieve locations.`); } else { throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); } } } static async fetchLocationElements(userId: string,locationId:string, lang: 'fr' | 'en'): Promise { try { const db: Database = System.getDb(); return db.all('SELECT element_id, location, user_id, element_name, original_name, element_description, last_update FROM location_element WHERE user_id=? AND location=?', [userId, locationId]) as LocationElementTable[]; } catch (e: unknown) { if (e instanceof Error) { console.error(`DB Error: ${e.message}`); throw new Error(lang === 'fr' ? `Impossible de récupérer les éléments de lieu.` : `Unable to retrieve location elements.`); } else { throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); } } } static async fetchLocationSubElements(userId: string,elementId:string, lang: 'fr' | 'en'): Promise { try { const db: Database = System.getDb(); return db.all('SELECT sub_element_id, element_id, user_id, sub_elem_name, original_name, sub_elem_description, last_update FROM location_sub_element WHERE user_id=? AND element_id=?', [userId, elementId]) as LocationSubElementTable[]; } catch (e: unknown) { if (e instanceof Error) { console.error(`DB Error: ${e.message}`); throw new Error(lang === 'fr' ? `Impossible de récupérer les sous-éléments de lieu.` : `Unable to retrieve location sub-elements.`); } else { throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); } } } static fetchSyncedLocations(userId: string, lang: 'fr' | 'en'): SyncedLocationResult[] { try { const db: Database = System.getDb(); return db.all('SELECT loc_id, book_id, loc_name, last_update FROM book_location WHERE user_id = ?', [userId]) as SyncedLocationResult[]; } catch (e: unknown) { if (e instanceof Error) { console.error(`DB Error: ${e.message}`); throw new Error(lang === 'fr' ? `Impossible de récupérer les lieux synchronisés.` : `Unable to retrieve synced locations.`); } else { console.error("An unknown error occurred."); throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); } } } static fetchSyncedLocationElements(userId: string, lang: 'fr' | 'en'): SyncedLocationElementResult[] { try { const db: Database = System.getDb(); return db.all('SELECT element_id, location, element_name, last_update FROM location_element WHERE user_id = ?', [userId]) as SyncedLocationElementResult[]; } catch (e: unknown) { if (e instanceof Error) { console.error(`DB Error: ${e.message}`); throw new Error(lang === 'fr' ? `Impossible de récupérer les éléments de lieu synchronisés.` : `Unable to retrieve synced location elements.`); } else { console.error("An unknown error occurred."); throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); } } } static fetchSyncedLocationSubElements(userId: string, lang: 'fr' | 'en'): SyncedLocationSubElementResult[] { try { const db: Database = System.getDb(); return db.all('SELECT sub_element_id, element_id, sub_elem_name, last_update FROM location_sub_element WHERE user_id = ?', [userId]) as SyncedLocationSubElementResult[]; } catch (e: unknown) { if (e instanceof Error) { console.error(`DB Error: ${e.message}`); throw new Error(lang === 'fr' ? `Impossible de récupérer les sous-éléments de lieu synchronisés.` : `Unable to retrieve synced location sub-elements.`); } else { console.error("An unknown error occurred."); throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); } } } static insertSyncLocation(locId: string, bookId: string, userId: string, locName: string, locOriginalName: string, lastUpdate: number, lang: 'fr' | 'en'): boolean { try { const db: Database = System.getDb(); const result: RunResult = db.run( `INSERT INTO book_location (loc_id, book_id, user_id, loc_name, loc_original_name, last_update) VALUES (?, ?, ?, ?, ?, ?)`, [locId, bookId, userId, locName, locOriginalName, lastUpdate] ); return result.changes > 0; } catch (e: unknown) { if (e instanceof Error) { console.error(`DB Error: ${e.message}`); throw new Error(lang === 'fr' ? `Impossible d'insérer le lieu.` : `Unable to insert location.`); } else { throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); } } } static insertSyncLocationElement(elementId: string, location: string, userId: string, elementName: string, originalName: string, elementDescription: string | null, lastUpdate: number, lang: 'fr' | 'en'): boolean { try { const db: Database = System.getDb(); const result: RunResult = db.run( `INSERT INTO location_element (element_id, location, user_id, element_name, original_name, element_description, last_update) VALUES (?, ?, ?, ?, ?, ?, ?)`, [elementId, location, userId, elementName, originalName, elementDescription, lastUpdate] ); return result.changes > 0; } catch (e: unknown) { if (e instanceof Error) { console.error(`DB Error: ${e.message}`); throw new Error(lang === 'fr' ? `Impossible d'insérer l'élément du lieu.` : `Unable to insert location element.`); } else { throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); } } } static insertSyncLocationSubElement(subElementId: string, elementId: string, userId: string, subElemName: string, originalName: string, subElemDescription: string | null, lastUpdate: number, lang: 'fr' | 'en'): boolean { try { const db: Database = System.getDb(); const result: RunResult = db.run( `INSERT INTO location_sub_element (sub_element_id, element_id, user_id, sub_elem_name, original_name, sub_elem_description, last_update) VALUES (?, ?, ?, ?, ?, ?, ?)`, [subElementId, elementId, userId, subElemName, originalName, subElemDescription, lastUpdate] ); return result.changes > 0; } catch (e: unknown) { if (e instanceof Error) { console.error(`DB Error: ${e.message}`); throw new Error(lang === 'fr' ? `Impossible d'insérer le sous-élément du lieu.` : `Unable to insert location sub-element.`); } else { throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); } } } static async fetchCompleteLocationById(id: string, lang: "fr" | "en"):Promise { try { const db: Database = System.getDb(); return db.all( `SELECT loc_id, book_id, user_id, loc_name, loc_original_name, last_update FROM book_location WHERE loc_id = ?`, [id] ) as BookLocationTable[]; } catch (e:unknown){ if (e instanceof Error) { throw new Error(lang === 'fr' ? `Impossible de récupérer le lieu complet.` : `Unable to retrieve complete location.`); } else { throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); } } } static async fetchCompleteLocationElementById(id: string, lang: "fr" | "en"):Promise { try { const db: Database = System.getDb(); return db.all( `SELECT element_id, location, user_id, element_name, original_name, element_description, last_update FROM location_element WHERE element_id = ?`, [id] ) as LocationElementTable[]; } catch (e:unknown){ if (e instanceof Error) { throw new Error(lang === 'fr' ? `Impossible de récupérer l'élément de lieu complet.` : `Unable to retrieve complete location element.`); } else { throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); } } } static async fetchCompleteLocationSubElementById(id: string, lang: "fr" | "en"):Promise { try { const db: Database = System.getDb(); return db.all( `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 = ?`, [id] ) as LocationSubElementTable[]; } catch (e:unknown){ if (e instanceof Error) { throw new Error(lang === 'fr' ? `Impossible de récupérer le sous-élément de lieu complet.` : `Unable to retrieve complete location sub-element.`); } else { throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); } } } }