import { getUserEncryptionKey } from "../keyManager.js"; import System from "../System.js"; import SeriesCharacterRepo, { SeriesCharacterAttributeResult, SeriesCharacterResult } from "../repositories/series-character.repo.js"; export type CharacterCategory = 'Main' | 'Secondary' | 'Recurring'; export interface SeriesCharacterPropsPost { id: string | null; name: string; lastName: string; nickname: string; age: number | null; gender: string; species: string; nationality: string; status: 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 }[]; arc: { name: string }[]; secrets: { name: string }[]; fears: { name: string }[]; flaws: { name: string }[]; beliefs: { name: string }[]; conflicts: { name: string }[]; quotes: { name: string }[]; distinguishingMarks: { name: string }[]; items: { name: string }[]; affiliations: { name: string }[]; role: string; biography?: string; history?: string; speechPattern?: string; catchphrase?: string; residence?: string; notes?: string; color?: string; } export interface SeriesCharacterListProps { id: string; name: string; lastName: string; nickname: string; age: number | null; gender: string; species: string; nationality: string; status: string; title: string; category: string; image: string; role: string; biography: string; history: string; speechPattern: string; catchphrase: string; residence: string; notes: string; color: string; } export interface SeriesAttribute { id: string; name: string; } export interface CharacterAttributesResponse { attributes: SeriesAttribute[]; } export default class SeriesCharacter { /** * Retrieves a list of characters for a specific series owned by a user. * @param userId - The unique identifier of the user * @param seriesId - The unique identifier of the series * @param lang - The language for error messages ('fr' or 'en') * @returns Characters list */ public static getCharacterList(userId: string, seriesId: string, lang: 'fr' | 'en' = 'fr'): SeriesCharacterListProps[] { const characters: SeriesCharacterResult[] = SeriesCharacterRepo.fetchCharacters(userId, seriesId, lang); if (!characters || characters.length === 0) { return []; } const userKey: string = getUserEncryptionKey(userId); return characters.map((character: SeriesCharacterResult): SeriesCharacterListProps => ({ id: character.character_id, name: character.first_name ? System.decryptDataWithUserKey(character.first_name, userKey) : '', lastName: character.last_name ? System.decryptDataWithUserKey(character.last_name, userKey) : '', nickname: character.nickname ? System.decryptDataWithUserKey(character.nickname, userKey) : '', age: character.age ? parseInt(System.decryptDataWithUserKey(character.age, userKey), 10) : null, gender: character.gender ? System.decryptDataWithUserKey(character.gender, userKey) : '', species: character.species ? System.decryptDataWithUserKey(character.species, userKey) : '', nationality: character.nationality ? System.decryptDataWithUserKey(character.nationality, userKey) : '', status: character.status ? System.decryptDataWithUserKey(character.status, userKey) : 'alive', 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) : '', speechPattern: character.speech_pattern ? System.decryptDataWithUserKey(character.speech_pattern, userKey) : '', catchphrase: character.catchphrase ? System.decryptDataWithUserKey(character.catchphrase, userKey) : '', residence: character.residence ? System.decryptDataWithUserKey(character.residence, userKey) : '', notes: character.notes ? System.decryptDataWithUserKey(character.notes, userKey) : '', color: character.color ? System.decryptDataWithUserKey(character.color, userKey) : '', })); } /** * Adds a new character to a series with all its attributes. * @param userId - The unique identifier of the user * @param character - The character data to create * @param seriesId - The unique identifier of the series * @param lang - The language for error messages ('fr' or 'en') * @returns The newly created character's ID */ public static addNewCharacter(userId: string, character: SeriesCharacterPropsPost, seriesId: 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 | null = character.lastName ? System.encryptDataWithUserKey(character.lastName, userKey) : null; const encryptedNickname: string | null = character.nickname ? System.encryptDataWithUserKey(character.nickname, userKey) : null; const encryptedAge: string | null = character.age !== null ? System.encryptDataWithUserKey(String(character.age), userKey) : null; const encryptedGender: string | null = character.gender ? System.encryptDataWithUserKey(character.gender, userKey) : null; const encryptedSpecies: string | null = character.species ? System.encryptDataWithUserKey(character.species, userKey) : null; const encryptedNationality: string | null = character.nationality ? System.encryptDataWithUserKey(character.nationality, userKey) : null; const encryptedStatus: string | null = character.status ? System.encryptDataWithUserKey(character.status, userKey) : null; const encryptedTitle: string | null = character.title ? System.encryptDataWithUserKey(character.title, userKey) : null; const encryptedCategory: string | null = character.category ? System.encryptDataWithUserKey(character.category, userKey) : null; const encryptedImage: string | null = character.image ? System.encryptDataWithUserKey(character.image, userKey) : null; const encryptedRole: string | null = character.role ? System.encryptDataWithUserKey(character.role, userKey) : null; const encryptedBiography: string | null = character.biography ? System.encryptDataWithUserKey(character.biography, userKey) : null; const encryptedHistory: string | null = character.history ? System.encryptDataWithUserKey(character.history, userKey) : null; const encryptedSpeechPattern: string | null = character.speechPattern ? System.encryptDataWithUserKey(character.speechPattern, userKey) : null; const encryptedCatchphrase: string | null = character.catchphrase ? System.encryptDataWithUserKey(character.catchphrase, userKey) : null; const encryptedResidence: string | null = character.residence ? System.encryptDataWithUserKey(character.residence, userKey) : null; const encryptedNotes: string | null = character.notes ? System.encryptDataWithUserKey(character.notes, userKey) : null; const encryptedColor: string | null = character.color ? System.encryptDataWithUserKey(character.color, userKey) : null; SeriesCharacterRepo.addNewCharacter(userId, characterId, encryptedName, encryptedLastName, encryptedNickname, encryptedAge, encryptedGender, encryptedSpecies, encryptedNationality, encryptedStatus, encryptedTitle, encryptedCategory, encryptedImage, encryptedRole, encryptedBiography, encryptedHistory, encryptedSpeechPattern, encryptedCatchphrase, encryptedResidence, encryptedNotes, encryptedColor, seriesId, lang); const attributeKeys: string[] = Object.keys(character); for (const attributeKey of attributeKeys) { const attributeValue = character[attributeKey as keyof SeriesCharacterPropsPost]; if (Array.isArray(attributeValue)) { const attributeArray: { name: string }[] = attributeValue; if (attributeArray.length > 0) { for (const attributeItem of attributeArray) { const attributeType: string = attributeKey; const attributeName: string = attributeItem.name; this.addNewAttribute(characterId, userId, attributeType, attributeName, lang); } } } } return characterId; } /** * Updates an existing character's information and attributes. * @param userId - The unique identifier of the user * @param character - The updated character data * @param lang - The language for error messages ('fr' or 'en') * @returns True if the update was successful */ public static updateCharacter(userId: string, character: SeriesCharacterPropsPost, lang: 'fr' | 'en' = 'fr'): boolean { if (!character.id) { throw new Error(lang === 'fr' ? 'ID du personnage requis.' : 'Character ID required.'); } const exists: boolean = SeriesCharacterRepo.isCharacterExist(userId, character.id, lang); if (!exists) { throw new Error(lang === 'fr' ? 'Personnage non trouvé.' : 'Character not found.'); } const userKey: string = getUserEncryptionKey(userId); const encryptedName: string = System.encryptDataWithUserKey(character.name, userKey); const encryptedLastName: string | null = character.lastName ? System.encryptDataWithUserKey(character.lastName, userKey) : null; const encryptedNickname: string | null = character.nickname ? System.encryptDataWithUserKey(character.nickname, userKey) : null; const encryptedAge: string | null = character.age !== null ? System.encryptDataWithUserKey(String(character.age), userKey) : null; const encryptedGender: string | null = character.gender ? System.encryptDataWithUserKey(character.gender, userKey) : null; const encryptedSpecies: string | null = character.species ? System.encryptDataWithUserKey(character.species, userKey) : null; const encryptedNationality: string | null = character.nationality ? System.encryptDataWithUserKey(character.nationality, userKey) : null; const encryptedStatus: string | null = character.status ? System.encryptDataWithUserKey(character.status, userKey) : null; const encryptedTitle: string | null = character.title ? System.encryptDataWithUserKey(character.title, userKey) : null; const encryptedCategory: string | null = character.category ? System.encryptDataWithUserKey(character.category, userKey) : null; const encryptedImage: string | null = character.image ? System.encryptDataWithUserKey(character.image, userKey) : null; const encryptedRole: string | null = character.role ? System.encryptDataWithUserKey(character.role, userKey) : null; const encryptedBiography: string | null = character.biography ? System.encryptDataWithUserKey(character.biography, userKey) : null; const encryptedHistory: string | null = character.history ? System.encryptDataWithUserKey(character.history, userKey) : null; const encryptedSpeechPattern: string | null = character.speechPattern ? System.encryptDataWithUserKey(character.speechPattern, userKey) : null; const encryptedCatchphrase: string | null = character.catchphrase ? System.encryptDataWithUserKey(character.catchphrase, userKey) : null; const encryptedResidence: string | null = character.residence ? System.encryptDataWithUserKey(character.residence, userKey) : null; const encryptedNotes: string | null = character.notes ? System.encryptDataWithUserKey(character.notes, userKey) : null; const encryptedColor: string | null = character.color ? System.encryptDataWithUserKey(character.color, userKey) : null; return SeriesCharacterRepo.updateCharacter(userId, character.id, encryptedName, encryptedLastName, encryptedNickname, encryptedAge, encryptedGender, encryptedSpecies, encryptedNationality, encryptedStatus, encryptedTitle, encryptedCategory, encryptedImage, encryptedRole, encryptedBiography, encryptedHistory, encryptedSpeechPattern, encryptedCatchphrase, encryptedResidence, encryptedNotes, encryptedColor, lang); } /** * Deletes a character from a series. * @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 True if the deletion was successful */ public static deleteCharacter(userId: string, characterId: string, lang: 'fr' | 'en' = 'fr'): boolean { const exists: boolean = SeriesCharacterRepo.isCharacterExist(userId, characterId, lang); if (!exists) { throw new Error(lang === 'fr' ? 'Personnage non trouvé.' : 'Character not found.'); } return SeriesCharacterRepo.deleteCharacter(userId, characterId, lang); } /** * Adds a new attribute to a character. * @param characterId - The unique identifier of the character * @param userId - The unique identifier of the user * @param type - The attribute type * @param name - The attribute value * @param lang - The language for error messages ('fr' or 'en') * @returns The attribute ID */ public 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); SeriesCharacterRepo.insertAttribute(attributeId, characterId, userId, encryptedType, encryptedName, lang); return attributeId; } /** * Deletes an attribute from a character. * @param userId - The unique identifier of the user * @param attributeId - The unique identifier of the attribute * @param lang - The language for error messages ('fr' or 'en') * @returns True if the deletion was successful */ public static deleteAttribute(userId: string, attributeId: string, lang: 'fr' | 'en' = 'fr'): boolean { return SeriesCharacterRepo.deleteAttribute(userId, attributeId, lang); } /** * Gets all attributes for a character. * @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 The character's attributes */ public static getCharacterAttributes(userId: string, characterId: string, lang: 'fr' | 'en' = 'fr'): CharacterAttributesResponse { const userKey: string = getUserEncryptionKey(userId); const attributesResult: SeriesCharacterAttributeResult[] = SeriesCharacterRepo.fetchAttributes(characterId, userId, lang); const attributes: SeriesAttribute[] = attributesResult.map((attr) => ({ id: attr.attr_id, name: System.decryptDataWithUserKey(attr.attribute_value, userKey) })); return { attributes }; } }