import {getUserEncryptionKey} from "../keyManager.js";
import System from "../System.js";
import {CompleteBook} from "./Book.js";
import BookRepo, {EritBooksTable, BookToolsTable} from "../repositories/book.repository.js";
import ChapterRepo, {
    BookChapterInfosTable,
    BookChaptersTable
} from "../repositories/chapter.repository.js";
import IncidentRepository, {BookIncidentsTable} from "../repositories/incident.repository.js";
import PlotPointRepository, {BookPlotPointsTable} from "../repositories/plotpoint.repository.js";
import ChapterContentRepository, {BookChapterContentTable} from "../repositories/chaptercontent.repository.js";
import CharacterRepo, {
    BookCharactersAttributesTable,
    BookCharactersTable
} from "../repositories/character.repository.js";
import LocationRepo, {
    BookLocationTable,
    LocationElementTable,
    LocationSubElementTable
} from "../repositories/location.repository.js";
import WorldRepository, {
    BookWorldElementsTable,
    BookWorldTable
} from "../repositories/world.repository.js";
import ActRepository, {BookActSummariesTable} from "../repositories/act.repository.js";
import GuidelineRepo, {
    BookAIGuideLineTable,
    BookGuideLineTable
} from "../repositories/guideline.repository.js";
import IssueRepository, {BookIssuesTable} from "../repositories/issue.repository.js";
import SpellRepo, {BookSpellsTable} from "../repositories/spell.repo.js";
import SpellTagRepo, {BookSpellTagsTable} from "../repositories/spelltag.repo.js";

export default class Download {
    /**
     * Saves a complete book with all its associated data to the local database.
     * This method encrypts all sensitive data using the user's encryption key before storing.
     * It processes and inserts all book components including chapters, incidents, plot points,
     * chapter contents, chapter infos, characters, character attributes, locations, location elements,
     * location sub-elements, worlds, world elements, act summaries, AI guidelines, guidelines, and issues.
     *
     * @param userId - The unique identifier of the user who owns the book
     * @param data - The complete book data structure containing all book components to save
     * @param lang - The language code for localization ("fr" for French or "en" for English)
     * @returns A promise that resolves to true if all data was saved successfully, false otherwise
     */
    static async saveCompleteBook(userId: string, data: CompleteBook, lang: "fr" | "en"): Promise<boolean> {
        const userEncryptionKey: string = getUserEncryptionKey(userId);

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

        const bookInserted: boolean = BookRepo.insertSyncBook(
            bookData.book_id,
            userId,
            bookData.type,
            encryptedBookTitle,
            bookData.hashed_title,
            encryptedBookSubTitle,
            bookData.hashed_sub_title,
            encryptedBookSummary,
            bookData.serie_id,
            bookData.desired_release_date,
            bookData.desired_word_count,
            bookData.words_count,
            encryptedBookCoverImage,
            bookData.last_update,
            lang
        );
        if (!bookInserted) return false;

        const chaptersInserted: boolean = data.chapters.every((chapter: BookChaptersTable): boolean => {
            const encryptedChapterTitle: string = System.encryptDataWithUserKey(chapter.title, userEncryptionKey);
            return ChapterRepo.insertSyncChapter(chapter.chapter_id, chapter.book_id, userId, encryptedChapterTitle, 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, userEncryptionKey);
            const encryptedIncidentSummary: string | null = incident.summary ? System.encryptDataWithUserKey(incident.summary, userEncryptionKey) : null;
            return IncidentRepository.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 encryptedPlotPointTitle: string = System.encryptDataWithUserKey(plotPoint.title, userEncryptionKey);
            const encryptedPlotPointSummary: string | null = plotPoint.summary ? System.encryptDataWithUserKey(plotPoint.summary, userEncryptionKey) : null;
            return PlotPointRepository.insertSyncPlotPoint(plotPoint.plot_point_id, encryptedPlotPointTitle, plotPoint.hashed_title, encryptedPlotPointSummary, plotPoint.linked_incident_id, userId, plotPoint.book_id, plotPoint.last_update, lang);
        });
        if (!plotPointsInserted) return false;

        const chapterContentsInserted: boolean = data.chapterContents.every((chapterContent: BookChapterContentTable): boolean => {
            const encryptedChapterContent: string | null = chapterContent.content ? System.encryptDataWithUserKey(JSON.stringify(chapterContent.content), userEncryptionKey) : null;
            return ChapterContentRepository.insertSyncChapterContent(chapterContent.content_id, chapterContent.chapter_id, userId, chapterContent.version, encryptedChapterContent, chapterContent.words_count, chapterContent.time_on_it, chapterContent.last_update, lang);
        });
        if (!chapterContentsInserted) return false;

        const chapterInfosInserted: boolean = data.chapterInfos.every((chapterInfo: BookChapterInfosTable): boolean => {
            const encryptedChapterSummary: string | null = chapterInfo.summary ? System.encryptDataWithUserKey(chapterInfo.summary, userEncryptionKey) : null;
            const encryptedChapterGoal: string | null = chapterInfo.goal ? System.encryptDataWithUserKey(chapterInfo.goal, userEncryptionKey) : null;
            return ChapterRepo.insertSyncChapterInfo(chapterInfo.chapter_info_id, chapterInfo.chapter_id, chapterInfo.act_id, chapterInfo.incident_id, chapterInfo.plot_point_id, chapterInfo.book_id, userId, encryptedChapterSummary, encryptedChapterGoal, chapterInfo.last_update, lang);
        });
        if (!chapterInfosInserted) return false;

        const charactersInserted: boolean = data.characters.every((character: BookCharactersTable): boolean => {
            const characterData = {
                firstName: System.encryptDataWithUserKey(character.first_name, userEncryptionKey),
                lastName: character.last_name ? System.encryptDataWithUserKey(character.last_name, userEncryptionKey) : null,
                nickname: character.nickname ? System.encryptDataWithUserKey(character.nickname, userEncryptionKey) : null,
                age: character.age ? System.encryptDataWithUserKey(character.age, userEncryptionKey) : null,
                gender: character.gender ? System.encryptDataWithUserKey(character.gender, userEncryptionKey) : null,
                species: character.species ? System.encryptDataWithUserKey(character.species, userEncryptionKey) : null,
                nationality: character.nationality ? System.encryptDataWithUserKey(character.nationality, userEncryptionKey) : null,
                status: character.status ? System.encryptDataWithUserKey(character.status, userEncryptionKey) : null,
                category: System.encryptDataWithUserKey(character.category, userEncryptionKey),
                title: character.title ? System.encryptDataWithUserKey(character.title, userEncryptionKey) : null,
                image: character.image ? System.encryptDataWithUserKey(character.image, userEncryptionKey) : null,
                role: character.role ? System.encryptDataWithUserKey(character.role, userEncryptionKey) : null,
                biography: character.biography ? System.encryptDataWithUserKey(character.biography, userEncryptionKey) : null,
                history: character.history ? System.encryptDataWithUserKey(character.history, userEncryptionKey) : null,
                speechPattern: character.speech_pattern ? System.encryptDataWithUserKey(character.speech_pattern, userEncryptionKey) : null,
                catchphrase: character.catchphrase ? System.encryptDataWithUserKey(character.catchphrase, userEncryptionKey) : null,
                residence: character.residence ? System.encryptDataWithUserKey(character.residence, userEncryptionKey) : null,
                notes: character.notes ? System.encryptDataWithUserKey(character.notes, userEncryptionKey) : null,
                color: character.color ? System.encryptDataWithUserKey(character.color, userEncryptionKey) : null
            };
            return CharacterRepo.insertSyncCharacter(character.character_id, character.book_id, userId, characterData, character.last_update, lang);
        });
        if (!charactersInserted) return false;

        const characterAttributesInserted: boolean = data.characterAttributes.every((characterAttribute: BookCharactersAttributesTable): boolean => {
            const encryptedAttributeName: string = System.encryptDataWithUserKey(characterAttribute.attribute_name, userEncryptionKey);
            const encryptedAttributeValue: string = System.encryptDataWithUserKey(characterAttribute.attribute_value, userEncryptionKey);
            return CharacterRepo.insertSyncCharacterAttribute(characterAttribute.attr_id, characterAttribute.character_id, userId, encryptedAttributeName, encryptedAttributeValue, characterAttribute.last_update, lang);
        });
        if (!characterAttributesInserted) return false;

        const locationsInserted: boolean = data.locations.every((location: BookLocationTable): boolean => {
            const encryptedLocationName: string = System.encryptDataWithUserKey(location.loc_name, userEncryptionKey);
            return LocationRepo.insertSyncLocation(location.loc_id, location.book_id, userId, encryptedLocationName, location.loc_original_name, location.last_update, lang);
        });
        if (!locationsInserted) return false;

        const locationElementsInserted: boolean = data.locationElements.every((locationElement: LocationElementTable): boolean => {
            const encryptedLocationElementName: string = System.encryptDataWithUserKey(locationElement.element_name, userEncryptionKey);
            const encryptedLocationElementDescription: string | null = locationElement.element_description ? System.encryptDataWithUserKey(locationElement.element_description, userEncryptionKey) : null;
            return LocationRepo.insertSyncLocationElement(locationElement.element_id, locationElement.location, userId, encryptedLocationElementName, locationElement.original_name, encryptedLocationElementDescription, locationElement.last_update, lang);
        });
        if (!locationElementsInserted) return false;

        const locationSubElementsInserted: boolean = data.locationSubElements.every((locationSubElement: LocationSubElementTable): boolean => {
            const encryptedSubElementName: string = System.encryptDataWithUserKey(locationSubElement.sub_elem_name, userEncryptionKey);
            const encryptedSubElementDescription: string | null = locationSubElement.sub_elem_description ? System.encryptDataWithUserKey(locationSubElement.sub_elem_description, userEncryptionKey) : null;
            return LocationRepo.insertSyncLocationSubElement(locationSubElement.sub_element_id, locationSubElement.element_id, userId, encryptedSubElementName, locationSubElement.original_name, encryptedSubElementDescription, locationSubElement.last_update, lang);
        });
        if (!locationSubElementsInserted) return false;

        const worldsInserted: boolean = data.worlds.every((world: BookWorldTable): boolean => {
            const encryptedWorldName: string = System.encryptDataWithUserKey(world.name, userEncryptionKey);
            const encryptedWorldHistory: string | null = world.history ? System.encryptDataWithUserKey(world.history, userEncryptionKey) : null;
            const encryptedWorldPolitics: string | null = world.politics ? System.encryptDataWithUserKey(world.politics, userEncryptionKey) : null;
            const encryptedWorldEconomy: string | null = world.economy ? System.encryptDataWithUserKey(world.economy, userEncryptionKey) : null;
            const encryptedWorldReligion: string | null = world.religion ? System.encryptDataWithUserKey(world.religion, userEncryptionKey) : null;
            const encryptedWorldLanguages: string | null = world.languages ? System.encryptDataWithUserKey(world.languages, userEncryptionKey) : null;
            return WorldRepository.insertSyncWorld(world.world_id, encryptedWorldName, world.hashed_name, userId, world.book_id, encryptedWorldHistory, encryptedWorldPolitics, encryptedWorldEconomy, encryptedWorldReligion, encryptedWorldLanguages, world.last_update, lang);
        });
        if (!worldsInserted) return false;

        const worldElementsInserted: boolean = data.worldElements.every((worldElement: BookWorldElementsTable): boolean => {
            const encryptedWorldElementName: string = System.encryptDataWithUserKey(worldElement.name, userEncryptionKey);
            const encryptedWorldElementDescription: string | null = worldElement.description ? System.encryptDataWithUserKey(worldElement.description, userEncryptionKey) : null;
            return WorldRepository.insertSyncWorldElement(worldElement.element_id, worldElement.world_id, userId, worldElement.element_type, encryptedWorldElementName, worldElement.original_name, encryptedWorldElementDescription, worldElement.last_update, lang);
        });
        if (!worldElementsInserted) return false;

        const actSummariesInserted: boolean = data.actSummaries.every((actSummary: BookActSummariesTable): boolean => {
            const encryptedActSummary: string | null = actSummary.summary ? System.encryptDataWithUserKey(actSummary.summary, userEncryptionKey) : null;
            return ActRepository.insertSyncActSummary(actSummary.act_sum_id, actSummary.book_id, userId, actSummary.act_index, encryptedActSummary, actSummary.last_update, lang);
        });
        if (!actSummariesInserted) return false;

        const aiGuidelinesInserted: boolean = data.aiGuideLine.every((aiGuideline: BookAIGuideLineTable): boolean => {
            const encryptedAIGlobalResume: string | null = aiGuideline.global_resume ? System.encryptDataWithUserKey(aiGuideline.global_resume, userEncryptionKey) : null;
            const encryptedAIThemes: string | null = aiGuideline.themes ? System.encryptDataWithUserKey(aiGuideline.themes, userEncryptionKey) : null;
            const encryptedAITone: string | null = aiGuideline.tone ? System.encryptDataWithUserKey(aiGuideline.tone, userEncryptionKey) : null;
            const encryptedAIAtmosphere: string | null = aiGuideline.atmosphere ? System.encryptDataWithUserKey(aiGuideline.atmosphere, userEncryptionKey) : null;
            const encryptedAICurrentResume: string | null = aiGuideline.current_resume ? System.encryptDataWithUserKey(aiGuideline.current_resume, userEncryptionKey) : null;
            return GuidelineRepo.insertSyncAIGuideLine(userId, aiGuideline.book_id, encryptedAIGlobalResume, encryptedAIThemes, aiGuideline.verbe_tense, aiGuideline.narrative_type, aiGuideline.langue, aiGuideline.dialogue_type, encryptedAITone, encryptedAIAtmosphere, encryptedAICurrentResume, aiGuideline.last_update, lang);
        });
        if (!aiGuidelinesInserted) return false;

        const guidelinesInserted: boolean = data.guideLine.every((guideline: BookGuideLineTable): boolean => {
            const encryptedGuidelineTone: string | null = guideline.tone ? System.encryptDataWithUserKey(guideline.tone, userEncryptionKey) : null;
            const encryptedGuidelineAtmosphere: string | null = guideline.atmosphere ? System.encryptDataWithUserKey(guideline.atmosphere, userEncryptionKey) : null;
            const encryptedGuidelineWritingStyle: string | null = guideline.writing_style ? System.encryptDataWithUserKey(guideline.writing_style, userEncryptionKey) : null;
            const encryptedGuidelineThemes: string | null = guideline.themes ? System.encryptDataWithUserKey(guideline.themes, userEncryptionKey) : null;
            const encryptedGuidelineSymbolism: string | null = guideline.symbolism ? System.encryptDataWithUserKey(guideline.symbolism, userEncryptionKey) : null;
            const encryptedGuidelineMotifs: string | null = guideline.motifs ? System.encryptDataWithUserKey(guideline.motifs, userEncryptionKey) : null;
            const encryptedGuidelineNarrativeVoice: string | null = guideline.narrative_voice ? System.encryptDataWithUserKey(guideline.narrative_voice, userEncryptionKey) : null;
            const encryptedGuidelinePacing: string | null = guideline.pacing ? System.encryptDataWithUserKey(guideline.pacing, userEncryptionKey) : null;
            const encryptedGuidelineIntendedAudience: string | null = guideline.intended_audience ? System.encryptDataWithUserKey(guideline.intended_audience, userEncryptionKey) : null;
            const encryptedGuidelineKeyMessages: string | null = guideline.key_messages ? System.encryptDataWithUserKey(guideline.key_messages, userEncryptionKey) : null;
            return GuidelineRepo.insertSyncGuideLine(userId, guideline.book_id, encryptedGuidelineTone, encryptedGuidelineAtmosphere, encryptedGuidelineWritingStyle, encryptedGuidelineThemes, encryptedGuidelineSymbolism, encryptedGuidelineMotifs, encryptedGuidelineNarrativeVoice, encryptedGuidelinePacing, encryptedGuidelineIntendedAudience, encryptedGuidelineKeyMessages, guideline.last_update, lang);
        });
        if (!guidelinesInserted) return false;

        const issuesInserted: boolean = data.issues.every((issue: BookIssuesTable): boolean => {
            const encryptedIssueName: string = System.encryptDataWithUserKey(issue.name, userEncryptionKey);
            return IssueRepository.insertSyncIssue(issue.issue_id, userId, issue.book_id, encryptedIssueName, issue.hashed_issue_name, issue.last_update, lang);
        });
        if (!issuesInserted) return false;

        const bookToolsInserted: boolean = data.bookTools.every((bookTool: BookToolsTable): boolean => {
            return BookRepo.insertSyncBookTools(bookTool.book_id, userId, bookTool.characters_enabled, bookTool.worlds_enabled, bookTool.locations_enabled, bookTool.spells_enabled, bookTool.last_update, lang);
        });
        if (!bookToolsInserted) return false;

        const spellTagsInserted: boolean = data.spellTags.every((spellTag: BookSpellTagsTable): boolean => {
            const encryptedTagName: string = System.encryptDataWithUserKey(spellTag.name, userEncryptionKey);
            return SpellTagRepo.insertSyncSpellTag(
                spellTag.tag_id,
                spellTag.book_id,
                userId,
                encryptedTagName,
                spellTag.name_hash,
                spellTag.color,
                spellTag.last_update,
                lang
            );
        });
        if (!spellTagsInserted) return false;

        const spellsInserted: boolean = data.spells.every((spell: BookSpellsTable): boolean => {
            const encryptedName: string = System.encryptDataWithUserKey(spell.name, userEncryptionKey);
            const encryptedDescription: string | null = spell.description ? System.encryptDataWithUserKey(spell.description, userEncryptionKey) : null;
            const encryptedAppearance: string | null = spell.appearance ? System.encryptDataWithUserKey(spell.appearance, userEncryptionKey) : null;
            const encryptedTags: string | null = spell.tags ? System.encryptDataWithUserKey(spell.tags, userEncryptionKey) : null;
            const encryptedPowerLevel: string | null = spell.power_level ? System.encryptDataWithUserKey(spell.power_level, userEncryptionKey) : null;
            const encryptedComponents: string | null = spell.components ? System.encryptDataWithUserKey(spell.components, userEncryptionKey) : null;
            const encryptedLimitations: string | null = spell.limitations ? System.encryptDataWithUserKey(spell.limitations, userEncryptionKey) : null;
            const encryptedNotes: string | null = spell.notes ? System.encryptDataWithUserKey(spell.notes, userEncryptionKey) : null;
            return SpellRepo.insertSyncSpell(
                spell.spell_id,
                spell.book_id,
                userId,
                encryptedName,
                spell.name_hash,
                encryptedDescription,
                encryptedAppearance,
                encryptedTags,
                encryptedPowerLevel,
                encryptedComponents,
                encryptedLimitations,
                encryptedNotes,
                spell.last_update,
                lang
            );
        });
        if (!spellsInserted) return false;

        return true;
    }
}
