import { getUserEncryptionKey } from "../keyManager.js";
import System from "../System.js";
import { BookSyncCompare, CompleteBook, SyncedBook, SyncedBookTools } from "./Book.js";
import { SyncedSpell, SyncedSpellTag } from "./Spell.js";
import SpellRepo, { BookSpellsTable, SyncedSpellResult } from "../repositories/spell.repo.js";
import SpellTagRepo, { BookSpellTagsTable, SyncedSpellTagResult } from "../repositories/spelltag.repo.js";
import BookRepo, { EritBooksTable, SyncedBookResult, BookToolsTable, SyncedBookToolsResult } from "../repositories/book.repository.js";
import ChapterRepo, {
    BookChapterInfosTable,
    BookChaptersTable,
    SyncedChapterInfoResult,
    SyncedChapterResult
} from "../repositories/chapter.repository.js";
import PlotPointRepository, { BookPlotPointsTable, SyncedPlotPointResult } from "../repositories/plotpoint.repository.js";
import IncidentRepository, { BookIncidentsTable, SyncedIncidentResult } from "../repositories/incident.repository.js";
import ChapterContentRepository, {
    BookChapterContentTable,
    SyncedChapterContentResult
} from "../repositories/chaptercontent.repository.js";
import CharacterRepo, {
    BookCharactersAttributesTable,
    BookCharactersTable,
    SyncedCharacterAttributeResult,
    SyncedCharacterResult
} from "../repositories/character.repository.js";
import LocationRepo, {
    BookLocationTable,
    LocationElementTable,
    LocationSubElementTable,
    SyncedLocationElementResult,
    SyncedLocationResult,
    SyncedLocationSubElementResult
} from "../repositories/location.repository.js";
import WorldRepository, {
    BookWorldElementsTable,
    BookWorldTable,
    SyncedWorldElementResult,
    SyncedWorldResult
} from "../repositories/world.repository.js";
import ActRepository, {
    BookActSummariesTable,
    SyncedActSummaryResult
} from "../repositories/act.repository.js";
import GuidelineRepo, {
    BookAIGuideLineTable,
    BookGuideLineTable,
    SyncedAIGuideLineResult,
    SyncedGuideLineResult
} from "../repositories/guideline.repository.js";
import IssueRepository, { BookIssuesTable, SyncedIssueResult } from "../repositories/issue.repository.js";
import { SyncedChapter, SyncedChapterContent, SyncedChapterInfo } from "./Chapter.js";
import { SyncedCharacter, SyncedCharacterAttribute } from "./Character.js";
import { SyncedLocation, SyncedLocationElement, SyncedLocationSubElement } from "./Location.js";
import { SyncedWorld, SyncedWorldElement } from "./World.js";
import { SyncedIncident } from "./Incident.js";
import { SyncedPlotPoint } from "./PlotPoint.js";
import { SyncedIssue } from "./Issue.js";
import { SyncedActSummary } from "./Act.js";
import { SyncedAIGuideLine, SyncedGuideLine } from "./GuideLine.js";
import {
    SyncedSeries,
    SyncedSeriesBook,
    SyncedSeriesCharacter,
    SyncedSeriesCharacterAttribute,
    SyncedSeriesWorld,
    SyncedSeriesWorldElement,
    SyncedSeriesLocation,
    SyncedSeriesLocationElement,
    SyncedSeriesLocationSubElement,
    SyncedSeriesSpell,
    SyncedSeriesSpellTag
} from "./Book.js";
import SeriesRepo, {
    SyncedSeriesResult,
    SyncedSeriesBookResult
} from "../repositories/series.repo.js";
import SeriesCharacterRepo, {
    SyncedSeriesCharacterResult,
    SyncedSeriesCharacterAttributeResult
} from "../repositories/series-character.repo.js";
import SeriesWorldRepo, {
    SyncedSeriesWorldResult,
    SyncedSeriesWorldElementResult
} from "../repositories/series-world.repo.js";
import SeriesLocationRepo, {
    SyncedSeriesLocationResult,
    SyncedSeriesLocationElementResult,
    SyncedSeriesLocationSubElementResult
} from "../repositories/series-location.repo.js";
import SeriesSpellRepo, {
    SyncedSeriesSpellResult,
    SyncedSeriesSpellTagResult
} from "../repositories/series-spell.repo.js";

/**
 * Handles synchronization operations between local database and remote server.
 * Provides methods to fetch, compare, and sync book data including all related entities.
 */
export default class Sync {
    /**
     * Retrieves a complete book with all its associated entities for synchronization.
     * Decrypts all encrypted fields using the user's encryption key.
     * @param userId - The unique identifier of the user
     * @param syncCompareData - Object containing IDs of entities to retrieve for sync comparison
     * @param lang - The language for error messages ('fr' or 'en')
     * @returns A promise resolving to a CompleteBook object with all decrypted data
     */
    static async getCompleteSyncBook(userId: string, syncCompareData: BookSyncCompare, lang: "fr" | "en"): Promise<CompleteBook> {
        const userEncryptionKey: string = getUserEncryptionKey(userId);
        const decryptedBooks: EritBooksTable[] = [];
        const decryptedChapters: BookChaptersTable[] = [];
        const decryptedPlotPoints: BookPlotPointsTable[] = [];
        const decryptedIncidents: BookIncidentsTable[] = [];
        const decryptedChapterContents: BookChapterContentTable[] = [];
        const decryptedChapterInfos: BookChapterInfosTable[] = [];
        const decryptedCharacters: BookCharactersTable[] = [];
        const decryptedCharacterAttributes: BookCharactersAttributesTable[] = [];
        const decryptedLocations: BookLocationTable[] = [];
        const decryptedLocationElements: LocationElementTable[] = [];
        const decryptedLocationSubElements: LocationSubElementTable[] = [];
        const decryptedWorlds: BookWorldTable[] = [];
        const decryptedWorldElements: BookWorldElementsTable[] = [];
        const decryptedActSummaries: BookActSummariesTable[] = [];
        const decryptedGuideLines: BookGuideLineTable[] = [];
        const decryptedAIGuideLines: BookAIGuideLineTable[] = [];
        const decryptedIssues: BookIssuesTable[] = [];
        const decryptedSpells: BookSpellsTable[] = [];
        const decryptedSpellTags: BookSpellTagsTable[] = [];

        const actSummaryIds: string[] = syncCompareData.actSummaries;
        const chapterIds: string[] = syncCompareData.chapters;
        const plotPointIds: string[] = syncCompareData.plotPoints;
        const incidentIds: string[] = syncCompareData.incidents;
        const chapterContentIds: string[] = syncCompareData.chapterContents;
        const chapterInfoIds: string[] = syncCompareData.chapterInfos;
        const characterIds: string[] = syncCompareData.characters;
        const characterAttributeIds: string[] = syncCompareData.characterAttributes;
        const locationIds: string[] = syncCompareData.locations;
        const locationElementIds: string[] = syncCompareData.locationElements;
        const locationSubElementIds: string[] = syncCompareData.locationSubElements;
        const worldIds: string[] = syncCompareData.worlds;
        const worldElementIds: string[] = syncCompareData.worldElements;
        const issueIds: string[] = syncCompareData.issues;
        const spellIds: string[] = syncCompareData.spells;
        const spellTagIds: string[] = syncCompareData.spellTags;

        if (actSummaryIds && actSummaryIds.length > 0) {
            for (const actSummaryId of actSummaryIds) {
                const actSummaryResults: BookActSummariesTable[] = await ActRepository.fetchCompleteActSummaryById(actSummaryId, lang);
                if (actSummaryResults.length > 0) {
                    const actSummaryRecord: BookActSummariesTable = actSummaryResults[0];
                    decryptedActSummaries.push({
                        ...actSummaryRecord,
                        summary: actSummaryRecord.summary ? System.decryptDataWithUserKey(actSummaryRecord.summary, userEncryptionKey) : null
                    });
                }
            }
        }

        if (chapterIds && chapterIds.length > 0) {
            for (const chapterId of chapterIds) {
                const chapterResults: BookChaptersTable[] = await ChapterRepo.fetchCompleteChapterById(chapterId, lang);
                if (chapterResults.length > 0) {
                    const chapterRecord: BookChaptersTable = chapterResults[0];
                    decryptedChapters.push({
                        ...chapterRecord,
                        title: System.decryptDataWithUserKey(chapterRecord.title, userEncryptionKey)
                    });
                }
            }
        }

        if (plotPointIds && plotPointIds.length > 0) {
            for (const plotPointId of plotPointIds) {
                const plotPointResults: BookPlotPointsTable[] = await PlotPointRepository.fetchCompletePlotPointById(plotPointId, lang);
                if (plotPointResults.length > 0) {
                    const plotPointRecord: BookPlotPointsTable = plotPointResults[0];
                    decryptedPlotPoints.push({
                        ...plotPointRecord,
                        title: System.decryptDataWithUserKey(plotPointRecord.title, userEncryptionKey),
                        summary: plotPointRecord.summary ? System.decryptDataWithUserKey(plotPointRecord.summary, userEncryptionKey) : null
                    });
                }
            }
        }

        if (incidentIds && incidentIds.length > 0) {
            for (const incidentId of incidentIds) {
                const incidentResults: BookIncidentsTable[] = await IncidentRepository.fetchCompleteIncidentById(incidentId, lang);
                if (incidentResults.length > 0) {
                    const incidentRecord: BookIncidentsTable = incidentResults[0];
                    decryptedIncidents.push({
                        ...incidentRecord,
                        title: System.decryptDataWithUserKey(incidentRecord.title, userEncryptionKey),
                        summary: incidentRecord.summary ? System.decryptDataWithUserKey(incidentRecord.summary, userEncryptionKey) : null
                    });
                }
            }
        }

        if (chapterContentIds && chapterContentIds.length > 0) {
            for (const chapterContentId of chapterContentIds) {
                const chapterContentResults: BookChapterContentTable[] = await ChapterContentRepository.fetchCompleteChapterContentById(chapterContentId, lang);
                if (chapterContentResults.length > 0) {
                    const chapterContentRecord: BookChapterContentTable = chapterContentResults[0];
                    decryptedChapterContents.push({
                        ...chapterContentRecord,
                        content: chapterContentRecord.content ? JSON.parse(System.decryptDataWithUserKey(chapterContentRecord.content, userEncryptionKey)) : null
                    });
                }
            }
        }

        if (chapterInfoIds && chapterInfoIds.length > 0) {
            for (const chapterInfoId of chapterInfoIds) {
                const chapterInfoResults: BookChapterInfosTable[] = await ChapterRepo.fetchCompleteChapterInfoById(chapterInfoId, lang);
                if (chapterInfoResults.length > 0) {
                    const chapterInfoRecord: BookChapterInfosTable = chapterInfoResults[0];
                    decryptedChapterInfos.push({
                        ...chapterInfoRecord,
                        summary: chapterInfoRecord.summary ? System.decryptDataWithUserKey(chapterInfoRecord.summary, userEncryptionKey) : null,
                        goal: chapterInfoRecord.goal ? System.decryptDataWithUserKey(chapterInfoRecord.goal, userEncryptionKey) : null
                    });
                }
            }
        }

        if (characterIds && characterIds.length > 0) {
            for (const characterId of characterIds) {
                const characterResults: BookCharactersTable[] = await CharacterRepo.fetchCompleteCharacterById(characterId, lang);
                if (characterResults.length > 0) {
                    const characterRecord: BookCharactersTable = characterResults[0];
                    decryptedCharacters.push({
                        ...characterRecord,
                        first_name: System.decryptDataWithUserKey(characterRecord.first_name, userEncryptionKey),
                        last_name: characterRecord.last_name ? System.decryptDataWithUserKey(characterRecord.last_name, userEncryptionKey) : null,
                        nickname: characterRecord.nickname ? System.decryptDataWithUserKey(characterRecord.nickname, userEncryptionKey) : null,
                        age: characterRecord.age ? System.decryptDataWithUserKey(characterRecord.age, userEncryptionKey) : null,
                        gender: characterRecord.gender ? System.decryptDataWithUserKey(characterRecord.gender, userEncryptionKey) : null,
                        species: characterRecord.species ? System.decryptDataWithUserKey(characterRecord.species, userEncryptionKey) : null,
                        nationality: characterRecord.nationality ? System.decryptDataWithUserKey(characterRecord.nationality, userEncryptionKey) : null,
                        status: characterRecord.status ? System.decryptDataWithUserKey(characterRecord.status, userEncryptionKey) : null,
                        category: System.decryptDataWithUserKey(characterRecord.category, userEncryptionKey),
                        title: characterRecord.title ? System.decryptDataWithUserKey(characterRecord.title, userEncryptionKey) : null,
                        role: characterRecord.role ? System.decryptDataWithUserKey(characterRecord.role, userEncryptionKey) : null,
                        biography: characterRecord.biography ? System.decryptDataWithUserKey(characterRecord.biography, userEncryptionKey) : null,
                        history: characterRecord.history ? System.decryptDataWithUserKey(characterRecord.history, userEncryptionKey) : null,
                        speech_pattern: characterRecord.speech_pattern ? System.decryptDataWithUserKey(characterRecord.speech_pattern, userEncryptionKey) : null,
                        catchphrase: characterRecord.catchphrase ? System.decryptDataWithUserKey(characterRecord.catchphrase, userEncryptionKey) : null,
                        residence: characterRecord.residence ? System.decryptDataWithUserKey(characterRecord.residence, userEncryptionKey) : null,
                        notes: characterRecord.notes ? System.decryptDataWithUserKey(characterRecord.notes, userEncryptionKey) : null,
                        color: characterRecord.color ? System.decryptDataWithUserKey(characterRecord.color, userEncryptionKey) : null
                    });
                }
            }
        }

        if (characterAttributeIds && characterAttributeIds.length > 0) {
            for (const characterAttributeId of characterAttributeIds) {
                const characterAttributeResults: BookCharactersAttributesTable[] = await CharacterRepo.fetchCompleteCharacterAttributeById(characterAttributeId, lang);
                if (characterAttributeResults.length > 0) {
                    const characterAttributeRecord: BookCharactersAttributesTable = characterAttributeResults[0];
                    decryptedCharacterAttributes.push({
                        ...characterAttributeRecord,
                        attribute_name: System.decryptDataWithUserKey(characterAttributeRecord.attribute_name, userEncryptionKey),
                        attribute_value: System.decryptDataWithUserKey(characterAttributeRecord.attribute_value, userEncryptionKey)
                    });
                }
            }
        }

        if (locationIds && locationIds.length > 0) {
            for (const locationId of locationIds) {
                const locationResults: BookLocationTable[] = await LocationRepo.fetchCompleteLocationById(locationId, lang);
                if (locationResults.length > 0) {
                    const locationRecord: BookLocationTable = locationResults[0];
                    decryptedLocations.push({
                        ...locationRecord,
                        loc_name: System.decryptDataWithUserKey(locationRecord.loc_name, userEncryptionKey)
                    });
                }
            }
        }

        if (locationElementIds && locationElementIds.length > 0) {
            for (const locationElementId of locationElementIds) {
                const locationElementResults: LocationElementTable[] = await LocationRepo.fetchCompleteLocationElementById(locationElementId, lang);
                if (locationElementResults.length > 0) {
                    const locationElementRecord: LocationElementTable = locationElementResults[0];
                    decryptedLocationElements.push({
                        ...locationElementRecord,
                        element_name: System.decryptDataWithUserKey(locationElementRecord.element_name, userEncryptionKey),
                        element_description: locationElementRecord.element_description ? System.decryptDataWithUserKey(locationElementRecord.element_description, userEncryptionKey) : null
                    });
                }
            }
        }

        if (locationSubElementIds && locationSubElementIds.length > 0) {
            for (const locationSubElementId of locationSubElementIds) {
                const locationSubElementResults: LocationSubElementTable[] = await LocationRepo.fetchCompleteLocationSubElementById(locationSubElementId, lang);
                if (locationSubElementResults.length > 0) {
                    const locationSubElementRecord: LocationSubElementTable = locationSubElementResults[0];
                    decryptedLocationSubElements.push({
                        ...locationSubElementRecord,
                        sub_elem_name: System.decryptDataWithUserKey(locationSubElementRecord.sub_elem_name, userEncryptionKey),
                        sub_elem_description: locationSubElementRecord.sub_elem_description ? System.decryptDataWithUserKey(locationSubElementRecord.sub_elem_description, userEncryptionKey) : null
                    });
                }
            }
        }

        if (worldIds && worldIds.length > 0) {
            for (const worldId of worldIds) {
                const worldResults: BookWorldTable[] = await WorldRepository.fetchCompleteWorldById(worldId, lang);
                if (worldResults.length > 0) {
                    const worldRecord: BookWorldTable = worldResults[0];
                    decryptedWorlds.push({
                        ...worldRecord,
                        name: System.decryptDataWithUserKey(worldRecord.name, userEncryptionKey),
                        history: worldRecord.history ? System.decryptDataWithUserKey(worldRecord.history, userEncryptionKey) : null,
                        politics: worldRecord.politics ? System.decryptDataWithUserKey(worldRecord.politics, userEncryptionKey) : null,
                        economy: worldRecord.economy ? System.decryptDataWithUserKey(worldRecord.economy, userEncryptionKey) : null,
                        religion: worldRecord.religion ? System.decryptDataWithUserKey(worldRecord.religion, userEncryptionKey) : null,
                        languages: worldRecord.languages ? System.decryptDataWithUserKey(worldRecord.languages, userEncryptionKey) : null
                    });
                }
            }
        }

        if (worldElementIds && worldElementIds.length > 0) {
            for (const worldElementId of worldElementIds) {
                const worldElementResults: BookWorldElementsTable[] = await WorldRepository.fetchCompleteWorldElementById(worldElementId, lang);
                if (worldElementResults.length > 0) {
                    const worldElementRecord: BookWorldElementsTable = worldElementResults[0];
                    decryptedWorldElements.push({
                        ...worldElementRecord,
                        name: System.decryptDataWithUserKey(worldElementRecord.name, userEncryptionKey),
                        description: worldElementRecord.description ? System.decryptDataWithUserKey(worldElementRecord.description, userEncryptionKey) : null
                    });
                }
            }
        }

        if (issueIds && issueIds.length > 0) {
            for (const issueId of issueIds) {
                const issueResults: BookIssuesTable[] = await IssueRepository.fetchCompleteIssueById(issueId, lang);
                if (issueResults.length > 0) {
                    const issueRecord: BookIssuesTable = issueResults[0];
                    decryptedIssues.push({
                        ...issueRecord,
                        name: System.decryptDataWithUserKey(issueRecord.name, userEncryptionKey)
                    });
                }
            }
        }

        if (syncCompareData.guideLine) {
            const guidelineResults: BookGuideLineTable[] = await GuidelineRepo.fetchBookGuideLineTable(userId, syncCompareData.id, lang);
            if (guidelineResults.length > 0) {
                const guidelineRecord: BookGuideLineTable = guidelineResults[0];
                decryptedGuideLines.push({
                    ...guidelineRecord,
                    tone: guidelineRecord.tone ? System.decryptDataWithUserKey(guidelineRecord.tone, userEncryptionKey) : null,
                    atmosphere: guidelineRecord.atmosphere ? System.decryptDataWithUserKey(guidelineRecord.atmosphere, userEncryptionKey) : null,
                    writing_style: guidelineRecord.writing_style ? System.decryptDataWithUserKey(guidelineRecord.writing_style, userEncryptionKey) : null,
                    themes: guidelineRecord.themes ? System.decryptDataWithUserKey(guidelineRecord.themes, userEncryptionKey) : null,
                    symbolism: guidelineRecord.symbolism ? System.decryptDataWithUserKey(guidelineRecord.symbolism, userEncryptionKey) : null,
                    motifs: guidelineRecord.motifs ? System.decryptDataWithUserKey(guidelineRecord.motifs, userEncryptionKey) : null,
                    narrative_voice: guidelineRecord.narrative_voice ? System.decryptDataWithUserKey(guidelineRecord.narrative_voice, userEncryptionKey) : null,
                    pacing: guidelineRecord.pacing ? System.decryptDataWithUserKey(guidelineRecord.pacing, userEncryptionKey) : null,
                    intended_audience: guidelineRecord.intended_audience ? System.decryptDataWithUserKey(guidelineRecord.intended_audience, userEncryptionKey) : null,
                    key_messages: guidelineRecord.key_messages ? System.decryptDataWithUserKey(guidelineRecord.key_messages, userEncryptionKey) : null
                });
            }
        }

        if (syncCompareData.aiGuideLine) {
            const aiGuidelineResults: BookAIGuideLineTable[] = await GuidelineRepo.fetchBookAIGuideLine(userId, syncCompareData.id, lang);
            if (aiGuidelineResults.length > 0) {
                const aiGuidelineRecord: BookAIGuideLineTable = aiGuidelineResults[0];
                decryptedAIGuideLines.push({
                    ...aiGuidelineRecord,
                    global_resume: aiGuidelineRecord.global_resume ? System.decryptDataWithUserKey(aiGuidelineRecord.global_resume, userEncryptionKey) : null,
                    themes: aiGuidelineRecord.themes ? System.decryptDataWithUserKey(aiGuidelineRecord.themes, userEncryptionKey) : null,
                    tone: aiGuidelineRecord.tone ? System.decryptDataWithUserKey(aiGuidelineRecord.tone, userEncryptionKey) : null,
                    atmosphere: aiGuidelineRecord.atmosphere ? System.decryptDataWithUserKey(aiGuidelineRecord.atmosphere, userEncryptionKey) : null,
                    current_resume: aiGuidelineRecord.current_resume ? System.decryptDataWithUserKey(aiGuidelineRecord.current_resume, userEncryptionKey) : null
                });
            }
        }

        if (spellTagIds && spellTagIds.length > 0) {
            for (const spellTagId of spellTagIds) {
                const spellTagRecord: BookSpellTagsTable | null = SpellTagRepo.fetchSpellTagTableById(userId, spellTagId, lang);
                if (spellTagRecord) {
                    decryptedSpellTags.push({
                        ...spellTagRecord,
                        name: System.decryptDataWithUserKey(spellTagRecord.name, userEncryptionKey)
                    });
                }
            }
        }

        if (spellIds && spellIds.length > 0) {
            for (const spellId of spellIds) {
                const spellRecord: BookSpellsTable | null = SpellRepo.fetchSpellTableById(userId, spellId, lang);
                if (spellRecord) {
                    decryptedSpells.push({
                        ...spellRecord,
                        name: System.decryptDataWithUserKey(spellRecord.name, userEncryptionKey),
                        description: spellRecord.description ? System.decryptDataWithUserKey(spellRecord.description, userEncryptionKey) : null,
                        appearance: spellRecord.appearance ? System.decryptDataWithUserKey(spellRecord.appearance, userEncryptionKey) : null,
                        tags: spellRecord.tags ? System.decryptDataWithUserKey(spellRecord.tags, userEncryptionKey) : null,
                        power_level: spellRecord.power_level ? System.decryptDataWithUserKey(spellRecord.power_level, userEncryptionKey) : null,
                        components: spellRecord.components ? System.decryptDataWithUserKey(spellRecord.components, userEncryptionKey) : null,
                        limitations: spellRecord.limitations ? System.decryptDataWithUserKey(spellRecord.limitations, userEncryptionKey) : null,
                        notes: spellRecord.notes ? System.decryptDataWithUserKey(spellRecord.notes, userEncryptionKey) : null
                    });
                }
            }
        }

        const bookResults: EritBooksTable[] = await BookRepo.fetchCompleteBookById(syncCompareData.id, lang);
        if (bookResults.length > 0) {
            const bookRecord: EritBooksTable = bookResults[0];
            decryptedBooks.push({
                ...bookRecord,
                title: System.decryptDataWithUserKey(bookRecord.title, userEncryptionKey),
                sub_title: bookRecord.sub_title ? System.decryptDataWithUserKey(bookRecord.sub_title, userEncryptionKey) : null,
                summary: bookRecord.summary ? System.decryptDataWithUserKey(bookRecord.summary, userEncryptionKey) : null,
                cover_image: bookRecord.cover_image ? System.decryptDataWithUserKey(bookRecord.cover_image, userEncryptionKey) : null
            });
        }

        const bookToolsResult: BookToolsTable | null = BookRepo.fetchBookTools(userId, syncCompareData.id, lang);
        const bookTools: BookToolsTable[] = bookToolsResult ? [bookToolsResult] : [];

        return {
            eritBooks: decryptedBooks,
            chapters: decryptedChapters,
            plotPoints: decryptedPlotPoints,
            incidents: decryptedIncidents,
            chapterContents: decryptedChapterContents,
            chapterInfos: decryptedChapterInfos,
            characters: decryptedCharacters,
            characterAttributes: decryptedCharacterAttributes,
            locations: decryptedLocations,
            locationElements: decryptedLocationElements,
            locationSubElements: decryptedLocationSubElements,
            worlds: decryptedWorlds,
            worldElements: decryptedWorldElements,
            actSummaries: decryptedActSummaries,
            guideLine: decryptedGuideLines,
            aiGuideLine: decryptedAIGuideLines,
            issues: decryptedIssues,
            bookTools: bookTools,
            spells: decryptedSpells,
            spellTags: decryptedSpellTags
        };
    }

    /**
     * Synchronizes a complete book from the server to the local client database.
     * Encrypts all data before storing and handles both insert and update operations.
     * @param userId - The unique identifier of the user
     * @param completeBook - The complete book data received from the server
     * @param lang - The language for error messages ('fr' or 'en')
     * @returns A promise resolving to true if sync was successful, false otherwise
     */
    static async syncBookFromServerToClient(userId: string, completeBook: CompleteBook, lang: "fr" | "en"): Promise<boolean> {
        const userEncryptionKey: string = getUserEncryptionKey(userId);

        const serverActSummaries: BookActSummariesTable[] = completeBook.actSummaries;
        const serverChapters: BookChaptersTable[] = completeBook.chapters;
        const serverPlotPoints: BookPlotPointsTable[] = completeBook.plotPoints;
        const serverIncidents: BookIncidentsTable[] = completeBook.incidents;
        const serverChapterContents: BookChapterContentTable[] = completeBook.chapterContents;
        const serverChapterInfos: BookChapterInfosTable[] = completeBook.chapterInfos;
        const serverCharacters: BookCharactersTable[] = completeBook.characters;
        const serverCharacterAttributes: BookCharactersAttributesTable[] = completeBook.characterAttributes;
        const serverLocations: BookLocationTable[] = completeBook.locations;
        const serverLocationElements: LocationElementTable[] = completeBook.locationElements;
        const serverLocationSubElements: LocationSubElementTable[] = completeBook.locationSubElements;
        const serverWorlds: BookWorldTable[] = completeBook.worlds;
        const serverWorldElements: BookWorldElementsTable[] = completeBook.worldElements;
        const serverIssues: BookIssuesTable[] = completeBook.issues;
        const serverGuideLines: BookGuideLineTable[] = completeBook.guideLine;
        const serverAIGuideLines: BookAIGuideLineTable[] = completeBook.aiGuideLine;

        const bookId: string = completeBook.eritBooks.length > 0 ? completeBook.eritBooks[0].book_id : '';

        if (serverChapters && serverChapters.length > 0) {
            for (const serverChapter of serverChapters) {
                const chapterExists: boolean = ChapterRepo.isChapterExist(userId, serverChapter.chapter_id, lang);
                const encryptedTitle: string = System.encryptDataWithUserKey(serverChapter.title, userEncryptionKey);
                if (chapterExists) {
                    const updateSuccessful: boolean = ChapterRepo.updateChapter(userId, serverChapter.chapter_id, encryptedTitle, serverChapter.hashed_title, serverChapter.chapter_order, serverChapter.last_update, lang);
                    if (!updateSuccessful) {
                        return false;
                    }
                } else {
                    const insertSuccessful: boolean = ChapterRepo.insertSyncChapter(serverChapter.chapter_id, serverChapter.book_id, userId, encryptedTitle, serverChapter.hashed_title, serverChapter.words_count || 0, serverChapter.chapter_order, serverChapter.last_update, lang);
                    if (!insertSuccessful) {
                        return false;
                    }
                }
            }
        }

        if (serverActSummaries && serverActSummaries.length > 0) {
            for (const serverActSummary of serverActSummaries) {
                const actSummaryExists: boolean = ActRepository.actSummarizeExist(userId, bookId, serverActSummary.act_index, lang);
                const encryptedSummary: string = System.encryptDataWithUserKey(serverActSummary.summary ? serverActSummary.summary : '', userEncryptionKey);
                if (actSummaryExists) {
                    const updateSuccessful: boolean = ActRepository.updateActSummary(userId, bookId, serverActSummary.act_index, encryptedSummary, serverActSummary.last_update, lang);
                    if (!updateSuccessful) {
                        return false;
                    }
                } else {
                    const insertSuccessful: boolean = ActRepository.insertSyncActSummary(serverActSummary.act_sum_id, userId, bookId, serverActSummary.act_index, encryptedSummary, serverActSummary.last_update, lang);
                    if (!insertSuccessful) {
                        return false;
                    }
                }
            }
        }

        if (serverPlotPoints && serverPlotPoints.length > 0) {
            for (const serverPlotPoint of serverPlotPoints) {
                const encryptedTitle: string = System.encryptDataWithUserKey(serverPlotPoint.title, userEncryptionKey);
                const encryptedSummary: string = System.encryptDataWithUserKey(serverPlotPoint.summary ? serverPlotPoint.summary : '', userEncryptionKey);
                const plotPointExists: boolean = PlotPointRepository.plotPointExist(userId, bookId, serverPlotPoint.plot_point_id, lang);
                if (plotPointExists) {
                    const updateSuccessful: boolean = PlotPointRepository.updatePlotPoint(userId, bookId, serverPlotPoint.plot_point_id, encryptedTitle, serverPlotPoint.hashed_title, encryptedSummary, serverPlotPoint.last_update, lang);
                    if (!updateSuccessful) {
                        return false;
                    }
                } else {
                    if (!serverPlotPoint.linked_incident_id) {
                        return false;
                    }
                    const insertSuccessful: boolean = PlotPointRepository.insertSyncPlotPoint(serverPlotPoint.plot_point_id, encryptedTitle, serverPlotPoint.hashed_title, encryptedSummary, serverPlotPoint.linked_incident_id, serverPlotPoint.author_id, bookId, serverPlotPoint.last_update, lang);
                    if (!insertSuccessful) {
                        return false;
                    }
                }
            }
        }

        if (serverIncidents && serverIncidents.length > 0) {
            for (const serverIncident of serverIncidents) {
                const encryptedTitle: string = System.encryptDataWithUserKey(serverIncident.title, userEncryptionKey);
                const encryptedSummary: string = System.encryptDataWithUserKey(serverIncident.summary ? serverIncident.summary : '', userEncryptionKey);
                const incidentExists: boolean = IncidentRepository.incidentExist(userId, bookId, serverIncident.incident_id, lang);
                if (incidentExists) {
                    const updateSuccessful: boolean = IncidentRepository.updateIncident(userId, bookId, serverIncident.incident_id, encryptedTitle, serverIncident.hashed_title, encryptedSummary, serverIncident.last_update, lang);
                    if (!updateSuccessful) {
                        return false;
                    }
                } else {
                    const insertSuccessful: boolean = IncidentRepository.insertSyncIncident(serverIncident.incident_id, userId, bookId, encryptedTitle, serverIncident.hashed_title, encryptedSummary, serverIncident.last_update, lang);
                    if (!insertSuccessful) {
                        return false;
                    }
                }
            }
        }

        if (serverChapterContents && serverChapterContents.length > 0) {
            for (const serverChapterContent of serverChapterContents) {
                const chapterContentExists: boolean = ChapterContentRepository.isChapterContentExist(userId, serverChapterContent.content_id, lang);
                const encryptedContent: string = System.encryptDataWithUserKey(serverChapterContent.content ? JSON.stringify(serverChapterContent.content) : '', userEncryptionKey);
                if (chapterContentExists) {
                    const updateSuccessful: boolean = ChapterContentRepository.updateChapterContent(userId, serverChapterContent.chapter_id, serverChapterContent.version, encryptedContent, serverChapterContent.words_count, serverChapterContent.last_update);
                    if (!updateSuccessful) {
                        return false;
                    }
                } else {
                    const insertSuccessful: boolean = ChapterContentRepository.insertSyncChapterContent(serverChapterContent.content_id, serverChapterContent.chapter_id, userId, serverChapterContent.version, encryptedContent, serverChapterContent.words_count, serverChapterContent.time_on_it, serverChapterContent.last_update, lang);
                    if (!insertSuccessful) {
                        return false;
                    }
                }
            }
        }

        if (serverChapterInfos && serverChapterInfos.length > 0) {
            for (const serverChapterInfo of serverChapterInfos) {
                const chapterInfoExists: boolean = ChapterRepo.isChapterInfoExist(userId, serverChapterInfo.chapter_id, lang);
                const encryptedSummary: string = System.encryptDataWithUserKey(serverChapterInfo.summary ? serverChapterInfo.summary : '', userEncryptionKey);
                const encryptedGoal: string = System.encryptDataWithUserKey(serverChapterInfo.goal ? serverChapterInfo.goal : '', userEncryptionKey);
                if (chapterInfoExists) {
                    const updateSuccessful: boolean = ChapterRepo.updateChapterInfos(userId, serverChapterInfo.chapter_id, serverChapterInfo.act_id, bookId, serverChapterInfo.incident_id, serverChapterInfo.plot_point_id, encryptedSummary, encryptedGoal, serverChapterInfo.last_update, lang);
                    if (!updateSuccessful) {
                        return false;
                    }
                } else {
                    const insertSuccessful: boolean = ChapterRepo.insertSyncChapterInfo(serverChapterInfo.chapter_info_id, serverChapterInfo.chapter_id, serverChapterInfo.act_id, serverChapterInfo.incident_id, serverChapterInfo.plot_point_id, bookId, serverChapterInfo.author_id, encryptedSummary, encryptedGoal, serverChapterInfo.last_update, lang);
                    if (!insertSuccessful) {
                        return false;
                    }
                }
            }
        }

        if (serverCharacters && serverCharacters.length > 0) {
            for (const serverCharacter of serverCharacters) {
                const characterExists: boolean = CharacterRepo.isCharacterExist(userId, serverCharacter.character_id, lang);
                const characterData = {
                    firstName: System.encryptDataWithUserKey(serverCharacter.first_name, userEncryptionKey),
                    lastName: System.encryptDataWithUserKey(serverCharacter.last_name ? serverCharacter.last_name : '', userEncryptionKey),
                    nickname: System.encryptDataWithUserKey(serverCharacter.nickname ? serverCharacter.nickname : '', userEncryptionKey),
                    age: System.encryptDataWithUserKey(serverCharacter.age ? serverCharacter.age : '', userEncryptionKey),
                    gender: System.encryptDataWithUserKey(serverCharacter.gender ? serverCharacter.gender : '', userEncryptionKey),
                    species: System.encryptDataWithUserKey(serverCharacter.species ? serverCharacter.species : '', userEncryptionKey),
                    nationality: System.encryptDataWithUserKey(serverCharacter.nationality ? serverCharacter.nationality : '', userEncryptionKey),
                    status: System.encryptDataWithUserKey(serverCharacter.status ? serverCharacter.status : 'alive', userEncryptionKey),
                    category: System.encryptDataWithUserKey(serverCharacter.category, userEncryptionKey),
                    title: System.encryptDataWithUserKey(serverCharacter.title ? serverCharacter.title : '', userEncryptionKey),
                    image: System.encryptDataWithUserKey(serverCharacter.image ? serverCharacter.image : '', userEncryptionKey),
                    role: System.encryptDataWithUserKey(serverCharacter.role ? serverCharacter.role : '', userEncryptionKey),
                    biography: System.encryptDataWithUserKey(serverCharacter.biography ? serverCharacter.biography : '', userEncryptionKey),
                    history: System.encryptDataWithUserKey(serverCharacter.history ? serverCharacter.history : '', userEncryptionKey),
                    speechPattern: System.encryptDataWithUserKey(serverCharacter.speech_pattern ? serverCharacter.speech_pattern : '', userEncryptionKey),
                    catchphrase: System.encryptDataWithUserKey(serverCharacter.catchphrase ? serverCharacter.catchphrase : '', userEncryptionKey),
                    residence: System.encryptDataWithUserKey(serverCharacter.residence ? serverCharacter.residence : '', userEncryptionKey),
                    notes: System.encryptDataWithUserKey(serverCharacter.notes ? serverCharacter.notes : '', userEncryptionKey),
                    color: System.encryptDataWithUserKey(serverCharacter.color ? serverCharacter.color : '', userEncryptionKey)
                };
                if (characterExists) {
                    const updateSuccessful: boolean = CharacterRepo.updateCharacter(userId, serverCharacter.character_id, characterData, serverCharacter.last_update, lang);
                    if (!updateSuccessful) {
                        return false;
                    }
                } else {
                    const insertSuccessful: boolean = CharacterRepo.insertSyncCharacter(serverCharacter.character_id, bookId, userId, characterData, serverCharacter.last_update, lang);
                    if (!insertSuccessful) {
                        return false;
                    }
                }
            }
        }

        if (serverCharacterAttributes && serverCharacterAttributes.length > 0) {
            for (const serverCharacterAttribute of serverCharacterAttributes) {
                const characterAttributeExists: boolean = CharacterRepo.isCharacterAttributeExist(userId, serverCharacterAttribute.attr_id, lang);
                const encryptedAttributeName: string = System.encryptDataWithUserKey(serverCharacterAttribute.attribute_name, userEncryptionKey);
                const encryptedAttributeValue: string = System.encryptDataWithUserKey(serverCharacterAttribute.attribute_value, userEncryptionKey);
                if (characterAttributeExists) {
                    const updateSuccessful: boolean = CharacterRepo.updateCharacterAttribute(userId, serverCharacterAttribute.attr_id, encryptedAttributeName, encryptedAttributeValue, serverCharacterAttribute.last_update, lang);
                    if (!updateSuccessful) {
                        return false;
                    }
                } else {
                    const insertSuccessful: boolean = CharacterRepo.insertSyncCharacterAttribute(serverCharacterAttribute.attr_id, serverCharacterAttribute.character_id, userId, encryptedAttributeName, encryptedAttributeValue, serverCharacterAttribute.last_update, lang);
                    if (!insertSuccessful) {
                        return false;
                    }
                }
            }
        }

        if (serverLocations && serverLocations.length > 0) {
            for (const serverLocation of serverLocations) {
                const locationExists: boolean = LocationRepo.isLocationExist(userId, serverLocation.loc_id, lang);
                const encryptedLocationName: string = System.encryptDataWithUserKey(serverLocation.loc_name, userEncryptionKey);
                if (locationExists) {
                    const updateSuccessful: boolean = LocationRepo.updateLocationSection(userId, serverLocation.loc_id, encryptedLocationName, serverLocation.loc_original_name, serverLocation.last_update, lang);
                    if (!updateSuccessful) {
                        return false;
                    }
                } else {
                    const insertSuccessful: boolean = LocationRepo.insertSyncLocation(serverLocation.loc_id, bookId, userId, encryptedLocationName, serverLocation.loc_original_name, serverLocation.last_update, lang);
                    if (!insertSuccessful) {
                        return false;
                    }
                }
            }
        }

        if (serverLocationElements && serverLocationElements.length > 0) {
            for (const serverLocationElement of serverLocationElements) {
                const locationElementExists: boolean = LocationRepo.isLocationElementExist(userId, serverLocationElement.element_id, lang);
                const encryptedElementName: string = System.encryptDataWithUserKey(serverLocationElement.element_name, userEncryptionKey);
                const encryptedElementDescription: string = System.encryptDataWithUserKey(serverLocationElement.element_description ? serverLocationElement.element_description : '', userEncryptionKey);
                if (locationElementExists) {
                    const updateSuccessful: boolean = LocationRepo.updateLocationElement(userId, serverLocationElement.element_id, encryptedElementName, serverLocationElement.original_name, encryptedElementDescription, serverLocationElement.last_update, lang);
                    if (!updateSuccessful) {
                        return false;
                    }
                } else {
                    const insertSuccessful: boolean = LocationRepo.insertSyncLocationElement(serverLocationElement.element_id, serverLocationElement.location, userId, encryptedElementName, serverLocationElement.original_name, encryptedElementDescription, serverLocationElement.last_update, lang);
                    if (!insertSuccessful) {
                        return false;
                    }
                }
            }
        }

        if (serverLocationSubElements && serverLocationSubElements.length > 0) {
            for (const serverLocationSubElement of serverLocationSubElements) {
                const locationSubElementExists: boolean = LocationRepo.isLocationSubElementExist(userId, serverLocationSubElement.sub_element_id, lang);
                const encryptedSubElementName: string = System.encryptDataWithUserKey(serverLocationSubElement.sub_elem_name, userEncryptionKey);
                const encryptedSubElementDescription: string = System.encryptDataWithUserKey(serverLocationSubElement.sub_elem_description ? serverLocationSubElement.sub_elem_description : '', userEncryptionKey);
                if (locationSubElementExists) {
                    const updateSuccessful: boolean = LocationRepo.updateLocationSubElement(userId, serverLocationSubElement.sub_element_id, encryptedSubElementName, serverLocationSubElement.original_name, encryptedSubElementDescription, serverLocationSubElement.last_update, lang);
                    if (!updateSuccessful) {
                        return false;
                    }
                } else {
                    const insertSuccessful: boolean = LocationRepo.insertSyncLocationSubElement(serverLocationSubElement.sub_element_id, serverLocationSubElement.element_id, userId, encryptedSubElementName, serverLocationSubElement.original_name, encryptedSubElementDescription, serverLocationSubElement.last_update, lang);
                    if (!insertSuccessful) {
                        return false;
                    }
                }
            }
        }

        if (serverWorlds && serverWorlds.length > 0) {
            for (const serverWorld of serverWorlds) {
                const worldExists: boolean = WorldRepository.worldExist(userId, bookId, serverWorld.world_id, lang);
                const encryptedName: string = System.encryptDataWithUserKey(serverWorld.name, userEncryptionKey);
                const encryptedHistory: string = System.encryptDataWithUserKey(serverWorld.history ? serverWorld.history : '', userEncryptionKey);
                const encryptedPolitics: string = System.encryptDataWithUserKey(serverWorld.politics ? serverWorld.politics : '', userEncryptionKey);
                const encryptedEconomy: string = System.encryptDataWithUserKey(serverWorld.economy ? serverWorld.economy : '', userEncryptionKey);
                const encryptedReligion: string = System.encryptDataWithUserKey(serverWorld.religion ? serverWorld.religion : '', userEncryptionKey);
                const encryptedLanguages: string = System.encryptDataWithUserKey(serverWorld.languages ? serverWorld.languages : '', userEncryptionKey);
                if (worldExists) {
                    const updateSuccessful: boolean = WorldRepository.updateWorld(userId, serverWorld.world_id, encryptedName, serverWorld.hashed_name, encryptedHistory, encryptedPolitics, encryptedEconomy, encryptedReligion, encryptedLanguages, serverWorld.last_update, lang);
                    if (!updateSuccessful) {
                        return false;
                    }
                } else {
                    const insertSuccessful: boolean = WorldRepository.insertSyncWorld(serverWorld.world_id, encryptedName, serverWorld.hashed_name, userId, bookId, encryptedHistory, encryptedPolitics, encryptedEconomy, encryptedReligion, encryptedLanguages, serverWorld.last_update, lang);
                    if (!insertSuccessful) {
                        return false;
                    }
                }
            }
        }

        if (serverWorldElements && serverWorldElements.length > 0) {
            for (const serverWorldElement of serverWorldElements) {
                const worldElementExists: boolean = WorldRepository.worldElementExist(userId, serverWorldElement.world_id, serverWorldElement.element_id, lang);
                const encryptedName: string = System.encryptDataWithUserKey(serverWorldElement.name, userEncryptionKey);
                const encryptedDescription: string = System.encryptDataWithUserKey(serverWorldElement.description ? serverWorldElement.description : '', userEncryptionKey);
                if (worldElementExists) {
                    const updateSuccessful: boolean = WorldRepository.updateWorldElement(userId, serverWorldElement.element_id, encryptedName, encryptedDescription, serverWorldElement.last_update, lang);
                    if (!updateSuccessful) {
                        return false;
                    }
                } else {
                    const insertSuccessful: boolean = WorldRepository.insertSyncWorldElement(serverWorldElement.element_id, serverWorldElement.world_id, userId, serverWorldElement.element_type, encryptedName, serverWorldElement.original_name, encryptedDescription, serverWorldElement.last_update, lang);
                    if (!insertSuccessful) {
                        return false;
                    }
                }
            }
        }

        if (serverIssues && serverIssues.length > 0) {
            for (const serverIssue of serverIssues) {
                const issueExists: boolean = IssueRepository.issueExist(userId, bookId, serverIssue.issue_id, lang);
                const encryptedName: string = System.encryptDataWithUserKey(serverIssue.name, userEncryptionKey);
                if (issueExists) {
                    const updateSuccessful: boolean = IssueRepository.updateIssue(userId, bookId, serverIssue.issue_id, encryptedName, serverIssue.hashed_issue_name, serverIssue.last_update, lang);
                    if (!updateSuccessful) {
                        return false;
                    }
                } else {
                    const insertSuccessful: boolean = IssueRepository.insertSyncIssue(serverIssue.issue_id, userId, bookId, encryptedName, serverIssue.hashed_issue_name, serverIssue.last_update, lang);
                    if (!insertSuccessful) {
                        return false;
                    }
                }
            }
        }

        if (serverGuideLines && serverGuideLines.length > 0) {
            for (const serverGuideLine of serverGuideLines) {
                const guideLineExists: boolean = GuidelineRepo.guideLineExist(userId, bookId, lang);
                const encryptedTone: string | null = serverGuideLine.tone ? System.encryptDataWithUserKey(serverGuideLine.tone, userEncryptionKey) : null;
                const encryptedAtmosphere: string | null = serverGuideLine.atmosphere ? System.encryptDataWithUserKey(serverGuideLine.atmosphere, userEncryptionKey) : null;
                const encryptedWritingStyle: string | null = serverGuideLine.writing_style ? System.encryptDataWithUserKey(serverGuideLine.writing_style, userEncryptionKey) : null;
                const encryptedThemes: string | null = serverGuideLine.themes ? System.encryptDataWithUserKey(serverGuideLine.themes, userEncryptionKey) : null;
                const encryptedSymbolism: string | null = serverGuideLine.symbolism ? System.encryptDataWithUserKey(serverGuideLine.symbolism, userEncryptionKey) : null;
                const encryptedMotifs: string | null = serverGuideLine.motifs ? System.encryptDataWithUserKey(serverGuideLine.motifs, userEncryptionKey) : null;
                const encryptedNarrativeVoice: string | null = serverGuideLine.narrative_voice ? System.encryptDataWithUserKey(serverGuideLine.narrative_voice, userEncryptionKey) : null;
                const encryptedPacing: string | null = serverGuideLine.pacing ? System.encryptDataWithUserKey(serverGuideLine.pacing, userEncryptionKey) : null;
                const encryptedKeyMessages: string | null = serverGuideLine.key_messages ? System.encryptDataWithUserKey(serverGuideLine.key_messages, userEncryptionKey) : null;
                const encryptedIntendedAudience: string | null = serverGuideLine.intended_audience ? System.encryptDataWithUserKey(serverGuideLine.intended_audience, userEncryptionKey) : null;
                if (guideLineExists) {
                    const updateSuccessful: boolean = GuidelineRepo.updateGuideLine(userId, bookId, encryptedTone, encryptedAtmosphere, encryptedWritingStyle, encryptedThemes, encryptedSymbolism, encryptedMotifs, encryptedNarrativeVoice, encryptedPacing, encryptedKeyMessages, encryptedIntendedAudience, serverGuideLine.last_update, lang);
                    if (!updateSuccessful) {
                        return false;
                    }
                } else {
                    const insertSuccessful: boolean = GuidelineRepo.insertSyncGuideLine(userId, bookId, encryptedTone, encryptedAtmosphere, encryptedWritingStyle, encryptedThemes, encryptedSymbolism, encryptedMotifs, encryptedNarrativeVoice, encryptedPacing, encryptedIntendedAudience, encryptedKeyMessages, serverGuideLine.last_update, lang);
                    if (!insertSuccessful) {
                        return false;
                    }
                }
            }
        }

        if (serverAIGuideLines && serverAIGuideLines.length > 0) {
            for (const serverAIGuideLine of serverAIGuideLines) {
                const aiGuideLineExists: boolean = GuidelineRepo.aiGuideLineExist(userId, bookId, lang);
                const encryptedGlobalResume: string | null = serverAIGuideLine.global_resume ? System.encryptDataWithUserKey(serverAIGuideLine.global_resume, userEncryptionKey) : null;
                const encryptedThemes: string | null = serverAIGuideLine.themes ? System.encryptDataWithUserKey(serverAIGuideLine.themes, userEncryptionKey) : null;
                const encryptedTone: string | null = serverAIGuideLine.tone ? System.encryptDataWithUserKey(serverAIGuideLine.tone, userEncryptionKey) : null;
                const encryptedAtmosphere: string | null = serverAIGuideLine.atmosphere ? System.encryptDataWithUserKey(serverAIGuideLine.atmosphere, userEncryptionKey) : null;
                const encryptedCurrentResume: string | null = serverAIGuideLine.current_resume ? System.encryptDataWithUserKey(serverAIGuideLine.current_resume, userEncryptionKey) : null;
                if (aiGuideLineExists) {
                    const updateSuccessful: boolean = GuidelineRepo.insertAIGuideLine(userId, bookId, serverAIGuideLine.narrative_type, serverAIGuideLine.dialogue_type, encryptedGlobalResume, encryptedAtmosphere, serverAIGuideLine.verbe_tense, serverAIGuideLine.langue, encryptedThemes, serverAIGuideLine.last_update, lang);
                    if (!updateSuccessful) {
                        return false;
                    }
                } else {
                    const insertSuccessful: boolean = GuidelineRepo.insertSyncAIGuideLine(userId, bookId, encryptedGlobalResume, encryptedThemes, serverAIGuideLine.verbe_tense, serverAIGuideLine.narrative_type, serverAIGuideLine.langue, serverAIGuideLine.dialogue_type, encryptedTone, encryptedAtmosphere, encryptedCurrentResume, serverAIGuideLine.last_update, lang);
                    if (!insertSuccessful) {
                        return false;
                    }
                }
            }
        }

        if (completeBook.bookTools && completeBook.bookTools.length > 0) {
            for (const serverBookTool of completeBook.bookTools) {
                const success: boolean = BookRepo.insertSyncBookTools(bookId, userId, serverBookTool.characters_enabled, serverBookTool.worlds_enabled, serverBookTool.locations_enabled, serverBookTool.spells_enabled, serverBookTool.last_update, lang);
                if (!success) {
                    return false;
                }
            }
        }

        if (completeBook.spellTags && completeBook.spellTags.length > 0) {
            for (const serverSpellTag of completeBook.spellTags) {
                const spellTagExists: boolean = SpellTagRepo.isSpellTagExist(userId, serverSpellTag.tag_id, lang);
                const encryptedName: string = System.encryptDataWithUserKey(serverSpellTag.name, userEncryptionKey);
                if (spellTagExists) {
                    const updateSuccessful: boolean = SpellTagRepo.updateSyncSpellTag(userId, serverSpellTag.tag_id, encryptedName, serverSpellTag.name_hash, serverSpellTag.color, serverSpellTag.last_update, lang);
                    if (!updateSuccessful) {
                        return false;
                    }
                } else {
                    const insertSuccessful: boolean = SpellTagRepo.insertSyncSpellTag(serverSpellTag.tag_id, serverSpellTag.book_id, userId, encryptedName, serverSpellTag.name_hash, serverSpellTag.color, serverSpellTag.last_update, lang);
                    if (!insertSuccessful) {
                        return false;
                    }
                }
            }
        }

        if (completeBook.spells && completeBook.spells.length > 0) {
            for (const serverSpell of completeBook.spells) {
                const spellExists: boolean = SpellRepo.isSpellExist(userId, serverSpell.spell_id, lang);
                const encryptedName: string = System.encryptDataWithUserKey(serverSpell.name, userEncryptionKey);
                const encryptedDescription: string | null = serverSpell.description ? System.encryptDataWithUserKey(serverSpell.description, userEncryptionKey) : null;
                const encryptedAppearance: string | null = serverSpell.appearance ? System.encryptDataWithUserKey(serverSpell.appearance, userEncryptionKey) : null;
                const encryptedTags: string | null = serverSpell.tags ? System.encryptDataWithUserKey(serverSpell.tags, userEncryptionKey) : null;
                const encryptedPowerLevel: string | null = serverSpell.power_level ? System.encryptDataWithUserKey(serverSpell.power_level, userEncryptionKey) : null;
                const encryptedComponents: string | null = serverSpell.components ? System.encryptDataWithUserKey(serverSpell.components, userEncryptionKey) : null;
                const encryptedLimitations: string | null = serverSpell.limitations ? System.encryptDataWithUserKey(serverSpell.limitations, userEncryptionKey) : null;
                const encryptedNotes: string | null = serverSpell.notes ? System.encryptDataWithUserKey(serverSpell.notes, userEncryptionKey) : null;
                if (spellExists) {
                    const updateSuccessful: boolean = SpellRepo.updateSyncSpell(userId, serverSpell.spell_id, encryptedName, serverSpell.name_hash, encryptedDescription, encryptedAppearance, encryptedTags, encryptedPowerLevel, encryptedComponents, encryptedLimitations, encryptedNotes, serverSpell.last_update, lang);
                    if (!updateSuccessful) {
                        return false;
                    }
                } else {
                    const insertSuccessful: boolean = SpellRepo.insertSyncSpell(serverSpell.spell_id, serverSpell.book_id, userId, encryptedName, serverSpell.name_hash, encryptedDescription, encryptedAppearance, encryptedTags, encryptedPowerLevel, encryptedComponents, encryptedLimitations, encryptedNotes, serverSpell.last_update, lang);
                    if (!insertSuccessful) {
                        return false;
                    }
                }
            }
        }

        return true;
    }

    /**
     * Retrieves all synced books for a user with their complete hierarchical data structure.
     * Fetches all related entities (chapters, characters, locations, etc.) and organizes them by book.
     * @param userId - The unique identifier of the user
     * @param lang - The language for error messages ('fr' or 'en')
     * @returns A promise resolving to an array of SyncedBook objects with decrypted data
     */
    static async getSyncedBooks(userId: string, lang: 'fr' | 'en'): Promise<SyncedBook[]> {
        const userEncryptionKey: string = getUserEncryptionKey(userId);

        const [
            allBooks,
            allChapters,
            allChapterContents,
            allChapterInfos,
            allCharacters,
            allCharacterAttributes,
            allLocations,
            allLocationElements,
            allLocationSubElements,
            allWorlds,
            allWorldElements,
            allIncidents,
            allPlotPoints,
            allIssues,
            allActSummaries,
            allGuidelines,
            allAIGuidelines,
            allSpells,
            allSpellTags
        ]: [
            SyncedBookResult[],
            SyncedChapterResult[],
            SyncedChapterContentResult[],
            SyncedChapterInfoResult[],
            SyncedCharacterResult[],
            SyncedCharacterAttributeResult[],
            SyncedLocationResult[],
            SyncedLocationElementResult[],
            SyncedLocationSubElementResult[],
            SyncedWorldResult[],
            SyncedWorldElementResult[],
            SyncedIncidentResult[],
            SyncedPlotPointResult[],
            SyncedIssueResult[],
            SyncedActSummaryResult[],
            SyncedGuideLineResult[],
            SyncedAIGuideLineResult[],
            SyncedSpellResult[],
            SyncedSpellTagResult[]
        ] = await Promise.all([
            BookRepo.fetchSyncedBooks(userId, lang),
            ChapterRepo.fetchSyncedChapters(userId, lang),
            ChapterContentRepository.fetchSyncedChapterContents(userId, lang),
            ChapterRepo.fetchSyncedChapterInfos(userId, lang),
            CharacterRepo.fetchSyncedCharacters(userId, lang),
            CharacterRepo.fetchSyncedCharacterAttributes(userId, lang),
            LocationRepo.fetchSyncedLocations(userId, lang),
            LocationRepo.fetchSyncedLocationElements(userId, lang),
            LocationRepo.fetchSyncedLocationSubElements(userId, lang),
            WorldRepository.fetchSyncedWorlds(userId, lang),
            WorldRepository.fetchSyncedWorldElements(userId, lang),
            IncidentRepository.fetchSyncedIncidents(userId, lang),
            PlotPointRepository.fetchSyncedPlotPoints(userId, lang),
            IssueRepository.fetchSyncedIssues(userId, lang),
            ActRepository.fetchSyncedActSummaries(userId, lang),
            GuidelineRepo.fetchSyncedGuideLine(userId, lang),
            GuidelineRepo.fetchSyncedAIGuideLine(userId, lang),
            SpellRepo.fetchSyncedSpells(userId, lang),
            SpellTagRepo.fetchSyncedSpellTags(userId, lang)
        ]);

        return allBooks.map((bookRecord: SyncedBookResult): SyncedBook => {
            const currentBookId: string = bookRecord.book_id;

            const bookChapters: SyncedChapter[] = allChapters
                .filter((chapterRecord: SyncedChapterResult): boolean => chapterRecord.book_id === currentBookId)
                .map((chapterRecord: SyncedChapterResult): SyncedChapter => {
                    const currentChapterId: string = chapterRecord.chapter_id;

                    const chapterContents: SyncedChapterContent[] = allChapterContents
                        .filter((contentRecord: SyncedChapterContentResult): boolean => contentRecord.chapter_id === currentChapterId)
                        .map((contentRecord: SyncedChapterContentResult): SyncedChapterContent => ({
                            id: contentRecord.content_id,
                            lastUpdate: contentRecord.last_update
                        }));

                    const chapterInfoRecord: SyncedChapterInfoResult | undefined = allChapterInfos.find((infoRecord: SyncedChapterInfoResult): boolean => infoRecord.chapter_id === currentChapterId);
                    const chapterInfo: SyncedChapterInfo | null = chapterInfoRecord ? {
                        id: chapterInfoRecord.chapter_info_id,
                        lastUpdate: chapterInfoRecord.last_update
                    } : null;

                    return {
                        id: currentChapterId,
                        name: System.decryptDataWithUserKey(chapterRecord.title, userEncryptionKey),
                        lastUpdate: chapterRecord.last_update,
                        contents: chapterContents,
                        info: chapterInfo
                    };
                });

            const bookCharacters: SyncedCharacter[] = allCharacters
                .filter((characterRecord: SyncedCharacterResult): boolean => characterRecord.book_id === currentBookId)
                .map((characterRecord: SyncedCharacterResult): SyncedCharacter => {
                    const currentCharacterId: string = characterRecord.character_id;

                    const characterAttributes: SyncedCharacterAttribute[] = allCharacterAttributes
                        .filter((attributeRecord: SyncedCharacterAttributeResult): boolean => attributeRecord.character_id === currentCharacterId)
                        .map((attributeRecord: SyncedCharacterAttributeResult): SyncedCharacterAttribute => ({
                            id: attributeRecord.attr_id,
                            name: System.decryptDataWithUserKey(attributeRecord.attribute_name, userEncryptionKey),
                            lastUpdate: attributeRecord.last_update
                        }));

                    return {
                        id: currentCharacterId,
                        name: System.decryptDataWithUserKey(characterRecord.first_name, userEncryptionKey),
                        lastUpdate: characterRecord.last_update,
                        attributes: characterAttributes
                    };
                });

            const bookLocations: SyncedLocation[] = allLocations
                .filter((locationRecord: SyncedLocationResult): boolean => locationRecord.book_id === currentBookId)
                .map((locationRecord: SyncedLocationResult): SyncedLocation => {
                    const currentLocationId: string = locationRecord.loc_id;

                    const locationElements: SyncedLocationElement[] = allLocationElements
                        .filter((elementRecord: SyncedLocationElementResult): boolean => elementRecord.location === currentLocationId)
                        .map((elementRecord: SyncedLocationElementResult): SyncedLocationElement => {
                            const currentElementId: string = elementRecord.element_id;

                            const locationSubElements: SyncedLocationSubElement[] = allLocationSubElements
                                .filter((subElementRecord: SyncedLocationSubElementResult): boolean => subElementRecord.element_id === currentElementId)
                                .map((subElementRecord: SyncedLocationSubElementResult): SyncedLocationSubElement => ({
                                    id: subElementRecord.sub_element_id,
                                    name: System.decryptDataWithUserKey(subElementRecord.sub_elem_name, userEncryptionKey),
                                    lastUpdate: subElementRecord.last_update
                                }));

                            return {
                                id: currentElementId,
                                name: System.decryptDataWithUserKey(elementRecord.element_name, userEncryptionKey),
                                lastUpdate: elementRecord.last_update,
                                subElements: locationSubElements
                            };
                        });

                    return {
                        id: currentLocationId,
                        name: System.decryptDataWithUserKey(locationRecord.loc_name, userEncryptionKey),
                        lastUpdate: locationRecord.last_update,
                        elements: locationElements
                    };
                });

            const bookWorlds: SyncedWorld[] = allWorlds
                .filter((worldRecord: SyncedWorldResult): boolean => worldRecord.book_id === currentBookId)
                .map((worldRecord: SyncedWorldResult): SyncedWorld => {
                    const currentWorldId: string = worldRecord.world_id;

                    const worldElements: SyncedWorldElement[] = allWorldElements
                        .filter((worldElementRecord: SyncedWorldElementResult): boolean => worldElementRecord.world_id === currentWorldId)
                        .map((worldElementRecord: SyncedWorldElementResult): SyncedWorldElement => ({
                            id: worldElementRecord.element_id,
                            name: System.decryptDataWithUserKey(worldElementRecord.name, userEncryptionKey),
                            lastUpdate: worldElementRecord.last_update
                        }));

                    return {
                        id: currentWorldId,
                        name: System.decryptDataWithUserKey(worldRecord.name, userEncryptionKey),
                        lastUpdate: worldRecord.last_update,
                        elements: worldElements
                    };
                });

            const bookIncidents: SyncedIncident[] = allIncidents
                .filter((incidentRecord: SyncedIncidentResult): boolean => incidentRecord.book_id === currentBookId)
                .map((incidentRecord: SyncedIncidentResult): SyncedIncident => ({
                    id: incidentRecord.incident_id,
                    name: System.decryptDataWithUserKey(incidentRecord.title, userEncryptionKey),
                    lastUpdate: incidentRecord.last_update
                }));

            const bookPlotPoints: SyncedPlotPoint[] = allPlotPoints
                .filter((plotPointRecord: SyncedPlotPointResult): boolean => plotPointRecord.book_id === currentBookId)
                .map((plotPointRecord: SyncedPlotPointResult): SyncedPlotPoint => ({
                    id: plotPointRecord.plot_point_id,
                    name: System.decryptDataWithUserKey(plotPointRecord.title, userEncryptionKey),
                    lastUpdate: plotPointRecord.last_update
                }));

            const bookIssues: SyncedIssue[] = allIssues
                .filter((issueRecord: SyncedIssueResult): boolean => issueRecord.book_id === currentBookId)
                .map((issueRecord: SyncedIssueResult): SyncedIssue => ({
                    id: issueRecord.issue_id,
                    name: System.decryptDataWithUserKey(issueRecord.name, userEncryptionKey),
                    lastUpdate: issueRecord.last_update
                }));

            const bookActSummaries: SyncedActSummary[] = allActSummaries
                .filter((actSummaryRecord: SyncedActSummaryResult): boolean => actSummaryRecord.book_id === currentBookId)
                .map((actSummaryRecord: SyncedActSummaryResult): SyncedActSummary => ({
                    id: actSummaryRecord.act_sum_id,
                    lastUpdate: actSummaryRecord.last_update
                }));

            const guidelineRecord: SyncedGuideLineResult | undefined = allGuidelines.find((guidelineItem: SyncedGuideLineResult): boolean => guidelineItem.book_id === currentBookId);
            const bookGuideLine: SyncedGuideLine | null = guidelineRecord ? {
                lastUpdate: guidelineRecord.last_update
            } : null;

            const aiGuidelineRecord: SyncedAIGuideLineResult | undefined = allAIGuidelines.find((aiGuidelineItem: SyncedAIGuideLineResult): boolean => aiGuidelineItem.book_id === currentBookId);
            const bookAIGuideLine: SyncedAIGuideLine | null = aiGuidelineRecord ? {
                lastUpdate: aiGuidelineRecord.last_update
            } : null;

            const bookToolsQuery: SyncedBookToolsResult | null = BookRepo.fetchSyncedBookTools(userId, currentBookId, lang);
            const bookTools: SyncedBookTools | null = bookToolsQuery ? {
                lastUpdate: bookToolsQuery.last_update,
                charactersEnabled: bookToolsQuery.characters_enabled === 1,
                worldsEnabled: bookToolsQuery.worlds_enabled === 1,
                locationsEnabled: bookToolsQuery.locations_enabled === 1,
                spellsEnabled: bookToolsQuery.spells_enabled === 1
            } : null;

            const bookSpells: SyncedSpell[] = allSpells
                .filter((spellRecord: SyncedSpellResult): boolean => spellRecord.book_id === currentBookId)
                .map((spellRecord: SyncedSpellResult): SyncedSpell => ({
                    id: spellRecord.spell_id,
                    name: System.decryptDataWithUserKey(spellRecord.name, userEncryptionKey),
                    lastUpdate: spellRecord.last_update
                }));

            const bookSpellTags: SyncedSpellTag[] = allSpellTags
                .filter((spellTagRecord: SyncedSpellTagResult): boolean => spellTagRecord.book_id === currentBookId)
                .map((spellTagRecord: SyncedSpellTagResult): SyncedSpellTag => ({
                    id: spellTagRecord.tag_id,
                    name: System.decryptDataWithUserKey(spellTagRecord.name, userEncryptionKey),
                    lastUpdate: spellTagRecord.last_update
                }));

            return {
                id: currentBookId,
                type: bookRecord.type,
                title: System.decryptDataWithUserKey(bookRecord.title, userEncryptionKey),
                subTitle: bookRecord.sub_title ? System.decryptDataWithUserKey(bookRecord.sub_title, userEncryptionKey) : null,
                lastUpdate: bookRecord.last_update,
                chapters: bookChapters,
                characters: bookCharacters,
                locations: bookLocations,
                worlds: bookWorlds,
                incidents: bookIncidents,
                plotPoints: bookPlotPoints,
                issues: bookIssues,
                actSummaries: bookActSummaries,
                guideLine: bookGuideLine,
                aiGuideLine: bookAIGuideLine,
                bookTools: bookTools,
                spells: bookSpells,
                spellTags: bookSpellTags
            };
        });
    }

    // ===== SERIES SYNC METHODS =====

    /**
     * Retrieves all series for the current user with lightweight structure for sync comparison.
     * Returns synced series with nested characters, worlds, locations, spells, and spell tags.
     * All encrypted fields are decrypted before return.
     * @param userId - The unique identifier of the user
     * @param lang - The language for error messages ('fr' or 'en')
     * @returns An array of synced series with all nested entities
     */
    static getSyncedSeries(userId: string, lang: 'fr' | 'en'): SyncedSeries[] {
        const userEncryptionKey: string = getUserEncryptionKey(userId);

        const allSeries: SyncedSeriesResult[] = SeriesRepo.fetchSyncedSeries(userId, lang);
        const allSeriesBooks: SyncedSeriesBookResult[] = SeriesRepo.fetchSyncedSeriesBooks(userId, lang);
        const allCharacters: SyncedSeriesCharacterResult[] = SeriesCharacterRepo.fetchSyncedSeriesCharacters(userId, lang);
        const allCharacterAttributes: SyncedSeriesCharacterAttributeResult[] = SeriesCharacterRepo.fetchSyncedSeriesCharacterAttributes(userId, lang);
        const allWorlds: SyncedSeriesWorldResult[] = SeriesWorldRepo.fetchSyncedSeriesWorlds(userId, lang);
        const allWorldElements: SyncedSeriesWorldElementResult[] = SeriesWorldRepo.fetchSyncedSeriesWorldElements(userId, lang);
        const allLocations: SyncedSeriesLocationResult[] = SeriesLocationRepo.fetchSyncedSeriesLocations(userId, lang);
        const allLocationElements: SyncedSeriesLocationElementResult[] = SeriesLocationRepo.fetchSyncedSeriesLocationElements(userId, lang);
        const allLocationSubElements: SyncedSeriesLocationSubElementResult[] = SeriesLocationRepo.fetchSyncedSeriesLocationSubElements(userId, lang);
        const allSpells: SyncedSeriesSpellResult[] = SeriesSpellRepo.fetchSyncedSeriesSpells(userId, lang);
        const allSpellTags: SyncedSeriesSpellTagResult[] = SeriesSpellRepo.fetchSyncedSeriesSpellTags(userId, lang);

        return allSeries.map((series: SyncedSeriesResult): SyncedSeries => {
            const seriesId: string = series.series_id;

            // Map series books
            const books: SyncedSeriesBook[] = allSeriesBooks
                .filter((sb: SyncedSeriesBookResult): boolean => sb.series_id === seriesId)
                .map((sb: SyncedSeriesBookResult): SyncedSeriesBook => ({
                    bookId: sb.book_id,
                    order: sb.book_order,
                    lastUpdate: sb.last_update
                }));

            // Map characters with attributes
            const characters: SyncedSeriesCharacter[] = allCharacters
                .filter((c: SyncedSeriesCharacterResult): boolean => c.series_id === seriesId)
                .map((c: SyncedSeriesCharacterResult): SyncedSeriesCharacter => ({
                    id: c.character_id,
                    name: System.decryptDataWithUserKey(c.first_name, userEncryptionKey),
                    lastUpdate: c.last_update,
                    attributes: allCharacterAttributes
                        .filter((a: SyncedSeriesCharacterAttributeResult): boolean => a.character_id === c.character_id)
                        .map((a: SyncedSeriesCharacterAttributeResult): SyncedSeriesCharacterAttribute => ({
                            id: a.attr_id,
                            name: System.decryptDataWithUserKey(a.attribute_name, userEncryptionKey),
                            lastUpdate: a.last_update
                        }))
                }));

            // Map worlds with elements
            const worlds: SyncedSeriesWorld[] = allWorlds
                .filter((w: SyncedSeriesWorldResult): boolean => w.series_id === seriesId)
                .map((w: SyncedSeriesWorldResult): SyncedSeriesWorld => ({
                    id: w.world_id,
                    name: System.decryptDataWithUserKey(w.name, userEncryptionKey),
                    lastUpdate: w.last_update,
                    elements: allWorldElements
                        .filter((e: SyncedSeriesWorldElementResult): boolean => e.world_id === w.world_id)
                        .map((e: SyncedSeriesWorldElementResult): SyncedSeriesWorldElement => ({
                            id: e.element_id,
                            name: System.decryptDataWithUserKey(e.name, userEncryptionKey),
                            lastUpdate: e.last_update
                        }))
                }));

            // Map locations with elements and sub-elements
            const locations: SyncedSeriesLocation[] = allLocations
                .filter((l: SyncedSeriesLocationResult): boolean => l.series_id === seriesId)
                .map((l: SyncedSeriesLocationResult): SyncedSeriesLocation => ({
                    id: l.loc_id,
                    name: System.decryptDataWithUserKey(l.loc_name, userEncryptionKey),
                    lastUpdate: l.last_update,
                    elements: allLocationElements
                        .filter((e: SyncedSeriesLocationElementResult): boolean => e.location_id === l.loc_id)
                        .map((e: SyncedSeriesLocationElementResult): SyncedSeriesLocationElement => ({
                            id: e.element_id,
                            name: System.decryptDataWithUserKey(e.element_name, userEncryptionKey),
                            lastUpdate: e.last_update,
                            subElements: allLocationSubElements
                                .filter((se: SyncedSeriesLocationSubElementResult): boolean => se.element_id === e.element_id)
                                .map((se: SyncedSeriesLocationSubElementResult): SyncedSeriesLocationSubElement => ({
                                    id: se.sub_element_id,
                                    name: System.decryptDataWithUserKey(se.sub_elem_name, userEncryptionKey),
                                    lastUpdate: se.last_update
                                }))
                        }))
                }));

            // Map spells
            const spells: SyncedSeriesSpell[] = allSpells
                .filter((s: SyncedSeriesSpellResult): boolean => s.series_id === seriesId)
                .map((s: SyncedSeriesSpellResult): SyncedSeriesSpell => ({
                    id: s.spell_id,
                    name: System.decryptDataWithUserKey(s.name, userEncryptionKey),
                    lastUpdate: s.last_update
                }));

            // Map spell tags
            const spellTags: SyncedSeriesSpellTag[] = allSpellTags
                .filter((t: SyncedSeriesSpellTagResult): boolean => t.series_id === seriesId)
                .map((t: SyncedSeriesSpellTagResult): SyncedSeriesSpellTag => ({
                    id: t.tag_id,
                    name: System.decryptDataWithUserKey(t.name, userEncryptionKey),
                    lastUpdate: t.last_update
                }));

            return {
                id: seriesId,
                name: System.decryptDataWithUserKey(series.name, userEncryptionKey),
                description: series.description
                    ? System.decryptDataWithUserKey(series.description, userEncryptionKey)
                    : null,
                lastUpdate: series.last_update,
                books,
                characters,
                worlds,
                locations,
                spells,
                spellTags
            };
        });
    }
}
