import type {
    ActQuery,
    BookActSummariesTable, BookAIGuideLineTable, BookChapterContentTable, BookChapterInfosTable, BookChaptersTable,
    BookCharactersAttributesTable,
    BookCharactersTable, BookGuideLineTable, BookIncidentsTable, BookIssuesTable, BookLocationTable,
    BookPlotPointsTable, BookWorldElementsTable, BookWorldTable,
    EritBooksTable,
    IncidentQuery,
    IssueQuery, LocationElementTable, LocationSubElementTable,
    PlotPointQuery,
    SyncedActSummaryResult,
    SyncedAIGuideLineResult,
    SyncedBookResult,
    SyncedChapterContentResult,
    SyncedChapterInfoResult,
    SyncedChapterResult,
    SyncedCharacterAttributeResult,
    SyncedCharacterResult,
    SyncedGuideLineResult,
    SyncedIncidentResult,
    SyncedIssueResult,
    SyncedLocationElementResult,
    SyncedLocationResult,
    SyncedLocationSubElementResult,
    SyncedPlotPointResult,
    SyncedWorldElementResult,
    SyncedWorldResult,
    WorldQuery
} from '../repositories/book.repository.js';
import BookRepository from '../repositories/book.repository.js';
import BookRepo, {
    BookCoverQuery,
    BookQuery,
    ChapterBookResult,
    GuideLineAIQuery,
    GuideLineQuery,
    WorldElementValue
} from '../repositories/book.repository.js';
import System from '../System.js';
import {getUserEncryptionKey} from '../keyManager.js';
import path from "path";
import fs from "fs";
import Chapter, {ActChapter, ChapterContentData, ChapterProps} from "./Chapter.js";
import UserRepo from "../repositories/user.repository.js";
import ChapterRepo from "../repositories/chapter.repository.js";
import CharacterRepo from "../repositories/character.repository.js";
import LocationRepo from "../repositories/location.repository.js";

export interface BookProps{
    id:string;
    type:string;
    authorId:string;
    title:string;
    subTitle?:string;
    summary?:string;
    serieId?:number;
    desiredReleaseDate?:string;
    desiredWordCount?:number;
    wordCount?:number;
    coverImage?:string;
    bookMeta?:string;
}

export interface CompleteBook {
    eritBooks: EritBooksTable[];
    actSummaries: BookActSummariesTable[];
    aiGuideLine: BookAIGuideLineTable[];
    chapters: BookChaptersTable[];
    chapterContents: BookChapterContentTable[];
    chapterInfos: BookChapterInfosTable[];
    characters: BookCharactersTable[];
    characterAttributes: BookCharactersAttributesTable[];
    guideLine: BookGuideLineTable[];
    incidents: BookIncidentsTable[];
    issues: BookIssuesTable[];
    locations: BookLocationTable[];
    plotPoints: BookPlotPointsTable[];
    worlds: BookWorldTable[];
    worldElements: BookWorldElementsTable[];
    locationElements: LocationElementTable[];
    locationSubElements: LocationSubElementTable[];
}

export interface SyncedBook {
    id: string;
    type: string;
    title: string;
    subTitle: string | null;
    lastUpdate: number;
    chapters: SyncedChapter[];
    characters: SyncedCharacter[];
    locations: SyncedLocation[];
    worlds: SyncedWorld[];
    incidents: SyncedIncident[];
    plotPoints: SyncedPlotPoint[];
    issues: SyncedIssue[];
    actSummaries: SyncedActSummary[];
    guideLine: SyncedGuideLine | null;
    aiGuideLine: SyncedAIGuideLine | null;
}

export interface BookSyncCompare {
    id: string;
    chapters: string[];
    chapterContents: string[];
    chapterInfos: string[];
    characters: string[];
    characterAttributes: string[];
    locations: string[];
    locationElements: string[];
    locationSubElements: string[];
    worlds: string[];
    worldElements: string[];
    incidents: string[];
    plotPoints: string[];
    issues: string[];
    actSummaries: string[];
    guideLine: boolean;
    aiGuideLine: boolean;
}

export interface SyncedChapter {
    id: string;
    name: string;
    lastUpdate: number;
    contents: SyncedChapterContent[];
    info: SyncedChapterInfo | null;
}

export interface SyncedChapterContent {
    id: string;
    lastUpdate: number;
}

export interface SyncedChapterInfo {
    id: string;
    lastUpdate: number;
}

export interface SyncedCharacter {
    id: string;
    name: string;
    lastUpdate: number;
    attributes: SyncedCharacterAttribute[];
}

export interface SyncedCharacterAttribute {
    id: string;
    name: string;
    lastUpdate: number;
}

export interface SyncedLocation {
    id: string;
    name: string;
    lastUpdate: number;
    elements: SyncedLocationElement[];
}

export interface SyncedLocationElement {
    id: string;
    name: string;
    lastUpdate: number;
    subElements: SyncedLocationSubElement[];
}

export interface SyncedLocationSubElement {
    id: string;
    name: string;
    lastUpdate: number;
}

export interface SyncedWorld {
    id: string;
    name: string;
    lastUpdate: number;
    elements: SyncedWorldElement[];
}

export interface SyncedWorldElement {
    id: string;
    name: string;
    lastUpdate: number;
}

export interface SyncedIncident {
    id: string;
    name: string;
    lastUpdate: number;
}

export interface SyncedPlotPoint {
    id: string;
    name: string;
    lastUpdate: number;
}

export interface SyncedIssue {
    id: string;
    name: string;
    lastUpdate: number;
}

export interface SyncedActSummary {
    id: string;
    lastUpdate: number;
}

export interface SyncedGuideLine {
    lastUpdate: number;
}

export interface SyncedAIGuideLine {
    lastUpdate: number;
}

export interface GuideLine{
    tone:string;
    atmosphere:string;
    writingStyle:string;
    themes:string;
    symbolism: string;
    motifs: string;
    narrativeVoice: string;
    pacing: string;
    intendedAudience: string;
    keyMessages: string;
}

interface PlotPoint {
    plotPointId: string,
    title:string,
    summary:string,
    linkedIncidentId: string | null,
    chapters?:ActChapter[]
}

interface Incident {
    incidentId: string,
    title:string,
    summary:string,
    chapters?:ActChapter[]
}

export interface ExportData {
    buffer: Buffer;
    fileName: string;
}

export interface Act {
    id: number;
    summary: string | null;
    incidents?:Incident[];
    plotPoints?:PlotPoint[],
    chapters?:ActChapter[]
}

export interface Issue {
    id: string,
    name: string
}

export interface WorldElement {
    id: string;
    name: string;
    description: string;
    type?:number;
}

export interface WorldProps {
    id: string;
    name: string;
    history: string;
    politics: string;
    economy: string;
    religion: string;
    languages: string;
    laws: WorldElement[];
    biomes: WorldElement[];
    issues: WorldElement[];
    customs: WorldElement[];
    kingdoms: WorldElement[];
    climate: WorldElement[];
    resources: WorldElement[];
    wildlife: WorldElement[];
    arts: WorldElement[];
    ethnicGroups: WorldElement[];
    socialClasses: WorldElement[];
    importantCharacters: WorldElement[];
}

export interface GuideLineAI {
    narrativeType: number|null;
    dialogueType: number|null;
    globalResume: string|null;
    atmosphere: string|null;
    verbeTense: number|null;
    langue: number|null;
    currentResume: string|null;
    themes: string|null;
}

export interface CompleteBookData {
    bookId: string;
    title: string;
    subTitle: string;
    summary: string;
    coverImage: string;
    userInfos: {
        firstName: string;
        lastName: string;
        authorName: string;
    },
    chapters: CompleteChapterContent[];
}

export interface CompleteChapterContent {
    id: string;
    title: string;
    content: string;
    order: number;
    version?: number;
}

export default class Book {
    private readonly id: string;
    private type:string;
    private authorId: string;
    private title: string;
    private subTitle: string;
    private summary: string;
    private serieId: number;
    private desiredReleaseDate: string;
    private desiredWordCount:number;
    private wordCount: number;
    private cover: string;
    
    constructor(id:string,authorId?:string) {
        this.id = id;
        if (authorId){
            this.authorId = authorId;
        } else {
            this.authorId = '';
        }
        this.title = '';
        this.subTitle = '';
        this.summary = '';
        this.serieId = 0;
        this.desiredReleaseDate = '';
        this.desiredWordCount = 0;
        this.wordCount = 0;
        this.cover = '';
        this.type = '';
    }
    public static async getBooks(userId: string, lang: 'fr' | 'en' = 'fr'): Promise<BookProps[]> {
        const userKey: string | null = getUserEncryptionKey(userId);
        if (!userKey) {
            throw new Error(
                lang === 'fr' ? "Clé d'encryption utilisateur non trouvée." : 'User encryption key not found.'
            );
        }

        const books:BookQuery[] = BookRepository.fetchBooks(userId, lang);
        if (!books || books.length === 0) {
            return [];
        }
        
        return await Promise.all(
            books.map(async (book: BookQuery):Promise<BookProps> => {
                return {
                    id: book.book_id,
                    type: book.type,
                    authorId: book.author_id,
                    title: System.decryptDataWithUserKey(book.title, userKey),
                    subTitle: book.sub_title ? System.decryptDataWithUserKey(book.sub_title, userKey) : '',
                    summary: book.summary ? System.decryptDataWithUserKey(book.summary, userKey) : '',
                    serieId: book.serie_id || 0,
                    desiredReleaseDate: book.desired_release_date || '',
                    desiredWordCount: book.desired_word_count || 0,
                    wordCount: book.words_count || 0,
                    coverImage: book.cover_image ? await this.getPicture(userId, userKey, book.cover_image) : '',
                };
            }) ?? []
        );
    }
    
    public static async getCoverPicture(userId:string,bookId:string, lang: 'fr' | 'en' = 'fr'):Promise<string>{
        const query:BookCoverQuery = BookRepo.fetchBookCover(userId,bookId,lang);
        if (query){
            const userKey:string = getUserEncryptionKey(userId);
            return System.decryptDataWithUserKey(query.cover_image,userKey);
        } else {
            return '';
        }
    }
    
    public static async deleteCoverPicture(userId: string, bookId: string, lang: 'fr' | 'en' = 'fr'): Promise<boolean> {
        const coverName:string = await Book.getCoverPicture(userId,bookId,lang);
        return BookRepo.updateBookCover(bookId, '', userId, lang);
    }
    public static getPicture(userId: string, userKey: string, image: string, lang: 'fr' | 'en' = 'fr'): string {
        if (!image) return '';
        try {
            const decryptedFileName: string = System.decryptDataWithUserKey(image, userKey);
            const userDirectory: string = path.join('');
            fs.accessSync(userDirectory, fs.constants.R_OK);
            const data = fs.readFileSync(userDirectory);
            return data.toString('base64');
        } catch (err) {
            return '';
        }
    }
    
    public static async addBook(bookId: string | null, userId: string, title: string, subTitle: string, summary: string, type: string, serie: number, publicationDate: string, desiredWordCount: number, lang: 'fr' | 'en' = 'fr'): Promise<string> {
        let id:string = '';
        const userKey:string|null = getUserEncryptionKey(userId);

        const encryptedTitle:string = System.encryptDataWithUserKey(title, userKey);
        const encryptedSubTitle:string = subTitle ? System.encryptDataWithUserKey(subTitle, userKey) : '';
        const encryptedSummary:string = summary ? System.encryptDataWithUserKey(summary, userKey) : '';
        const hashedTitle:string = System.hashElement(title);
        const hashedSubTitle:string = subTitle ? System.hashElement(subTitle) : '';
        
        if (BookRepo.verifyBookExist(hashedTitle,hashedSubTitle,userId,lang)){
            throw new Error(lang === "fr" ? `Tu as déjà un livre intitulé ${title} - ${subTitle}.` : `You already have a book named ${title} - ${subTitle}.`);
        }
        if (bookId){
            id = bookId;
        } else {
            id = System.createUniqueId();
        }
        return BookRepo.insertBook(id,userId,encryptedTitle,hashedTitle,encryptedSubTitle,hashedSubTitle,encryptedSummary,type,serie,publicationDate,desiredWordCount,lang);
    }
    
    public static async getBook(userId:string,bookId: string): Promise<BookProps> {
        const book:Book = new Book(bookId);
        await book.getBookInfos(userId);
        return {
            id: book.getId(),
            type: book.getType(),
            authorId: book.getAuthorId(),
            title: book.getTitle(),
            subTitle: book.getSubTitle(),
            summary: book.getSummary(),
            serieId: book.getSerieId(),
            desiredReleaseDate: book.getDesiredReleaseDate(),
            desiredWordCount: book.getDesiredWordCount(),
            wordCount: book.getWordCount(),
            coverImage: book.getCover()
        };
    }
    
    public static async getGuideLine(userId: string, bookId: string, lang: 'fr' | 'en' = 'fr'): Promise<GuideLine | null> {
        const guideLineResponse: GuideLineQuery[] = BookRepo.fetchGuideLine(userId, bookId,lang);
        if (guideLineResponse.length === 0) {
            return null;
        }
        const guideLine: GuideLineQuery = guideLineResponse[0];
        const userKey: string = getUserEncryptionKey(userId);
        return {
            tone: guideLine.tone ? System.decryptDataWithUserKey(guideLine.tone, userKey) : '',
            atmosphere: guideLine.atmosphere ? System.decryptDataWithUserKey(guideLine.atmosphere, userKey) : '',
            writingStyle: guideLine.writing_style ? System.decryptDataWithUserKey(guideLine.writing_style, userKey) : '',
            themes: guideLine.themes ? System.decryptDataWithUserKey(guideLine.themes, userKey) : '',
            symbolism: guideLine.symbolism ? System.decryptDataWithUserKey(guideLine.symbolism, userKey) : '',
            motifs: guideLine.motifs ? System.decryptDataWithUserKey(guideLine.motifs, userKey) : '',
            narrativeVoice: guideLine['narrative-voice'] ? System.decryptDataWithUserKey(guideLine['narrative-voice'] as string, userKey) : '',
            pacing: guideLine.pacing ? System.decryptDataWithUserKey(guideLine.pacing, userKey) : '',
            intendedAudience: guideLine.intended_audience ? System.decryptDataWithUserKey(guideLine.intended_audience, userKey) : '',
            keyMessages: guideLine.key_messages ? System.decryptDataWithUserKey(guideLine.key_messages, userKey) : '',
        };
    }
    
    public static async updateGuideLine(userId: string, bookId: string, tone: string | null, atmosphere: string | null, writingStyle: string | null, themes: string | null, symbolism: string | null, motifs: string | null, narrativeVoice: string | null, pacing: string | null, keyMessages: string | null, intendedAudience: string | null, lang: 'fr' | 'en' = 'fr'): Promise<boolean> {
        const userKey: string = getUserEncryptionKey(userId);
        const encryptedTone: string = tone ? System.encryptDataWithUserKey(tone, userKey) : '';
        const encryptedAtmosphere: string = atmosphere ? System.encryptDataWithUserKey(atmosphere, userKey) : '';
        const encryptedWritingStyle: string = writingStyle ? System.encryptDataWithUserKey(writingStyle, userKey) : '';
        const encryptedThemes: string = themes ? System.encryptDataWithUserKey(themes, userKey) : '';
        const encryptedSymbolism: string = symbolism ? System.encryptDataWithUserKey(symbolism, userKey) : '';
        const encryptedMotifs: string = motifs ? System.encryptDataWithUserKey(motifs, userKey) : '';
        const encryptedNarrativeVoice: string = narrativeVoice ? System.encryptDataWithUserKey(narrativeVoice, userKey) : '';
        const encryptedPacing: string = pacing ? System.encryptDataWithUserKey(pacing, userKey) : '';
        const encryptedKeyMessages: string = keyMessages ? System.encryptDataWithUserKey(keyMessages, userKey) : '';
        const encryptedIntendedAudience: string = intendedAudience ? System.encryptDataWithUserKey(intendedAudience, userKey) : '';
        
        return BookRepo.updateGuideLine(userId, bookId, encryptedTone, encryptedAtmosphere, encryptedWritingStyle, encryptedThemes, encryptedSymbolism, encryptedMotifs, encryptedNarrativeVoice, encryptedPacing, encryptedKeyMessages, encryptedIntendedAudience, lang);
    }
    
    public static addNewIncident(userId: string, bookId: string, name: string, lang: 'fr' | 'en' = 'fr'): string {
        const userKey: string = getUserEncryptionKey(userId);
        const encryptedName:string = System.encryptDataWithUserKey(name,userKey);
        const hashedName:string = System.hashElement(name);
        const incidentId: string = System.createUniqueId();
        return BookRepo.insertNewIncident(incidentId, userId, bookId, encryptedName, hashedName, lang);
    }
    public static async getPlotPoints(userId:string, bookId: string,actChapters:ActChapter[], lang: 'fr' | 'en' = 'fr'):Promise<PlotPoint[]>{
        const query:PlotPointQuery[] = BookRepo.fetchAllPlotPoints(userId,bookId,lang);
        const userKey:string = getUserEncryptionKey(userId);
        let plotPoints:PlotPoint[] = [];
        if (query.length>0){
            for (const plot of query) {
                let chapters:ActChapter[] = [];
                for (const chapter of actChapters) {
                    if (chapter.plotPointId === plot.plot_point_id){
                        chapters.push(chapter);
                    }
                }
                plotPoints.push({
                    plotPointId: plot.plot_point_id,
                    title: plot.title ? System.decryptDataWithUserKey(plot.title,userKey) : '',
                    summary : plot.summary ? System.decryptDataWithUserKey(plot.summary,userKey) : '',
                    linkedIncidentId: plot.linked_incident_id,
                    chapters:chapters
                })
            }
        }
        return plotPoints;
    }
    public static async getIncitentsIncidents(userId:string,bookId: string,actChapters:ActChapter[], lang: 'fr' | 'en' = 'fr'):Promise<Incident[]>{
        const query:IncidentQuery[] = BookRepo.fetchAllIncitentIncidents(userId,bookId,lang);
        let incidents:Incident[] = [];
        const userKey:string = getUserEncryptionKey(userId);
        if (query.length>0){
            for (const incident of query) {
                let chapters:ActChapter[] = [];
                for (const chapter of actChapters) {
                    if (chapter.incidentId === incident.incident_id){
                        chapters.push(chapter);
                    }
                }
                incidents.push({
                    incidentId: incident.incident_id,
                    title: incident.title ? System.decryptDataWithUserKey(incident.title,userKey) : '',
                    summary : incident.summary ? System.decryptDataWithUserKey(incident.summary,userKey) : '',
                    chapters:chapters
                })
            }
        }
        return incidents;
    }
    
    public static removeIncident(userId: string, bookId: string, incidentId: string, lang: 'fr' | 'en' = 'fr'): boolean {
        return BookRepo.deleteIncident(userId, bookId, incidentId, lang);
    }
    
    public static async getActsData(userId: string, bookId: string, lang: 'fr' | 'en' = 'fr'): Promise<Act[]> {
        const userKey:string = getUserEncryptionKey(userId);
        const actChapters:ActChapter[] = Chapter.getAllChapterFromActs(userId, bookId, lang);
        const query: ActQuery[] = BookRepo.fetchAllActs(userId, bookId, lang);
        const incidents: Incident[] = await Book.getIncitentsIncidents(userId, bookId, actChapters);
        const plotsPoint: PlotPoint[] = await Book.getPlotPoints(userId, bookId, actChapters);
        let acts: Act[] = [];
        acts.push({
            id: 1,
            summary: '',
            chapters: actChapters.filter((chapter: ActChapter) => chapter.actId === 1)
        })
        acts.push({
            id: 2,
            summary: '',
            incidents: incidents ? incidents : [],
        })
        acts.push({
            id: 3,
            summary: '',
            plotPoints: plotsPoint ? plotsPoint : [],
        })
        acts.push({
            id: 4,
            summary: '',
            chapters: actChapters.filter((chapter: ActChapter) => chapter.actId === 4)
        })
        acts.push({
            id: 5,
            summary: '',
            chapters: actChapters.filter((chapter: ActChapter) => chapter.actId === 5)
        })
        if (query.length > 0) {
            for (const act of query) {
                acts[act.act_index - 1].summary = act.summary && userKey ? System.decryptDataWithUserKey(act.summary, userKey) : '';
            }
        }
        return acts;
    }
    public static async getIssuesFromBook(userId:string,bookId:string, lang: 'fr' | 'en' = 'fr'):Promise<Issue[]>{
        const query:IssueQuery[] = BookRepo.fetchIssuesFromBook(userId,bookId,lang);
        const userKey:string = getUserEncryptionKey(userId);
        let issues:Issue[] = [];
        if (query.length>0){
            for (const issue of query) {
                issues.push({
                    id:issue.issue_id,
                    name: System.decryptDataWithUserKey(issue.name,userKey)
                })
            }
        }
        return issues;
    }
    
    static updateBookBasicInformation(userId: string, title: string, subTitle: string, summary: string, publicationDate: string, wordCount: number, bookId: string, lang: 'fr' | 'en' = 'fr'): boolean {
        const userKey:string = getUserEncryptionKey(userId);
        const encryptedTitle:string = System.encryptDataWithUserKey(title, userKey);
        const encryptedSubTitle:string = subTitle ? System.encryptDataWithUserKey(subTitle, userKey) : '';
        const encryptedSummary:string = summary ? System.encryptDataWithUserKey(summary, userKey) : '';
        const hashedTitle:string = System.hashElement(title);
        const hashedSubTitle:string = subTitle ? System.hashElement(subTitle) : '';
        return BookRepo.updateBookBasicInformation(userId, encryptedTitle, hashedTitle, encryptedSubTitle, hashedSubTitle, encryptedSummary, publicationDate, wordCount, bookId, lang);
    }
    
    static addNewPlotPoint(userId: string, bookId: string, incidentId: string, name: string, lang: 'fr' | 'en' = 'fr'): string {
        const userKey:string = getUserEncryptionKey(userId);
        const encryptedName:string = System.encryptDataWithUserKey(name, userKey);
        const hashedName:string = System.hashElement(name);
        const plotPointId: string = System.createUniqueId();
        return BookRepo.insertNewPlotPoint(plotPointId, userId, bookId, encryptedName, hashedName, incidentId, lang);
    }
    
    static removePlotPoint(userId: string, plotId: string, lang: 'fr' | 'en' = 'fr'): boolean{
        return BookRepo.deletePlotPoint(userId, plotId, lang);
    }
    
    public static addNewIssue(userId: string, bookId: string, name: string, lang: 'fr' | 'en' = 'fr'): string {
        const userKey:string = getUserEncryptionKey(userId);
        const encryptedName:string = System.encryptDataWithUserKey(name,userKey);
        const hashedName:string = System.hashElement(name);
        const issueId: string = System.createUniqueId();
        return BookRepo.insertNewIssue(issueId, userId, bookId, encryptedName, hashedName,lang);
    }
    
    public static removeIssue(userId: string, issueId: string, lang: 'fr' | 'en' = 'fr'): boolean{
        return BookRepo.deleteIssue(userId, issueId,lang);
    }
    
    public static async updateAct(acts: Act[], userId: string, bookId: string, userKey: string, lang: 'fr' | 'en' = 'fr'): Promise<boolean> {
        for (const act of acts) {
            const incidents: Incident[] = act.incidents ? act.incidents : [];
            const actId: number = act.id;
            if (actId === 1 || actId === 4 || actId === 5) {
                const actSummary: string = act.summary ? System.encryptDataWithUserKey(act.summary, userKey) : '';
                try {
                    BookRepo.updateActSummary(userId, bookId, actId, actSummary,System.timeStampInSeconds(),lang);
                } catch (e: unknown) {
                    const actSummaryId: string = System.createUniqueId();
                    BookRepo.insertActSummary(actSummaryId, userId, bookId, actId, actSummary,lang);
                }
                if (act.chapters) {
                    Chapter.updateChapterInfos(act.chapters, userId, actId, bookId, null, null, lang);
                }
            } else if (actId === 2) {
                for (const incident of incidents) {
                    const incidentSummary: string = incident.summary ? System.encryptDataWithUserKey(incident.summary, userKey) : '';
                    const incidentId: string = incident.incidentId;
                    const incidentName: string = incident.title;
                    const incidentHashedName: string = System.hashElement(incidentName);
                    const encryptedIncidentName: string = System.encryptDataWithUserKey(incidentName, userKey);
                    BookRepo.updateIncident(userId, bookId, incidentId, encryptedIncidentName, incidentHashedName, incidentSummary, System.timeStampInSeconds(), lang);
                    if (incident.chapters) {
                        Chapter.updateChapterInfos(incident.chapters, userId, actId, bookId, incidentId, null, lang);
                    }
                }
            } else {
                const plotPoints: PlotPoint[] = act.plotPoints ? act.plotPoints : [];
                for (const plotPoint of plotPoints) {
                    const plotPointSummary: string = plotPoint.summary ? System.encryptDataWithUserKey(plotPoint.summary, userKey) : '';
                    const plotPointId: string = plotPoint.plotPointId;
                    const plotPointName: string = plotPoint.title;
                    const plotPointHashedName: string = System.hashElement(plotPointName);
                    const encryptedPlotPointName: string = System.encryptDataWithUserKey(plotPointName, userKey);
                    BookRepo.updatePlotPoint(userId, bookId, plotPointId, encryptedPlotPointName, plotPointHashedName, plotPointSummary, System.timeStampInSeconds(), lang);
                    if (plotPoint.chapters) {
                        Chapter.updateChapterInfos(plotPoint.chapters, userId, actId, bookId, null, plotPointId, lang);
                    }
                }
            }
        }
        return true;
    }

    public static updateStory(userId: string, bookId: string, acts: Act[], mainChapters: ChapterProps[], lang: 'fr' | 'en' = 'fr'): boolean {
        const userKey: string = getUserEncryptionKey(userId);
        Book.updateAct(acts, userId, bookId, userKey, lang);
        for (const chapter of mainChapters) {
            const chapterId: string = chapter.chapterId;
            const chapterTitle: string = chapter.title;
            const chapterHashedTitle: string = System.hashElement(chapterTitle);
            const encryptedTitle: string = System.encryptDataWithUserKey(chapterTitle, userKey);
            const chapterOrder: number = chapter.chapterOrder;
            ChapterRepo.updateChapter(userId, chapterId, encryptedTitle, chapterHashedTitle, chapterOrder, System.timeStampInSeconds(), lang);
        }
        return true;
    }

    public static addNewWorld(userId: string, bookId: string, worldName: string, lang: 'fr' | 'en' = 'fr'): string {
        const userKey: string = getUserEncryptionKey(userId);
        const hashedName: string = System.hashElement(worldName);
        if (BookRepo.checkWorldExist(userId, bookId, hashedName, lang)) {
            throw new Error(lang === "fr" ? `Tu as déjà un monde ${worldName}.` : `You already have a world named ${worldName}.`);
        }
        const encryptedName: string = System.encryptDataWithUserKey(worldName, userKey);
        const worldId: string = System.createUniqueId();
        return BookRepo.insertNewWorld(worldId, userId, bookId, encryptedName, hashedName, lang);
    }

    public static getWorlds(userId: string, bookId: string, lang: 'fr' | 'en' = 'fr'): WorldProps[] {
        const worldElements: WorldQuery[] = BookRepo.fetchWorlds(userId, bookId, lang);
        const userKey: string = getUserEncryptionKey(userId);
        let worlds: WorldProps[] = []
        for (const element of worldElements) {
            const existWorld: WorldProps | undefined = worlds.find((world: WorldProps) => world.id === element.world_id);

            if (!existWorld) {
                const newWorld: WorldProps = {
                    id: element.world_id,
                    name: System.decryptDataWithUserKey(element.world_name, userKey),
                    history: element.history ? System.decryptDataWithUserKey(element.history, userKey) : '',
                    politics: element.politics ? System.decryptDataWithUserKey(element.politics, userKey) : '',
                    economy: element.economy ? System.decryptDataWithUserKey(element.economy, userKey) : '',
                    religion: element.religion ? System.decryptDataWithUserKey(element.religion, userKey) : '',
                    languages: element.languages ? System.decryptDataWithUserKey(element.languages, userKey) : '',
                    laws: [],
                    biomes: [],
                    issues: [],
                    customs: [],
                    kingdoms: [],
                    climate: [],
                    resources: [],
                    wildlife: [],
                    arts: [],
                    ethnicGroups: [],
                    socialClasses: [],
                    importantCharacters: [],
                };
                
                worlds.push(newWorld);

                if (element.element_type) {
                    const newElement = {
                        id: element.element_id as string,
                        name: element.element_name ? System.decryptDataWithUserKey(element.element_name, userKey) : '',
                        description: element.element_description ? System.decryptDataWithUserKey(element.element_description, userKey) : ''
                    };
                    
                    switch (element.element_type) {
                        case 1:
                            worlds[worlds.length - 1].laws.push(newElement);
                            break;
                        case 2:
                            worlds[worlds.length - 1].biomes.push(newElement);
                            break;
                        case 3:
                            worlds[worlds.length - 1].issues.push(newElement);
                            break;
                        case 4:
                            worlds[worlds.length - 1].customs.push(newElement);
                            break;
                        case 5:
                            worlds[worlds.length - 1].kingdoms.push(newElement);
                            break;
                        case 6:
                            worlds[worlds.length - 1].climate.push(newElement);
                            break;
                        case 7:
                            worlds[worlds.length - 1].resources.push(newElement);
                            break;
                        case 8:
                            worlds[worlds.length - 1].wildlife.push(newElement);
                            break;
                        case 9:
                            worlds[worlds.length - 1].arts.push(newElement);
                            break;
                        case 10:
                            worlds[worlds.length - 1].ethnicGroups.push(newElement);
                            break;
                        case 11:
                            worlds[worlds.length - 1].socialClasses.push(newElement);
                            break;
                        case 12:
                            worlds[worlds.length - 1].importantCharacters.push(newElement);
                            break;
                    }
                }
            } else {
                const existingElement = {
                    id: element.element_id as string,
                    name: element.element_name ? System.decryptDataWithUserKey(element.element_name, userKey) : '',
                    description: element.element_description ? System.decryptDataWithUserKey(element.element_description, userKey) : ''
                };
                
                switch (element.element_type) {
                    case 1:
                        existWorld.laws.push(existingElement);
                        break;
                    case 2:
                        existWorld.biomes.push(existingElement);
                        break;
                    case 3:
                        existWorld.issues.push(existingElement);
                        break;
                    case 4:
                        existWorld.customs.push(existingElement);
                        break;
                    case 5:
                        existWorld.kingdoms.push(existingElement);
                        break;
                    case 6:
                        existWorld.climate.push(existingElement);
                        break;
                    case 7:
                        existWorld.resources.push(existingElement);
                        break;
                    case 8:
                        existWorld.wildlife.push(existingElement);
                        break;
                    case 9:
                        existWorld.arts.push(existingElement);
                        break;
                    case 10:
                        existWorld.ethnicGroups.push(existingElement);
                        break;
                    case 11:
                        existWorld.socialClasses.push(existingElement);
                        break;
                    case 12:
                        existWorld.importantCharacters.push(existingElement);
                        break;
                }
            }
        }
        return worlds;
    }
    
    public static updateWorld(userId: string, world: WorldProps, lang: 'fr' | 'en' = 'fr'): boolean {
        const userKey: string = getUserEncryptionKey(userId);
        const encryptName: string = world.name ? System.encryptDataWithUserKey(world.name, userKey) : '';
        const encryptHistory: string = world.history ? System.encryptDataWithUserKey(world.history, userKey) : '';
        const encryptPolitics: string = world.politics ? System.encryptDataWithUserKey(world.politics, userKey) : '';
        const encryptEconomy: string = world.economy ? System.encryptDataWithUserKey(world.economy, userKey) : '';
        const encryptReligion: string = world.religion ? System.encryptDataWithUserKey(world.religion, userKey) : '';
        const encryptLanguages: string = world.languages ? System.encryptDataWithUserKey(world.languages, userKey) : '';
        let elements: WorldElementValue[] = [];
        const elementTypes: { key: keyof WorldProps, elements: WorldElement[] }[] = [
            {key: 'laws', elements: world.laws},
            {key: 'biomes', elements: world.biomes},
            {key: 'issues', elements: world.issues},
            {key: 'customs', elements: world.customs},
            {key: 'kingdoms', elements: world.kingdoms},
            {key: 'climate', elements: world.climate},
            {key: 'resources', elements: world.resources},
            {key: 'wildlife', elements: world.wildlife},
            {key: 'arts', elements: world.arts},
            {key: 'ethnicGroups', elements: world.ethnicGroups},
            {key: 'socialClasses', elements: world.socialClasses},
            {key: 'importantCharacters', elements: world.importantCharacters}
        ];

        elementTypes.forEach(({key, elements: elementsList}) => {
            elements = elements.concat(elementsList.map((element: WorldElement) => {
                const encryptedName: string = System.encryptDataWithUserKey(element.name, userKey);
                const hashedName: string = System.hashElement(element.name);
                const encryptedDescription: string = element.description ? System.encryptDataWithUserKey(element.description, userKey) : '';
                const elementType: number = Book.getElementTypes(key);

                return {
                    id: element.id,
                    name: encryptedName,
                    hashedName: hashedName,
                    description: encryptedDescription,
                    type: elementType
                };
            }));
        });

        BookRepo.updateWorld(userId, world.id, encryptName, System.hashElement(world.name), encryptHistory, encryptPolitics, encryptEconomy, encryptReligion, encryptLanguages, System.timeStampInSeconds(), lang);
        return BookRepo.updateWorldElements(userId, elements, lang);
    }

    public static addNewElementToWorld(userId: string, worldId: string, elementName: string, elementType: string, lang: 'fr' | 'en' = 'fr'): string {
        const userKey: string = getUserEncryptionKey(userId);
        const hashedName: string = System.hashElement(elementName);
        if (BookRepo.checkElementExist(worldId, hashedName, lang)) {
            throw new Error(lang === "fr" ? `Vous avez déjà un élément avec ce nom ${elementName}.` : `You already have an element named ${elementName}.`);
        }
        const elementTypeId: number = Book.getElementTypes(elementType);
        const encryptedName: string = System.encryptDataWithUserKey(elementName, userKey);
        const elementId: string = System.createUniqueId();
        return BookRepo.insertNewElement(userId, elementId, elementTypeId, worldId, encryptedName, hashedName, lang);
    }
    public static getElementTypes(elementType:string):number{
        switch (elementType){
            case 'laws':
                return 1;
            case 'biomes':
                return 2;
            case 'issues':
                return 3;
            case 'customs':
                return 4;
            case 'kingdoms':
                return 5;
            case 'climate':
                return 6;
            case 'resources':
                return 7;
            case 'wildlife':
                return 8;
            case 'arts':
                return 9;
            case 'ethnicGroups':
                return 10;
            case 'socialClasses':
                return 11;
            case 'importantCharacters':
                return 12;
            default:
                return 0;
        }
    }
    
    public static removeElementFromWorld(userId: string, elementId: string, lang: 'fr' | 'en' = 'fr'): boolean {
        return BookRepo.deleteElement(userId, elementId, lang);
    }

    public static removeBook(userId: string, bookId: string, lang: 'fr' | 'en' = 'fr'): boolean {
        return BookRepo.deleteBook(userId, bookId, lang);
    }

    static getGuideLineAI(userId: string, bookId: string, lang: 'fr' | 'en' = 'fr'): GuideLineAI {
        const userKey: string = getUserEncryptionKey(userId);
        try {
            const guideLine: GuideLineAIQuery = BookRepo.fetchGuideLineAI(userId, bookId, lang);
            return {
                narrativeType: guideLine.narrative_type,
                dialogueType: guideLine.dialogue_type,
                globalResume: guideLine.global_resume ? System.decryptDataWithUserKey(guideLine.global_resume, userKey) : '',
                atmosphere: guideLine.atmosphere ? System.decryptDataWithUserKey(guideLine.atmosphere, userKey) : '',
                verbeTense: guideLine.verbe_tense,
                themes: guideLine.themes ? System.decryptDataWithUserKey(guideLine.themes, userKey) : '',
                currentResume: guideLine.current_resume ? System.decryptDataWithUserKey(guideLine.current_resume, userKey) : '',
                langue: guideLine.langue
            }
        } catch (e: unknown) {
            if (e instanceof Error && e.message.includes('not found')) {
                return {
                    narrativeType: 0,
                    dialogueType: 0,
                    globalResume: '',
                    atmosphere: '',
                    verbeTense: 0,
                    themes: '',
                    currentResume: '',
                    langue: 0
                }
            }
            if (e instanceof Error) {
                throw new Error(e.message);
            } else {
                console.error(lang === 'fr' ? "Erreur inconnue lors de la récupération de la ligne directrice de l'IA." : "Unknown error while fetching AI guideline.");
                throw new Error(lang === 'fr' ? "Erreur inconnue lors de la récupération de la ligne directrice de l'IA." : "Unknown error while fetching AI guideline.");
            }
        }
    }

    public static setAIGuideLine(userId: string, bookId: string, narrativeType: number, dialogueType: number, plotSummary: string, toneAtmosphere: string, verbTense: number, language: number, themes: string, lang: 'fr' | 'en' = 'fr'): boolean {
        const userKey: string = getUserEncryptionKey(userId);
        const encryptedPlotSummary: string = plotSummary ? System.encryptDataWithUserKey(plotSummary, userKey) : '';
        const encryptedToneAtmosphere: string = toneAtmosphere ? System.encryptDataWithUserKey(toneAtmosphere, userKey) : '';
        const encryptedThemes: string = themes ? System.encryptDataWithUserKey(themes, userKey) : '';
        return BookRepo.insertAIGuideLine(userId, bookId, narrativeType, dialogueType, encryptedPlotSummary, encryptedToneAtmosphere, verbTense, language, encryptedThemes, lang);
    }
    
    public getId(): string {
        return this.id;
    }
    
    public getAuthorId(): string {
        return this.authorId;
    }
    
    public getTitle(): string {
        return this.title;
    }
    
    public getSubTitle(): string {
        return this.subTitle;
    }
    
    public getSummary(): string {
        return this.summary;
    }
    
    public getSerieId(): number {
        return this.serieId;
    }
    
    public getDesiredReleaseDate(): string {
        return this.desiredReleaseDate;
    }
    
    public getDesiredWordCount(): number {
        return this.desiredWordCount;
    }
    
    public getWordCount(): number {
        return this.wordCount;
    }
    
    public getCover(): string {
        return this.cover;
    }
    
    public getType(): string {
        return this.type;
    }
    
    static completeBookData(userId: string, id: string, lang: 'fr' | 'en' = 'fr'): CompleteBookData {
        const data = BookRepo.fetchBook(id, userId, lang);
        const chapters: ChapterBookResult[] = BookRepo.fetchCompleteBookChapters(id, lang);
        const userKey: string = getUserEncryptionKey(userId);
        const userInfos = UserRepo.fetchAccountInformation(userId, lang);

        const bookTitle: string = data.title ? System.decryptDataWithUserKey(data.title, userKey) : '';
        const decryptedChapters: any[] = [];

        for (const chapter of chapters) {
            decryptedChapters.push({
                id: '',
                title: chapter.title ? System.decryptDataWithUserKey(chapter.title, userKey) : '',
                content: chapter.content ? System.decryptDataWithUserKey(chapter.content, userKey) : '',
                order: chapter.chapter_order
            })
        }
        const coverImage: string = data.cover_image ? Book.getPicture(userId, userKey, data.cover_image, lang) : '';
        return {
            bookId: id,
            title: bookTitle,
            subTitle: data.sub_title ? System.decryptDataWithUserKey(data.sub_title, userKey) : '',
            summary: data.summary ? System.decryptDataWithUserKey(data.summary, userKey) : '',
            coverImage: coverImage,
            userInfos: {
                firstName: userInfos.first_name ? System.decryptDataWithUserKey(userInfos.first_name, userKey) : '',
                lastName: userInfos.last_name ? System.decryptDataWithUserKey(userInfos.last_name, userKey) : '',
                authorName: userInfos.author_name ? System.decryptDataWithUserKey(userInfos.author_name, userKey) : '',
            },
            chapters: decryptedChapters
        };
    }
    
    static getChaptersOrSheet(bookChapters: CompleteChapterContent[]): ChapterContentData[] {
        const chapters: ChapterContentData[] = [];
        const haveSheet: CompleteChapterContent | undefined = bookChapters.find((chapter: CompleteChapterContent): boolean => chapter.order === -1);
        const haveChapter: CompleteChapterContent | undefined = bookChapters.find((chapter: CompleteChapterContent): boolean => chapter.order > 0);
        if (haveSheet && !haveChapter) {
            chapters.push({
                title: haveSheet.title,
                chapterOrder: haveSheet.order,
                content: System.htmlToText(Chapter.tipTapToHtml(JSON.parse(haveSheet.content))),
                wordsCount: 0,
                version: haveSheet.version || 0
            });
        } else if (haveChapter) {
            for (const chapter of bookChapters) {
                if (chapter.order < 0) continue;
                chapters.push({
                    title: chapter.title,
                    chapterOrder: chapter.order,
                    content: System.htmlToText(Chapter.tipTapToHtml(JSON.parse(chapter.content))),
                    wordsCount: 0,
                    version: chapter.version || 0
                });
            }
        }
        return chapters;
    }

    static getAllChapters(userId: string, bookId: string, lang: 'fr' | 'en' = 'fr'): ChapterContentData[] {
        try {
            const book: CompleteBookData = Book.completeBookData(userId, bookId, lang);
            return Book.getChaptersOrSheet(book.chapters);
        } catch (e: unknown) {
            return [];
        }
    }
    
    public getBookInfos(userId: string, lang: 'fr' | 'en' = 'fr'): void {
        const book: BookQuery = BookRepo.fetchBook(this.id, userId, lang);
        const userKey: string = getUserEncryptionKey(userId);
        if (book) {
            this.authorId = book.author_id;
            this.type = book.type;
            this.title = book.title ? System.decryptDataWithUserKey(book.title, userKey) : '';
            this.subTitle = book.sub_title ? System.decryptDataWithUserKey(book.sub_title, userKey) : '';
            this.summary = book.summary ? System.decryptDataWithUserKey(book.summary, userKey) : '';
            this.serieId = book.serie_id ?? 0;
            this.desiredReleaseDate = book.desired_release_date ?? '';
            this.desiredWordCount = book.desired_word_count ?? 0;
            this.wordCount = book.words_count ?? 0;
            this.cover = book.cover_image ? Book.getPicture(userId, userKey, book.cover_image, lang) : '';
        } else {
            this.authorId = '';
            this.title = '';
            this.subTitle = '';
            this.summary = '';
            this.serieId = 0;
            this.desiredReleaseDate = '';
            this.wordCount = 0;
            this.cover = '';
        }
    }
    
    static async getSyncedBooks(userId: string, lang: 'fr' | 'en'):Promise<SyncedBook[]> {
        const userKey: string = getUserEncryptionKey(userId);
        
        const [
            allBooks,
            allChapters,
            allChapterContents,
            allChapterInfos,
            allCharacters,
            allCharacterAttributes,
            allLocations,
            allLocationElements,
            allLocationSubElements,
            allWorlds,
            allWorldElements,
            allIncidents,
            allPlotPoints,
            allIssues,
            allActSummaries,
            allGuidelines,
            allAIGuidelines
        ]: [
            SyncedBookResult[],
            SyncedChapterResult[],
            SyncedChapterContentResult[],
            SyncedChapterInfoResult[],
            SyncedCharacterResult[],
            SyncedCharacterAttributeResult[],
            SyncedLocationResult[],
            SyncedLocationElementResult[],
            SyncedLocationSubElementResult[],
            SyncedWorldResult[],
            SyncedWorldElementResult[],
            SyncedIncidentResult[],
            SyncedPlotPointResult[],
            SyncedIssueResult[],
            SyncedActSummaryResult[],
            SyncedGuideLineResult[],
            SyncedAIGuideLineResult[]
        ] = await Promise.all([
            BookRepo.fetchSyncedBooks(userId,lang),
            BookRepo.fetchSyncedChapters(userId,lang),
            BookRepo.fetchSyncedChapterContents(userId,lang),
            BookRepo.fetchSyncedChapterInfos(userId,lang),
            BookRepo.fetchSyncedCharacters(userId,lang),
            BookRepo.fetchSyncedCharacterAttributes(userId,lang),
            BookRepo.fetchSyncedLocations(userId,lang),
            BookRepo.fetchSyncedLocationElements(userId,lang),
            BookRepo.fetchSyncedLocationSubElements(userId,lang),
            BookRepo.fetchSyncedWorlds(userId,lang),
            BookRepo.fetchSyncedWorldElements(userId,lang),
            BookRepo.fetchSyncedIncidents(userId,lang),
            BookRepo.fetchSyncedPlotPoints(userId,lang),
            BookRepo.fetchSyncedIssues(userId,lang),
            BookRepo.fetchSyncedActSummaries(userId,lang),
            BookRepo.fetchSyncedGuideLine(userId,lang),
            BookRepo.fetchSyncedAIGuideLine(userId,lang)
        ]);
        
        return allBooks.map((book: SyncedBookResult): SyncedBook => {
            const bookId: string = book.book_id;
            
            const chapters: SyncedChapter[] = allChapters
                .filter((chapter: SyncedChapterResult): boolean => chapter.book_id === bookId)
                .map((chapter: SyncedChapterResult): SyncedChapter => {
                    const chapterId: string = chapter.chapter_id;
                    
                    const contents: SyncedChapterContent[] = allChapterContents
                        .filter((content: SyncedChapterContentResult): boolean => content.chapter_id === chapterId)
                        .map((content: SyncedChapterContentResult): SyncedChapterContent => ({
                            id: content.content_id,
                            lastUpdate: content.last_update
                        }));
                    
                    const infoData: SyncedChapterInfoResult | undefined = allChapterInfos.find((info: SyncedChapterInfoResult): boolean => info.chapter_id === chapterId);
                    const info: SyncedChapterInfo | null = infoData ? {
                        id: infoData.chapter_info_id,
                        lastUpdate: infoData.last_update
                    } : null;
                    
                    return {
                        id: chapterId,
                        name: System.decryptDataWithUserKey(chapter.title, userKey),
                        lastUpdate: chapter.last_update,
                        contents,
                        info
                    };
                });
            
            const characters: SyncedCharacter[] = allCharacters
                .filter((character: SyncedCharacterResult): boolean => character.book_id === bookId)
                .map((character: SyncedCharacterResult): SyncedCharacter => {
                    const characterId: string = character.character_id;
                    
                    const attributes: SyncedCharacterAttribute[] = allCharacterAttributes
                        .filter((attribute: SyncedCharacterAttributeResult): boolean => attribute.character_id === characterId)
                        .map((attribute: SyncedCharacterAttributeResult): SyncedCharacterAttribute => ({
                            id: attribute.attr_id,
                            name: System.decryptDataWithUserKey(attribute.attribute_name, userKey),
                            lastUpdate: attribute.last_update
                        }));
                    
                    return {
                        id: characterId,
                        name: System.decryptDataWithUserKey(character.first_name, userKey),
                        lastUpdate: character.last_update,
                        attributes
                    };
                });
            
            const locations: SyncedLocation[] = allLocations
                .filter((location: SyncedLocationResult): boolean => location.book_id === bookId)
                .map((location: SyncedLocationResult): SyncedLocation => {
                    const locationId: string = location.loc_id;
                    
                    const elements: SyncedLocationElement[] = allLocationElements
                        .filter((element: SyncedLocationElementResult): boolean => element.location === locationId)
                        .map((element: SyncedLocationElementResult): SyncedLocationElement => {
                            const elementId: string = element.element_id;
                            
                            const subElements: SyncedLocationSubElement[] = allLocationSubElements
                                .filter((subElement: SyncedLocationSubElementResult): boolean => subElement.element_id === elementId)
                                .map((subElement: SyncedLocationSubElementResult): SyncedLocationSubElement => ({
                                    id: subElement.sub_element_id,
                                    name: System.decryptDataWithUserKey(subElement.sub_elem_name, userKey),
                                    lastUpdate: subElement.last_update
                                }));
                            
                            return {
                                id: elementId,
                                name: System.decryptDataWithUserKey(element.element_name, userKey),
                                lastUpdate: element.last_update,
                                subElements
                            };
                        });
                    
                    return {
                        id: locationId,
                        name: System.decryptDataWithUserKey(location.loc_name, userKey),
                        lastUpdate: location.last_update,
                        elements
                    };
                });
            
            const worlds: SyncedWorld[] = allWorlds
                .filter((world: SyncedWorldResult): boolean => world.book_id === bookId)
                .map((world: SyncedWorldResult): SyncedWorld => {
                    const worldId: string = world.world_id;
                    
                    const elements: SyncedWorldElement[] = allWorldElements
                        .filter((worldElement: SyncedWorldElementResult): boolean => worldElement.world_id === worldId)
                        .map((worldElement: SyncedWorldElementResult): SyncedWorldElement => ({
                            id: worldElement.element_id,
                            name: System.decryptDataWithUserKey(worldElement.name, userKey),
                            lastUpdate: worldElement.last_update
                        }));
                    
                    return {
                        id: worldId,
                        name: System.decryptDataWithUserKey(world.name, userKey),
                        lastUpdate: world.last_update,
                        elements
                    };
                });
            
            const incidents: SyncedIncident[] = allIncidents
                .filter((incident: SyncedIncidentResult): boolean => incident.book_id === bookId)
                .map((incident: SyncedIncidentResult): SyncedIncident => ({
                    id: incident.incident_id,
                    name: System.decryptDataWithUserKey(incident.title, userKey),
                    lastUpdate: incident.last_update
                }));
            
            const plotPoints: SyncedPlotPoint[] = allPlotPoints
                .filter((plotPoint: SyncedPlotPointResult): boolean => plotPoint.book_id === bookId)
                .map((plotPoint: SyncedPlotPointResult): SyncedPlotPoint => ({
                    id: plotPoint.plot_point_id,
                    name: System.decryptDataWithUserKey(plotPoint.title, userKey),
                    lastUpdate: plotPoint.last_update
                }));
            
            const issues: SyncedIssue[] = allIssues
                .filter((issue: SyncedIssueResult): boolean => issue.book_id === bookId)
                .map((issue: SyncedIssueResult): SyncedIssue => ({
                    id: issue.issue_id,
                    name: System.decryptDataWithUserKey(issue.name, userKey),
                    lastUpdate: issue.last_update
                }));
            
            const actSummaries: SyncedActSummary[] = allActSummaries
                .filter((actSummary: SyncedActSummaryResult): boolean => actSummary.book_id === bookId)
                .map((actSummary: SyncedActSummaryResult): SyncedActSummary => ({
                    id: actSummary.act_sum_id,
                    lastUpdate: actSummary.last_update
                }));
            
            const guidelineData: SyncedGuideLineResult | undefined = allGuidelines.find((guideline: SyncedGuideLineResult): boolean => guideline.book_id === bookId);
            const guideLine: SyncedGuideLine | null = guidelineData ? {
                lastUpdate: guidelineData.last_update
            } : null;
            
            const aiGuidelineData: SyncedAIGuideLineResult | undefined = allAIGuidelines.find((aiGuideline: SyncedAIGuideLineResult): boolean => aiGuideline.book_id === bookId);
            const aiGuideLine: SyncedAIGuideLine | null = aiGuidelineData ? {
                lastUpdate: aiGuidelineData.last_update
            } : null;
            
            return {
                id: bookId,
                type: book.type,
                title: System.decryptDataWithUserKey(book.title, userKey),
                subTitle: book.sub_title ? System.decryptDataWithUserKey(book.sub_title, userKey) : null,
                lastUpdate: book.last_update,
                chapters,
                characters,
                locations,
                worlds,
                incidents,
                plotPoints,
                issues,
                actSummaries,
                guideLine,
                aiGuideLine
            };
        });
    }
    
    static async saveCompleteBook(userId: string, data: CompleteBook, lang: "fr" | "en"):Promise<boolean> {
        const userKey: string = getUserEncryptionKey(userId);

        const book: EritBooksTable = data.eritBooks[0];
        const encryptedBookTitle: string = System.encryptDataWithUserKey(book.title, userKey);
        const encryptedBookSubTitle: string | null = book.sub_title ? System.encryptDataWithUserKey(book.sub_title, userKey) : null;
        const encryptedBookSummary: string | null = book.summary ? System.encryptDataWithUserKey(book.summary, userKey) : null;
        const encryptedBookCoverImage: string | null = book.cover_image ? System.encryptDataWithUserKey(book.cover_image, userKey) : null;

        const bookInserted: boolean = BookRepo.insertSyncBook(
            book.book_id,
            userId,
            book.type,
            encryptedBookTitle,
            book.hashed_title,
            encryptedBookSubTitle,
            book.hashed_sub_title,
            encryptedBookSummary,
            book.serie_id,
            book.desired_release_date,
            book.desired_word_count,
            book.words_count,
            encryptedBookCoverImage,
            book.last_update,
            lang
        );
        if (!bookInserted) return false;
        
        const chaptersInserted: boolean = data.chapters.every((chapter: BookChaptersTable): boolean => {
            const encryptedTitle: string = System.encryptDataWithUserKey(chapter.title, userKey);
            return BookRepo.insertSyncChapter(chapter.chapter_id, chapter.book_id, userId, encryptedTitle, chapter.hashed_title, chapter.words_count, chapter.chapter_order, chapter.last_update, lang);
        });
        if (!chaptersInserted) return false;
        
        const incidentsInserted: boolean = data.incidents.every((incident: BookIncidentsTable): boolean => {
            const encryptedIncidentTitle: string = System.encryptDataWithUserKey(incident.title, userKey);
            const encryptedIncidentSummary: string | null = incident.summary ? System.encryptDataWithUserKey(incident.summary, userKey) : null;
            return BookRepo.insertSyncIncident(incident.incident_id, userId, incident.book_id, encryptedIncidentTitle, incident.hashed_title, encryptedIncidentSummary, incident.last_update, lang);
        });
        if (!incidentsInserted) return false;
        
        const plotPointsInserted: boolean = data.plotPoints.every((plotPoint: BookPlotPointsTable): boolean => {
            const encryptedPlotTitle: string = System.encryptDataWithUserKey(plotPoint.title, userKey);
            const encryptedPlotSummary: string | null = plotPoint.summary ? System.encryptDataWithUserKey(plotPoint.summary, userKey) : null;
            return BookRepo.insertSyncPlotPoint(plotPoint.plot_point_id, encryptedPlotTitle, plotPoint.hashed_title, encryptedPlotSummary, plotPoint.linked_incident_id, userId, plotPoint.book_id, plotPoint.last_update, lang);
        });
        if (!plotPointsInserted) return false;
        
        const chapterContentsInserted: boolean = data.chapterContents.every((content: BookChapterContentTable): boolean => {
            const encryptedContent: string | null = content.content ? System.encryptDataWithUserKey(JSON.stringify(content.content), userKey) : null;
            return BookRepo.insertSyncChapterContent(content.content_id, content.chapter_id, userId, content.version, encryptedContent, content.words_count, content.time_on_it, content.last_update, lang);
        });
        if (!chapterContentsInserted) return false;
        
        const chapterInfosInserted: boolean = data.chapterInfos.every((info: BookChapterInfosTable): boolean => {
            const encryptedSummary: string | null = info.summary ? System.encryptDataWithUserKey(info.summary, userKey) : null;
            const encryptedGoal: string | null = info.goal ? System.encryptDataWithUserKey(info.goal, userKey) : null;
            return BookRepo.insertSyncChapterInfo(info.chapter_info_id, info.chapter_id, info.act_id, info.incident_id, info.plot_point_id, info.book_id, userId, encryptedSummary, encryptedGoal, info.last_update, lang);
        });
        if (!chapterInfosInserted) return false;
        
        const charactersInserted: boolean = data.characters.every((character: BookCharactersTable): boolean => {
            const encryptedFirstName: string = System.encryptDataWithUserKey(character.first_name, userKey);
            const encryptedLastName: string | null = character.last_name ? System.encryptDataWithUserKey(character.last_name, userKey) : null;
            const encryptedCategory: string = System.encryptDataWithUserKey(character.category, userKey);
            const encryptedCharTitle: string | null = character.title ? System.encryptDataWithUserKey(character.title, 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 encryptedCharHistory: string | null = character.history ? System.encryptDataWithUserKey(character.history, userKey) : null;
            return BookRepo.insertSyncCharacter(character.character_id, character.book_id, userId, encryptedFirstName, encryptedLastName, encryptedCategory, encryptedCharTitle, character.image, encryptedRole, encryptedBiography, encryptedCharHistory, character.last_update, lang);
        });
        if (!charactersInserted) return false;
        
        const characterAttributesInserted: boolean = data.characterAttributes.every((attr: BookCharactersAttributesTable): boolean => {
            const encryptedAttrName: string = System.encryptDataWithUserKey(attr.attribute_name, userKey);
            const encryptedAttrValue: string = System.encryptDataWithUserKey(attr.attribute_value, userKey);
            return BookRepo.insertSyncCharacterAttribute(attr.attr_id, attr.character_id, userId, encryptedAttrName, encryptedAttrValue, attr.last_update, lang);
        });
        if (!characterAttributesInserted) return false;
        
        const locationsInserted: boolean = data.locations.every((location: BookLocationTable): boolean => {
            const encryptedLocName: string = System.encryptDataWithUserKey(location.loc_name, userKey);
            return BookRepo.insertSyncLocation(location.loc_id, location.book_id, userId, encryptedLocName, location.loc_original_name, location.last_update, lang);
        });
        if (!locationsInserted) return false;
        
        const locationElementsInserted: boolean = data.locationElements.every((element: LocationElementTable): boolean => {
            const encryptedLocElemName: string = System.encryptDataWithUserKey(element.element_name, userKey);
            const encryptedLocElemDesc: string | null = element.element_description ? System.encryptDataWithUserKey(element.element_description, userKey) : null;
            return BookRepo.insertSyncLocationElement(element.element_id, element.location, userId, encryptedLocElemName, element.original_name, encryptedLocElemDesc, element.last_update, lang);
        });
        if (!locationElementsInserted) return false;
        
        const locationSubElementsInserted: boolean = data.locationSubElements.every((subElement: LocationSubElementTable): boolean => {
            const encryptedSubElemName: string = System.encryptDataWithUserKey(subElement.sub_elem_name, userKey);
            const encryptedSubElemDesc: string | null = subElement.sub_elem_description ? System.encryptDataWithUserKey(subElement.sub_elem_description, userKey) : null;
            return BookRepo.insertSyncLocationSubElement(subElement.sub_element_id, subElement.element_id, userId, encryptedSubElemName, subElement.original_name, encryptedSubElemDesc, subElement.last_update, lang);
        });
        if (!locationSubElementsInserted) return false;
        
        const worldsInserted: boolean = data.worlds.every((world: BookWorldTable): boolean => {
            const encryptedWorldName: string = System.encryptDataWithUserKey(world.name, userKey);
            const encryptedWorldHistory: string | null = world.history ? System.encryptDataWithUserKey(world.history, userKey) : null;
            const encryptedPolitics: string | null = world.politics ? System.encryptDataWithUserKey(world.politics, userKey) : null;
            const encryptedEconomy: string | null = world.economy ? System.encryptDataWithUserKey(world.economy, userKey) : null;
            const encryptedReligion: string | null = world.religion ? System.encryptDataWithUserKey(world.religion, userKey) : null;
            const encryptedLanguages: string | null = world.languages ? System.encryptDataWithUserKey(world.languages, userKey) : null;
            return BookRepo.insertSyncWorld(world.world_id, encryptedWorldName, world.hashed_name, userId, world.book_id, encryptedWorldHistory, encryptedPolitics, encryptedEconomy, encryptedReligion, encryptedLanguages, world.last_update, lang);
        });
        if (!worldsInserted) return false;
        
        const worldElementsInserted: boolean = data.worldElements.every((element: BookWorldElementsTable): boolean => {
            const encryptedElemName: string = System.encryptDataWithUserKey(element.name, userKey);
            const encryptedElemDesc: string | null = element.description ? System.encryptDataWithUserKey(element.description, userKey) : null;
            return BookRepo.insertSyncWorldElement(element.element_id, element.world_id, userId, element.element_type, encryptedElemName, element.original_name, encryptedElemDesc, element.last_update, lang);
        });
        if (!worldElementsInserted) return false;
        
        const actSummariesInserted: boolean = data.actSummaries.every((actSummary: BookActSummariesTable): boolean => {
            const encryptedSummary: string | null = actSummary.summary ? System.encryptDataWithUserKey(actSummary.summary, userKey) : null;
            return BookRepo.insertSyncActSummary(actSummary.act_sum_id, actSummary.book_id, userId, actSummary.act_index, encryptedSummary, actSummary.last_update, lang);
        });
        if (!actSummariesInserted) return false;
        
        const aiGuidelinesInserted: boolean = data.aiGuideLine.every((aiGuide: BookAIGuideLineTable): boolean => {
            const encryptedGlobalResume: string | null = aiGuide.global_resume ? System.encryptDataWithUserKey(aiGuide.global_resume, userKey) : null;
            const encryptedAIThemes: string | null = aiGuide.themes ? System.encryptDataWithUserKey(aiGuide.themes, userKey) : null;
            const encryptedAITone: string | null = aiGuide.tone ? System.encryptDataWithUserKey(aiGuide.tone, userKey) : null;
            const encryptedAIAtmosphere: string | null = aiGuide.atmosphere ? System.encryptDataWithUserKey(aiGuide.atmosphere, userKey) : null;
            const encryptedCurrentResume: string | null = aiGuide.current_resume ? System.encryptDataWithUserKey(aiGuide.current_resume, userKey) : null;
            return BookRepo.insertSyncAIGuideLine(userId, aiGuide.book_id, encryptedGlobalResume, encryptedAIThemes, aiGuide.verbe_tense, aiGuide.narrative_type, aiGuide.langue, aiGuide.dialogue_type, encryptedAITone, encryptedAIAtmosphere, encryptedCurrentResume, aiGuide.last_update, lang);
        });
        if (!aiGuidelinesInserted) return false;
        
        const guidelinesInserted: boolean = data.guideLine.every((guide: BookGuideLineTable): boolean => {
            const encryptedGuideTone: string | null = guide.tone ? System.encryptDataWithUserKey(guide.tone, userKey) : null;
            const encryptedGuideAtmosphere: string | null = guide.atmosphere ? System.encryptDataWithUserKey(guide.atmosphere, userKey) : null;
            const encryptedWritingStyle: string | null = guide.writing_style ? System.encryptDataWithUserKey(guide.writing_style, userKey) : null;
            const encryptedGuideThemes: string | null = guide.themes ? System.encryptDataWithUserKey(guide.themes, userKey) : null;
            const encryptedSymbolism: string | null = guide.symbolism ? System.encryptDataWithUserKey(guide.symbolism, userKey) : null;
            const encryptedMotifs: string | null = guide.motifs ? System.encryptDataWithUserKey(guide.motifs, userKey) : null;
            const encryptedNarrativeVoice: string | null = guide.narrative_voice ? System.encryptDataWithUserKey(guide.narrative_voice, userKey) : null;
            const encryptedPacing: string | null = guide.pacing ? System.encryptDataWithUserKey(guide.pacing, userKey) : null;
            const encryptedIntendedAudience: string | null = guide.intended_audience ? System.encryptDataWithUserKey(guide.intended_audience, userKey) : null;
            const encryptedKeyMessages: string | null = guide.key_messages ? System.encryptDataWithUserKey(guide.key_messages, userKey) : null;
            return BookRepo.insertSyncGuideLine(userId, guide.book_id, encryptedGuideTone, encryptedGuideAtmosphere, encryptedWritingStyle, encryptedGuideThemes, encryptedSymbolism, encryptedMotifs, encryptedNarrativeVoice, encryptedPacing, encryptedIntendedAudience, encryptedKeyMessages, guide.last_update, lang);
        });
        if (!guidelinesInserted) return false;
        
        return data.issues.every((issue: BookIssuesTable): boolean => {
            const encryptedIssueName: string = System.encryptDataWithUserKey(issue.name, userKey);
            return BookRepo.insertSyncIssue(issue.issue_id, userId, issue.book_id, encryptedIssueName, issue.hashed_issue_name, issue.last_update, lang);
        });
    }
    
    static async getCompleteSyncBook(userId: string, data: BookSyncCompare, lang: "fr" | "en"):Promise<CompleteBook> {
        const userKey: string = getUserEncryptionKey(userId);
        const bookData: EritBooksTable[] = [];
        const chaptersData: BookChaptersTable[] = [];
        const plotPointsData: BookPlotPointsTable[] = [];
        const incidentsData: BookIncidentsTable[] = [];
        const chapterContentsData: BookChapterContentTable[] = [];
        const chapterInfosData: BookChapterInfosTable[] = [];
        const charactersData: BookCharactersTable[] = [];
        const characterAttributesData: BookCharactersAttributesTable[] = [];
        const locationsData: BookLocationTable[] = [];
        const locationElementsData: LocationElementTable[] = [];
        const locationSubElementsData: LocationSubElementTable[] = [];
        const worldsData: BookWorldTable[] = [];
        const worldElementsData: BookWorldElementsTable[] = [];
        const actSummariesData: BookActSummariesTable[] = [];
        const guideLineData: BookGuideLineTable[] = [];
        const aiGuideLineData: BookAIGuideLineTable[] = [];
        const issuesData: BookIssuesTable[] = [];

        const actSummaries: string[] = data.actSummaries;
        const chapters: string[] = data.chapters;
        const plotPoints: string[] = data.plotPoints;
        const incidents: string[] = data.incidents;
        const chapterContents: string[] = data.chapterContents;
        const chapterInfos: string[] = data.chapterInfos;
        const characters: string[] = data.characters;
        const characterAttributes: string[] = data.characterAttributes;
        const locations: string[] = data.locations;
        const locationElements: string[] = data.locationElements;
        const locationSubElements: string[] = data.locationSubElements;
        const worlds: string[] = data.worlds;
        const worldElements: string[] = data.worldElements;
        const issues: string[] = data.issues;
        
        if (actSummaries && actSummaries.length > 0) {
            for (const id of actSummaries) {
                const actSummary: BookActSummariesTable[] = await BookRepo.fetchCompleteActSummaryById(id, lang);
                if (actSummary.length>0) {
                    const actSummaryData: BookActSummariesTable = actSummary[0];
                    actSummariesData.push({
                        ...actSummaryData,
                        summary: actSummaryData.summary ? System.decryptDataWithUserKey(actSummaryData.summary, userKey) : null
                    });
                }
            }
        }

        if (chapters && chapters.length > 0) {
            for (const id of chapters) {
                const chapter: BookChaptersTable[] = await BookRepo.fetchCompleteChapterById(id, lang);
                if (chapter.length>0) {
                    const chapterData: BookChaptersTable = chapter[0];
                    chaptersData.push({
                        ...chapterData,
                        title: System.decryptDataWithUserKey(chapterData.title, userKey)
                    });
                }
            }
        }

        if (plotPoints && plotPoints.length > 0) {
            for (const id of plotPoints) {
                const plotPoint: BookPlotPointsTable[] = await BookRepo.fetchCompletePlotPointById(id, lang);
                if (plotPoint.length>0) {
                    const plotPointData: BookPlotPointsTable = plotPoint[0];
                    plotPointsData.push({
                        ...plotPointData,
                        title: System.decryptDataWithUserKey(plotPointData.title, userKey),
                        summary: plotPointData.summary ? System.decryptDataWithUserKey(plotPointData.summary, userKey) : null
                    });
                }
            }
        }

        if (incidents && incidents.length > 0) {
            for (const id of incidents) {
                const incident: BookIncidentsTable[] = await BookRepo.fetchCompleteIncidentById(id, lang);
                if (incident.length>0) {
                    const incidentData: BookIncidentsTable = incident[0];
                    incidentsData.push({
                        ...incidentData,
                        title: System.decryptDataWithUserKey(incidentData.title, userKey),
                        summary: incidentData.summary ? System.decryptDataWithUserKey(incidentData.summary, userKey) : null
                    });
                }
            }
        }

        if (chapterContents && chapterContents.length > 0) {
            for (const id of chapterContents) {
                const chapterContent: BookChapterContentTable[] = await BookRepo.fetchCompleteChapterContentById(id, lang);
                if (chapterContent.length>0) {
                    const chapterContentData: BookChapterContentTable = chapterContent[0];
                    chapterContentsData.push({
                        ...chapterContentData,
                        content: chapterContentData.content ? JSON.parse(System.decryptDataWithUserKey(chapterContentData.content, userKey)) : null
                    });
                }
            }
        }

        if (chapterInfos && chapterInfos.length > 0) {
            for (const id of chapterInfos) {
                const chapterInfo: BookChapterInfosTable[] = await BookRepo.fetchCompleteChapterInfoById(id, lang);
                if (chapterInfo.length>0) {
                    const chapterInfoData: BookChapterInfosTable = chapterInfo[0];
                    chapterInfosData.push({
                        ...chapterInfoData,
                        summary: chapterInfoData.summary ? System.decryptDataWithUserKey(chapterInfoData.summary, userKey) : null,
                        goal: chapterInfoData.goal ? System.decryptDataWithUserKey(chapterInfoData.goal, userKey) : null
                    });
                }
            }
        }

        if (characters && characters.length > 0) {
            for (const id of characters) {
                const character: BookCharactersTable[] = await BookRepo.fetchCompleteCharacterById(id, lang);
                if (character.length>0) {
                    const characterData: BookCharactersTable = character[0];
                    charactersData.push({
                        ...characterData,
                        first_name: System.decryptDataWithUserKey(characterData.first_name, userKey),
                        last_name: characterData.last_name ? System.decryptDataWithUserKey(characterData.last_name, userKey) : null,
                        category: System.decryptDataWithUserKey(characterData.category, userKey),
                        title: characterData.title ? System.decryptDataWithUserKey(characterData.title, userKey) : null,
                        role: characterData.role ? System.decryptDataWithUserKey(characterData.role, userKey) : null,
                        biography: characterData.biography ? System.decryptDataWithUserKey(characterData.biography, userKey) : null,
                        history: characterData.history ? System.decryptDataWithUserKey(characterData.history, userKey) : null
                    });
                }
            }
        }

        if (characterAttributes && characterAttributes.length > 0) {
            for (const id of characterAttributes) {
                const characterAttribute: BookCharactersAttributesTable[] = await BookRepo.fetchCompleteCharacterAttributeById(id, lang);
                if (characterAttribute.length>0) {
                    const characterAttributeData: BookCharactersAttributesTable = characterAttribute[0];
                    characterAttributesData.push({
                        ...characterAttributeData,
                        attribute_name: System.decryptDataWithUserKey(characterAttributeData.attribute_name, userKey),
                        attribute_value: System.decryptDataWithUserKey(characterAttributeData.attribute_value, userKey)
                    });
                }
            }
        }

        if (locations && locations.length > 0) {
            for (const id of locations) {
                const location: BookLocationTable[] = await BookRepo.fetchCompleteLocationById(id, lang);
                if (location.length>0) {
                    const locationData: BookLocationTable = location[0];
                    locationsData.push({
                        ...locationData,
                        loc_name: System.decryptDataWithUserKey(locationData.loc_name, userKey)
                    });
                }
            }
        }

        if (locationElements && locationElements.length > 0) {
            for (const id of locationElements) {
                const locationElement: LocationElementTable[] = await BookRepo.fetchCompleteLocationElementById(id, lang);
                if (locationElement.length>0) {
                    const locationElementData: LocationElementTable = locationElement[0];
                    locationElementsData.push({
                        ...locationElementData,
                        element_name: System.decryptDataWithUserKey(locationElementData.element_name, userKey),
                        element_description: locationElementData.element_description ? System.decryptDataWithUserKey(locationElementData.element_description, userKey) : null
                    });
                }
            }
        }

        if (locationSubElements && locationSubElements.length > 0) {
            for (const id of locationSubElements) {
                const locationSubElement: LocationSubElementTable[] = await BookRepo.fetchCompleteLocationSubElementById(id, lang);
                if (locationSubElement.length>0) {
                    const locationSubElementData: LocationSubElementTable = locationSubElement[0];
                    locationSubElementsData.push({
                        ...locationSubElementData,
                        sub_elem_name: System.decryptDataWithUserKey(locationSubElementData.sub_elem_name, userKey),
                        sub_elem_description: locationSubElementData.sub_elem_description ? System.decryptDataWithUserKey(locationSubElementData.sub_elem_description, userKey) : null
                    });
                }
            }
        }

        if (worlds && worlds.length > 0) {
            for (const id of worlds) {
                const world: BookWorldTable[] = await BookRepo.fetchCompleteWorldById(id, lang);
                if (world.length>0) {
                    const worldData: BookWorldTable = world[0];
                    worldsData.push({
                        ...worldData,
                        name: System.decryptDataWithUserKey(worldData.name, userKey),
                        history: worldData.history ? System.decryptDataWithUserKey(worldData.history, userKey) : null,
                        politics: worldData.politics ? System.decryptDataWithUserKey(worldData.politics, userKey) : null,
                        economy: worldData.economy ? System.decryptDataWithUserKey(worldData.economy, userKey) : null,
                        religion: worldData.religion ? System.decryptDataWithUserKey(worldData.religion, userKey) : null,
                        languages: worldData.languages ? System.decryptDataWithUserKey(worldData.languages, userKey) : null
                    });
                }
            }
        }

        if (worldElements && worldElements.length > 0) {
            for (const id of worldElements) {
                const worldElement: BookWorldElementsTable[] = await BookRepo.fetchCompleteWorldElementById(id, lang);
                if (worldElement.length>0) {
                    const worldElementData: BookWorldElementsTable = worldElement[0];
                    worldElementsData.push({
                        ...worldElementData,
                        name: System.decryptDataWithUserKey(worldElementData.name, userKey),
                        description: worldElementData.description ? System.decryptDataWithUserKey(worldElementData.description, userKey) : null
                    });
                }
            }
        }

        if (issues && issues.length > 0) {
            for (const id of issues) {
                const issue: BookIssuesTable[] = await BookRepo.fetchCompleteIssueById(id, lang);
                if (issue.length>0) {
                    const issueData: BookIssuesTable = issue[0];
                    issuesData.push({
                        ...issueData,
                        name: System.decryptDataWithUserKey(issueData.name, userKey)
                    });
                }
            }
        }
        console.log(data.id)
        const book: EritBooksTable[] = await BookRepo.fetchCompleteBookById(data.id, lang);
        if (book.length>0) {
            const bookDataItem: EritBooksTable = book[0];
            bookData.push({
                ...bookDataItem,
                title: System.decryptDataWithUserKey(bookDataItem.title, userKey),
                sub_title: bookDataItem.sub_title ? System.decryptDataWithUserKey(bookDataItem.sub_title, userKey) : null,
                summary: bookDataItem.summary ? System.decryptDataWithUserKey(bookDataItem.summary, userKey) : null,
                cover_image: bookDataItem.cover_image ? System.decryptDataWithUserKey(bookDataItem.cover_image, userKey) : null
            });
        }
        return {
            eritBooks: bookData,
            chapters: chaptersData,
            plotPoints: plotPointsData,
            incidents: incidentsData,
            chapterContents: chapterContentsData,
            chapterInfos: chapterInfosData,
            characters: charactersData,
            characterAttributes: characterAttributesData,
            locations: locationsData,
            locationElements: locationElementsData,
            locationSubElements: locationSubElementsData,
            worlds: worldsData,
            worldElements: worldElementsData,
            actSummaries: actSummariesData,
            guideLine: guideLineData,
            aiGuideLine: aiGuideLineData,
            issues: issuesData
        };
    }
    
    static async syncBookFromServerToClient(userId:string,completeBook: CompleteBook,lang:"fr"|"en"):Promise<boolean> {
        const userKey: string = getUserEncryptionKey(userId);
        const actSummaries: BookActSummariesTable[] = completeBook.actSummaries;
        const chapters: BookChaptersTable[] = completeBook.chapters;
        const plotPoints: BookPlotPointsTable[] = completeBook.plotPoints;
        const incidents: BookIncidentsTable[] = completeBook.incidents;
        const chapterContents: BookChapterContentTable[] = completeBook.chapterContents;
        const chapterInfos: BookChapterInfosTable[] = completeBook.chapterInfos;
        const characters: BookCharactersTable[] = completeBook.characters;
        const characterAttributes: BookCharactersAttributesTable[] = completeBook.characterAttributes;
        const locations: BookLocationTable[] = completeBook.locations;
        const locationElements: LocationElementTable[] = completeBook.locationElements;
        const locationSubElements: LocationSubElementTable[] = completeBook.locationSubElements;
        const worlds: BookWorldTable[] = completeBook.worlds;
        const worldElements: BookWorldElementsTable[] = completeBook.worldElements;
        const issues: BookIssuesTable[] = completeBook.issues;
        
        const bookId: string = completeBook.eritBooks.length > 0 ? completeBook.eritBooks[0].book_id : '';
        
        if (actSummaries && actSummaries.length > 0) {
            for (const actSummary of actSummaries) {
                const summary: string = System.encryptDataWithUserKey(actSummary.summary ? actSummary.summary : '', userKey)
                const updated: boolean = BookRepo.updateActSummary(userId, bookId, actSummary.act_index, summary, actSummary.last_update, lang);
                if (!updated) {
                    return false;
                }
            }
        }
        
        if (chapters && chapters.length > 0) {
            for (const chapter of chapters) {
                const title: string = System.encryptDataWithUserKey(chapter.title, userKey)
                const updated: boolean = ChapterRepo.updateChapter(userId, chapter.chapter_id, title, chapter.hashed_title, chapter.chapter_order, chapter.last_update, lang);
                if (!updated) {
                    return false;
                }
            }
        }
        
        if (plotPoints && plotPoints.length > 0) {
            for (const plotPoint of plotPoints) {
                const title: string = System.encryptDataWithUserKey(plotPoint.title, userKey);
                const summary: string = System.encryptDataWithUserKey(plotPoint.summary ? plotPoint.summary : '', userKey);
                const updated: boolean = BookRepo.updatePlotPoint(userId, bookId, plotPoint.plot_point_id, title, plotPoint.hashed_title, summary, plotPoint.last_update,lang);
                if (!updated) {
                    return false;
                }
            }
        }
        
        if (incidents && incidents.length > 0) {
            for (const incident of incidents) {
                const title: string = System.encryptDataWithUserKey(incident.title, userKey);
                const summary: string = System.encryptDataWithUserKey(incident.summary ? incident.summary : '', userKey);
                const updated: boolean = BookRepo.updateIncident(userId, bookId, incident.incident_id, title, incident.hashed_title, summary, incident.last_update, lang);
                if (!updated) {
                    return false;
                }
            }
        }
        
        if (chapterContents && chapterContents.length > 0) {
            for (const chapterContent of chapterContents) {
                const content: string = System.encryptDataWithUserKey(chapterContent.content ? JSON.stringify(chapterContent.content) : '', userKey);
                const updated: boolean = ChapterRepo.updateChapterContent(userId, chapterContent.chapter_id, chapterContent.version, content, chapterContent.words_count, chapterContent.last_update, lang);
                if (!updated) {
                    return false;
                }
            }
        }
        
        if (chapterInfos && chapterInfos.length > 0) {
            for (const chapterInfo of chapterInfos) {
                const summary: string = System.encryptDataWithUserKey(chapterInfo.summary ? chapterInfo.summary : '', userKey);
                const goal: string = System.encryptDataWithUserKey(chapterInfo.goal ? chapterInfo.goal : '', userKey);
                const updated: boolean = ChapterRepo.updateChapterInfos(userId, chapterInfo.chapter_id, chapterInfo.act_id, bookId,chapterInfo.incident_id, chapterInfo.plot_point_id, summary, goal, chapterInfo.last_update, lang);
                if (!updated) {
                    return false;
                }
            }
        }
        
        if (characters && characters.length > 0) {
            for (const character of characters) {
                const firstName: string = System.encryptDataWithUserKey(character.first_name, userKey);
                const lastName: string = System.encryptDataWithUserKey(character.last_name ? character.last_name : '', userKey);
                const category: string = System.encryptDataWithUserKey(character.category, userKey);
                const title: string = System.encryptDataWithUserKey(character.title ? character.title : '', userKey);
                const role: string = System.encryptDataWithUserKey(character.role ? character.role : '', userKey);
                const biography: string = System.encryptDataWithUserKey(character.biography ? character.biography : '', userKey);
                const history: string = System.encryptDataWithUserKey(character.history ? character.history : '', userKey);
                const updated: boolean = CharacterRepo.updateCharacter(userId, character.character_id, firstName, lastName, title, category, character.image || '', role, biography, history, character.last_update, lang);
                if (!updated) {
                    return false;
                }
            }
        }
        
        if (characterAttributes && characterAttributes.length > 0) {
            for (const characterAttribute of characterAttributes) {
                const attributeName: string = System.encryptDataWithUserKey(characterAttribute.attribute_name, userKey);
                const attributeValue: string = System.encryptDataWithUserKey(characterAttribute.attribute_value, userKey);
                const updated: boolean = CharacterRepo.updateCharacterAttribute(userId, characterAttribute.attr_id, attributeName, attributeValue, characterAttribute.last_update, lang);
                if (!updated) {
                    return false;
                }
            }
        }
        
        if (locations && locations.length > 0) {
            for (const location of locations) {
                const locName: string = System.encryptDataWithUserKey(location.loc_name, userKey);
                const updated: boolean = LocationRepo.updateLocationSection(userId, location.loc_id, locName, location.loc_original_name, location.last_update, lang);
                if (!updated) {
                    return false;
                }
            }
        }
        
        if (locationElements && locationElements.length > 0) {
            for (const locationElement of locationElements) {
                const elementName: string = System.encryptDataWithUserKey(locationElement.element_name, userKey);
                const elementDescription: string = System.encryptDataWithUserKey(locationElement.element_description ? locationElement.element_description : '', userKey);
                const updated: boolean = LocationRepo.updateLocationElement(userId, locationElement.element_id, elementName,locationElement.original_name, elementDescription, locationElement.last_update, lang);
                if (!updated) {
                    return false;
                }
            }
        }
        
        if (locationSubElements && locationSubElements.length > 0) {
            for (const locationSubElement of locationSubElements) {
                const subElemName: string = System.encryptDataWithUserKey(locationSubElement.sub_elem_name, userKey);
                const subElemDescription: string = System.encryptDataWithUserKey(locationSubElement.sub_elem_description ? locationSubElement.sub_elem_description : '', userKey);
                const updated: boolean = LocationRepo.updateLocationSubElement(userId, locationSubElement.sub_element_id, subElemName, locationSubElement.original_name, subElemDescription, locationSubElement.last_update, lang);
                if (!updated) {
                    return false;
                }
            }
        }
        
        if (worlds && worlds.length > 0) {
            for (const world of worlds) {
                const name: string = System.encryptDataWithUserKey(world.name, userKey);
                const history: string = System.encryptDataWithUserKey(world.history ? world.history : '', userKey);
                const politics: string = System.encryptDataWithUserKey(world.politics ? world.politics : '', userKey);
                const economy: string = System.encryptDataWithUserKey(world.economy ? world.economy : '', userKey);
                const religion: string = System.encryptDataWithUserKey(world.religion ? world.religion : '', userKey);
                const languages: string = System.encryptDataWithUserKey(world.languages ? world.languages : '', userKey);
                const updated: boolean = BookRepo.updateWorld(userId, world.world_id, name, world.hashed_name, history, politics, economy, religion, languages, world.last_update, lang);
                if (!updated) {
                    return false;
                }
            }
        }
        
        if (worldElements && worldElements.length > 0) {
            for (const worldElement of worldElements) {
                const name: string = System.encryptDataWithUserKey(worldElement.name, userKey);
                const description: string = System.encryptDataWithUserKey(worldElement.description ? worldElement.description : '', userKey);
                const updated: boolean = BookRepo.updateWorldElement(userId, worldElement.element_id, name, description, worldElement.last_update, lang);
                if (!updated) {
                    return false;
                }
            }
        }
        
        if (issues && issues.length > 0) {
            for (const issue of issues) {
                const name: string = System.encryptDataWithUserKey(issue.name, userKey);
                const updated: boolean = BookRepo.updateIssue(userId, bookId, issue.issue_id, name, issue.hashed_issue_name, issue.last_update, lang);
                if (!updated) {
                    return false;
                }
            }
        }
        return true;
    }
}