import CharacterRepo, { AttributeResult, CharacterResult, CompleteCharacterResult } from "../repositories/character.repository.js"; import System from "../System.js"; import {getUserEncryptionKey} from "../keyManager.js"; export type CharacterCategory = 'Main' | 'Secondary' | 'Recurring'; export interface CharacterPropsPost { id: string | null; name: string; lastName: string; category: CharacterCategory; title: string; image: string; physical: { name: string }[]; psychological: { name: string }[]; relations: { name: string }[]; skills: { name: string }[]; weaknesses: { name: string }[]; strengths: { name: string }[]; goals: { name: string }[]; motivations: { name: string }[]; role: string; biography?: string; history?: string; } export interface CharacterProps { id: string; name: string; lastName: string; title: string; category: string; image: string; role: string; biography: string; history: string; } export interface CompleteCharacterProps { id?: string; name: string; lastName: string; title: string; category: string; image?: string; role: string; biography: string; history: string; [key: string]: Attribute[] | string | undefined; } export interface Attribute { id: string; name: string; } export interface CharacterAttribute { type: string; values: Attribute[]; } export default class Character { public static getCharacterList(userId: string, bookId: string, lang: 'fr' | 'en' = 'fr'): CharacterProps[] { const userKey: string = getUserEncryptionKey(userId); const characters: CharacterResult[] = CharacterRepo.fetchCharacters(userId, bookId, lang); if (!characters) return []; if (characters.length === 0) return []; const characterList: CharacterProps[] = []; for (const character of characters) { characterList.push({ id: character.character_id, name: character.first_name ? System.decryptDataWithUserKey(character.first_name, userKey) : '', lastName: character.last_name ? System.decryptDataWithUserKey(character.last_name, userKey) : '', title: character.title ? System.decryptDataWithUserKey(character.title, userKey) : '', category: character.category ? System.decryptDataWithUserKey(character.category, userKey) : '', image: character.image ? System.decryptDataWithUserKey(character.image, userKey) : '', role: character.role ? System.decryptDataWithUserKey(character.role, userKey) : '', biography: character.biography ? System.decryptDataWithUserKey(character.biography, userKey) : '', history: character.history ? System.decryptDataWithUserKey(character.history, userKey) : '', }) } return characterList; } public static addNewCharacter(userId: string, character: CharacterPropsPost, bookId: string, lang: 'fr' | 'en' = 'fr'): string { const userKey: string = getUserEncryptionKey(userId); const characterId: string = System.createUniqueId(); const encryptedName: string = System.encryptDataWithUserKey(character.name, userKey); const encryptedLastName: string = System.encryptDataWithUserKey(character.lastName, userKey); const encryptedTitle: string = System.encryptDataWithUserKey(character.title, userKey); const encryptedCategory: string = System.encryptDataWithUserKey(character.category, userKey); const encryptedImage: string = System.encryptDataWithUserKey(character.image, userKey); const encryptedRole: string = System.encryptDataWithUserKey(character.role, userKey); const encryptedBiography: string = System.encryptDataWithUserKey(character.biography ? character.biography : '', userKey); const encryptedHistory: string = System.encryptDataWithUserKey(character.history ? character.history : '', userKey); CharacterRepo.addNewCharacter(userId, characterId, encryptedName, encryptedLastName, encryptedTitle, encryptedCategory, encryptedImage, encryptedRole, encryptedBiography, encryptedHistory, bookId, lang); const attributes: string[] = Object.keys(character); for (const key of attributes) { if (Array.isArray(character[key as keyof CharacterPropsPost])) { const array = character[key as keyof CharacterPropsPost] as { name: string }[]; if (array.length > 0) { for (const item of array) { const type: string = key; const name: string = item.name; this.addNewAttribute(characterId, userId, type, name, lang); } } } } return characterId; } static updateCharacter(userId: string, character: CharacterPropsPost, lang: 'fr' | 'en' = 'fr'): boolean { const userKey: string = getUserEncryptionKey(userId); if (!character.id) { return false; } const encryptedName: string = System.encryptDataWithUserKey(character.name, userKey); const encryptedLastName: string = System.encryptDataWithUserKey(character.lastName, userKey); const encryptedTitle: string = System.encryptDataWithUserKey(character.title, userKey); const encryptedCategory: string = System.encryptDataWithUserKey(character.category, userKey); const encryptedImage: string = System.encryptDataWithUserKey(character.image, userKey); const encryptedRole: string = System.encryptDataWithUserKey(character.role, userKey); const encryptedBiography: string = System.encryptDataWithUserKey(character.biography ? character.biography : '', userKey); const encryptedHistory: string = System.encryptDataWithUserKey(character.history ? character.history : '', userKey); return CharacterRepo.updateCharacter(userId, character.id, encryptedName, encryptedLastName, encryptedTitle, encryptedCategory, encryptedImage, encryptedRole, encryptedBiography, encryptedHistory, System.timeStampInSeconds(), lang); } static addNewAttribute(characterId: string, userId: string, type: string, name: string, lang: 'fr' | 'en' = 'fr'): string { const userKey: string = getUserEncryptionKey(userId); const attributeId: string = System.createUniqueId(); const encryptedType: string = System.encryptDataWithUserKey(type, userKey); const encryptedName: string = System.encryptDataWithUserKey(name, userKey); return CharacterRepo.insertAttribute(attributeId, characterId, userId, encryptedType, encryptedName, lang); } static deleteAttribute(userId: string, attributeId: string, lang: 'fr' | 'en' = 'fr') { return CharacterRepo.deleteAttribute(userId, attributeId, lang); } static getAttributes(characterId: string, userId: string, lang: 'fr' | 'en' = 'fr'): CharacterAttribute[] { const userKey: string = getUserEncryptionKey(userId); const attributes: AttributeResult[] = CharacterRepo.fetchAttributes(characterId, userId, lang); if (!attributes?.length) return []; const groupedMap: Map = new Map(); for (const attribute of attributes) { const type: string = System.decryptDataWithUserKey(attribute.attribute_name, userKey); const value: string = attribute.attribute_value ? System.decryptDataWithUserKey(attribute.attribute_value, userKey) : ''; if (!groupedMap.has(type)) { groupedMap.set(type, []); } groupedMap.get(type)!.push({ id: attribute.attr_id, name: value }); } return Array.from<[string, Attribute[]], CharacterAttribute>( groupedMap, ([type, values]: [string, Attribute[]]): CharacterAttribute => ({type, values}) ); } static getCompleteCharacterList(userId: string, bookId: string, characters: string[], lang: 'fr' | 'en' = 'fr'): CompleteCharacterProps[] { const characterList: CompleteCharacterResult[] = CharacterRepo.fetchCompleteCharacters(userId, bookId, characters, lang); if (!characterList || characterList.length === 0) { return []; } const userKey: string = getUserEncryptionKey(userId); const completeCharactersMap = new Map(); for (const character of characterList) { if (!character.character_id) { continue; } if (!completeCharactersMap.has(character.character_id)) { const personnageObj: CompleteCharacterProps = { id: '', name: character.first_name ? System.decryptDataWithUserKey(character.first_name, userKey) : '', lastName: character.last_name ? System.decryptDataWithUserKey(character.last_name, userKey) : '', title: character.title ? System.decryptDataWithUserKey(character.title, userKey) : '', category: character.category ? System.decryptDataWithUserKey(character.category, userKey) : '', role: character.role ? System.decryptDataWithUserKey(character.role, userKey) : '', biography: character.biography ? System.decryptDataWithUserKey(character.biography, userKey) : '', history: character.history ? System.decryptDataWithUserKey(character.history, userKey) : '', physical: [], psychological: [], relations: [], skills: [], weaknesses: [], strengths: [], goals: [], motivations: [] }; completeCharactersMap.set(character.character_id, personnageObj); } const personnage: CompleteCharacterProps | undefined = completeCharactersMap.get(character.character_id); if (!character.attribute_name || !personnage) { continue; } const decryptedName: string = System.decryptDataWithUserKey(character.attribute_name, userKey); const decryptedValue: string = character.attribute_value ? System.decryptDataWithUserKey(character.attribute_value, userKey) : ''; if (Array.isArray(personnage[decryptedName])) { personnage[decryptedName].push({ id: '', name: decryptedValue }); } } return Array.from(completeCharactersMap.values()); } static characterVCard(characters: CompleteCharacterProps[]): string { const charactersMap = new Map(); let charactersDescription: string = ''; characters.forEach((character: CompleteCharacterProps): void => { const characterKey: string = character.name || character.id || 'unknown'; if (!charactersMap.has(characterKey)) { charactersMap.set(characterKey, { name: character.name, lastName: character.lastName, category: character.category, title: character.title, role: character.role, biography: character.biography, history: character.history }); } const characterData: CompleteCharacterProps = charactersMap.get(characterKey)!; Object.keys(character).forEach((fieldName: string): void => { if (Array.isArray(character[fieldName])) { if (!characterData[fieldName]) characterData[fieldName] = []; (characterData[fieldName] as Attribute[]).push(...(character[fieldName] as Attribute[])); } }); }); charactersDescription = Array.from(charactersMap.values()).map((character: CompleteCharacterProps): string => { const descriptionFields: string[] = []; const fullName: string = [character.name, character.lastName].filter(Boolean).join(' '); if (fullName) descriptionFields.push(`Nom : ${fullName}`); (['category', 'title', 'role', 'biography', 'history'] as const).forEach((propertyKey) => { if (character[propertyKey]) { descriptionFields.push(`${propertyKey.charAt(0).toUpperCase() + propertyKey.slice(1)} : ${character[propertyKey]}`); } }); Object.keys(character).forEach((propertyKey: string): void => { const propertyValue: string | Attribute[] | undefined = character[propertyKey]; if (Array.isArray(propertyValue) && propertyValue.length > 0) { const capitalizedPropertyKey: string = propertyKey.charAt(0).toUpperCase() + propertyKey.slice(1); const formattedValues: string = propertyValue.map((item: Attribute) => item.name).join(', '); descriptionFields.push(`${capitalizedPropertyKey} : ${formattedValues}`); } }); return descriptionFields.join('\n'); }).join('\n\n'); return charactersDescription; } }