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 ActRepository, { BookActSummariesTable } from "../repositories/act.repository.js";
import GuidelineRepo, { BookAIGuideLineTable, BookGuideLineTable } from "../repositories/guideline.repository.js";
import ChapterRepo, {
    BookChapterInfosTable,
    BookChaptersTable
} from "../repositories/chapter.repository.js";
import CharacterRepo, {
    BookCharactersAttributesTable,
    BookCharactersTable
} from "../repositories/character.repository.js";
import IncidentRepository, { BookIncidentsTable } from "../repositories/incident.repository.js";
import IssueRepository, { BookIssuesTable } from "../repositories/issue.repository.js";
import LocationRepo, {
    BookLocationTable,
    LocationElementTable,
    LocationSubElementTable
} from "../repositories/location.repository.js";
import PlotPointRepository, { BookPlotPointsTable } from "../repositories/plotpoint.repository.js";
import WorldRepository, {
    BookWorldElementsTable,
    BookWorldTable
} from "../repositories/world.repository.js";
import ChapterContentRepository, { BookChapterContentTable } from "../repositories/chaptercontent.repository.js";
import SpellRepo, { BookSpellsTable } from "../repositories/spell.repo.js";
import SpellTagRepo, { BookSpellTagsTable } from "../repositories/spelltag.repo.js";

export default class Upload {
    /**
     * Prepares a complete book with all related data for synchronization upload.
     * Fetches all book-related tables from the database, decrypts encrypted fields
     * using the user's encryption key, and returns a complete book object ready for sync.
     *
     * @param userId - The unique identifier of the user who owns the book
     * @param bookId - The unique identifier of the book to upload
     * @param lang - The language code for localization ("fr" or "en")
     * @returns A promise that resolves to a CompleteBook object containing all decrypted book data
     */
    static async uploadBookForSync(userId: string, bookId: string, lang: "fr" | "en"): Promise<CompleteBook> {
        const userEncryptionKey: string = getUserEncryptionKey(userId);

        const [
            encryptedBooks,
            encryptedActSummaries,
            encryptedAIGuidelines,
            encryptedChapters,
            encryptedCharacters,
            encryptedGuidelines,
            encryptedIncidents,
            encryptedIssues,
            encryptedLocations,
            encryptedPlotPoints,
            encryptedWorlds,
            bookToolsData,
            encryptedSpells,
            encryptedSpellTags
        ]: [
            EritBooksTable[],
            BookActSummariesTable[],
            BookAIGuideLineTable[],
            BookChaptersTable[],
            BookCharactersTable[],
            BookGuideLineTable[],
            BookIncidentsTable[],
            BookIssuesTable[],
            BookLocationTable[],
            BookPlotPointsTable[],
            BookWorldTable[],
            BookToolsTable | null,
            BookSpellsTable[],
            BookSpellTagsTable[]
        ] = await Promise.all([
            BookRepo.fetchEritBooksTable(userId, bookId, lang),
            ActRepository.fetchBookActSummaries(userId, bookId, lang),
            GuidelineRepo.fetchBookAIGuideLine(userId, bookId, lang),
            ChapterRepo.fetchBookChapters(userId, bookId, lang),
            CharacterRepo.fetchBookCharacters(userId, bookId, lang),
            GuidelineRepo.fetchBookGuideLineTable(userId, bookId, lang),
            IncidentRepository.fetchBookIncidents(userId, bookId, lang),
            IssueRepository.fetchBookIssues(userId, bookId, lang),
            LocationRepo.fetchBookLocations(userId, bookId, lang),
            PlotPointRepository.fetchBookPlotPoints(userId, bookId, lang),
            WorldRepository.fetchBookWorlds(userId, bookId, lang),
            BookRepo.fetchBookTools(userId, bookId, lang),
            SpellRepo.fetchBookSpellsTable(userId, bookId, lang),
            SpellTagRepo.fetchBookSpellTagsTable(userId, bookId, lang)
        ]);

        const [
            nestedChapterContents,
            nestedChapterInfos,
            nestedCharacterAttributes,
            nestedWorldElements,
            nestedLocationElements
        ]: [
            BookChapterContentTable[][],
            BookChapterInfosTable[][],
            BookCharactersAttributesTable[][],
            BookWorldElementsTable[][],
            LocationElementTable[][]
        ] = await Promise.all([
            Promise.all(encryptedChapters.map((chapter: BookChaptersTable): Promise<BookChapterContentTable[]> =>
                ChapterContentRepository.fetchBookChapterContents(userId, chapter.chapter_id, lang))),
            Promise.all(encryptedChapters.map((chapter: BookChaptersTable): Promise<BookChapterInfosTable[]> =>
                ChapterRepo.fetchBookChapterInfos(userId, chapter.chapter_id, lang))),
            Promise.all(encryptedCharacters.map((character: BookCharactersTable): Promise<BookCharactersAttributesTable[]> =>
                CharacterRepo.fetchBookCharactersAttributes(userId, character.character_id, lang))),
            Promise.all(encryptedWorlds.map((world: BookWorldTable): Promise<BookWorldElementsTable[]> =>
                WorldRepository.fetchBookWorldElements(userId, world.world_id, lang))),
            Promise.all(encryptedLocations.map((location: BookLocationTable): Promise<LocationElementTable[]> =>
                LocationRepo.fetchLocationElements(userId, location.loc_id, lang)))
        ]);

        const encryptedChapterContents: BookChapterContentTable[] = nestedChapterContents.flat();
        const encryptedChapterInfos: BookChapterInfosTable[] = nestedChapterInfos.flat();
        const encryptedCharacterAttributes: BookCharactersAttributesTable[] = nestedCharacterAttributes.flat();
        const encryptedWorldElements: BookWorldElementsTable[] = nestedWorldElements.flat();
        const encryptedLocationElements: LocationElementTable[] = nestedLocationElements.flat();

        const nestedLocationSubElements: LocationSubElementTable[][] = await Promise.all(
            encryptedLocationElements.map((element: LocationElementTable): Promise<LocationSubElementTable[]> =>
                LocationRepo.fetchLocationSubElements(userId, element.element_id, lang))
        );
        const encryptedLocationSubElements: LocationSubElementTable[] = nestedLocationSubElements.flat();

        const eritBooks: EritBooksTable[] = encryptedBooks.map((book: EritBooksTable): EritBooksTable => ({
            ...book,
            title: System.decryptDataWithUserKey(book.title, userEncryptionKey),
            sub_title: book.sub_title ? System.decryptDataWithUserKey(book.sub_title, userEncryptionKey) : null,
            summary: book.summary ? System.decryptDataWithUserKey(book.summary, userEncryptionKey) : null,
            cover_image: book.cover_image ? System.decryptDataWithUserKey(book.cover_image, userEncryptionKey) : null
        }));

        const actSummaries: BookActSummariesTable[] = encryptedActSummaries.map((actSummary: BookActSummariesTable): BookActSummariesTable => ({
            ...actSummary,
            summary: actSummary.summary ? System.decryptDataWithUserKey(actSummary.summary, userEncryptionKey) : null
        }));

        const aiGuideLine: BookAIGuideLineTable[] = encryptedAIGuidelines.map((guideLine: BookAIGuideLineTable): BookAIGuideLineTable => ({
            ...guideLine,
            global_resume: guideLine.global_resume ? System.decryptDataWithUserKey(guideLine.global_resume, userEncryptionKey) : null,
            themes: guideLine.themes ? System.decryptDataWithUserKey(guideLine.themes, userEncryptionKey) : null,
            tone: guideLine.tone ? System.decryptDataWithUserKey(guideLine.tone, userEncryptionKey) : null,
            atmosphere: guideLine.atmosphere ? System.decryptDataWithUserKey(guideLine.atmosphere, userEncryptionKey) : null,
            current_resume: guideLine.current_resume ? System.decryptDataWithUserKey(guideLine.current_resume, userEncryptionKey) : null
        }));

        const chapters: BookChaptersTable[] = encryptedChapters.map((chapter: BookChaptersTable): BookChaptersTable => ({
            ...chapter,
            title: System.decryptDataWithUserKey(chapter.title, userEncryptionKey)
        }));

        const chapterContents: BookChapterContentTable[] = encryptedChapterContents.map((chapterContent: BookChapterContentTable): BookChapterContentTable => ({
            ...chapterContent,
            content: chapterContent.content ? JSON.parse(System.decryptDataWithUserKey(chapterContent.content, userEncryptionKey)) : null
        }));

        const chapterInfos: BookChapterInfosTable[] = encryptedChapterInfos.map((chapterInfo: BookChapterInfosTable): BookChapterInfosTable => ({
            ...chapterInfo,
            summary: chapterInfo.summary ? System.decryptDataWithUserKey(chapterInfo.summary, userEncryptionKey) : null,
            goal: chapterInfo.goal ? System.decryptDataWithUserKey(chapterInfo.goal, userEncryptionKey) : null
        }));

        const characters: BookCharactersTable[] = encryptedCharacters.map((character: BookCharactersTable): BookCharactersTable => ({
            ...character,
            first_name: System.decryptDataWithUserKey(character.first_name, userEncryptionKey),
            last_name: character.last_name ? System.decryptDataWithUserKey(character.last_name, userEncryptionKey) : null,
            nickname: character.nickname ? System.decryptDataWithUserKey(character.nickname, userEncryptionKey) : null,
            age: character.age ? System.decryptDataWithUserKey(character.age, userEncryptionKey) : null,
            gender: character.gender ? System.decryptDataWithUserKey(character.gender, userEncryptionKey) : null,
            species: character.species ? System.decryptDataWithUserKey(character.species, userEncryptionKey) : null,
            nationality: character.nationality ? System.decryptDataWithUserKey(character.nationality, userEncryptionKey) : null,
            status: character.status ? System.decryptDataWithUserKey(character.status, userEncryptionKey) : null,
            category: System.decryptDataWithUserKey(character.category, userEncryptionKey),
            title: character.title ? System.decryptDataWithUserKey(character.title, userEncryptionKey) : null,
            role: character.role ? System.decryptDataWithUserKey(character.role, userEncryptionKey) : null,
            biography: character.biography ? System.decryptDataWithUserKey(character.biography, userEncryptionKey) : null,
            history: character.history ? System.decryptDataWithUserKey(character.history, userEncryptionKey) : null,
            speech_pattern: character.speech_pattern ? System.decryptDataWithUserKey(character.speech_pattern, userEncryptionKey) : null,
            catchphrase: character.catchphrase ? System.decryptDataWithUserKey(character.catchphrase, userEncryptionKey) : null,
            residence: character.residence ? System.decryptDataWithUserKey(character.residence, userEncryptionKey) : null,
            notes: character.notes ? System.decryptDataWithUserKey(character.notes, userEncryptionKey) : null,
            color: character.color ? System.decryptDataWithUserKey(character.color, userEncryptionKey) : null
        }));

        const characterAttributes: BookCharactersAttributesTable[] = encryptedCharacterAttributes.map((attribute: BookCharactersAttributesTable): BookCharactersAttributesTable => ({
            ...attribute,
            attribute_name: System.decryptDataWithUserKey(attribute.attribute_name, userEncryptionKey),
            attribute_value: System.decryptDataWithUserKey(attribute.attribute_value, userEncryptionKey)
        }));

        const guideLine: BookGuideLineTable[] = encryptedGuidelines.map((guide: BookGuideLineTable): BookGuideLineTable => ({
            ...guide,
            tone: guide.tone ? System.decryptDataWithUserKey(guide.tone, userEncryptionKey) : null,
            atmosphere: guide.atmosphere ? System.decryptDataWithUserKey(guide.atmosphere, userEncryptionKey) : null,
            writing_style: guide.writing_style ? System.decryptDataWithUserKey(guide.writing_style, userEncryptionKey) : null,
            themes: guide.themes ? System.decryptDataWithUserKey(guide.themes, userEncryptionKey) : null,
            symbolism: guide.symbolism ? System.decryptDataWithUserKey(guide.symbolism, userEncryptionKey) : null,
            motifs: guide.motifs ? System.decryptDataWithUserKey(guide.motifs, userEncryptionKey) : null,
            narrative_voice: guide.narrative_voice ? System.decryptDataWithUserKey(guide.narrative_voice, userEncryptionKey) : null,
            pacing: guide.pacing ? System.decryptDataWithUserKey(guide.pacing, userEncryptionKey) : null,
            intended_audience: guide.intended_audience ? System.decryptDataWithUserKey(guide.intended_audience, userEncryptionKey) : null,
            key_messages: guide.key_messages ? System.decryptDataWithUserKey(guide.key_messages, userEncryptionKey) : null
        }));

        const incidents: BookIncidentsTable[] = encryptedIncidents.map((incident: BookIncidentsTable): BookIncidentsTable => ({
            ...incident,
            title: System.decryptDataWithUserKey(incident.title, userEncryptionKey),
            summary: incident.summary ? System.decryptDataWithUserKey(incident.summary, userEncryptionKey) : null
        }));

        const issues: BookIssuesTable[] = encryptedIssues.map((issue: BookIssuesTable): BookIssuesTable => ({
            ...issue,
            name: System.decryptDataWithUserKey(issue.name, userEncryptionKey)
        }));

        const locations: BookLocationTable[] = encryptedLocations.map((location: BookLocationTable): BookLocationTable => ({
            ...location,
            loc_name: System.decryptDataWithUserKey(location.loc_name, userEncryptionKey)
        }));

        const plotPoints: BookPlotPointsTable[] = encryptedPlotPoints.map((plotPoint: BookPlotPointsTable): BookPlotPointsTable => ({
            ...plotPoint,
            title: System.decryptDataWithUserKey(plotPoint.title, userEncryptionKey),
            summary: plotPoint.summary ? System.decryptDataWithUserKey(plotPoint.summary, userEncryptionKey) : null
        }));

        const worlds: BookWorldTable[] = encryptedWorlds.map((world: BookWorldTable): BookWorldTable => ({
            ...world,
            name: System.decryptDataWithUserKey(world.name, userEncryptionKey),
            history: world.history ? System.decryptDataWithUserKey(world.history, userEncryptionKey) : null,
            politics: world.politics ? System.decryptDataWithUserKey(world.politics, userEncryptionKey) : null,
            economy: world.economy ? System.decryptDataWithUserKey(world.economy, userEncryptionKey) : null,
            religion: world.religion ? System.decryptDataWithUserKey(world.religion, userEncryptionKey) : null,
            languages: world.languages ? System.decryptDataWithUserKey(world.languages, userEncryptionKey) : null
        }));

        const worldElements: BookWorldElementsTable[] = encryptedWorldElements.map((worldElement: BookWorldElementsTable): BookWorldElementsTable => ({
            ...worldElement,
            name: System.decryptDataWithUserKey(worldElement.name, userEncryptionKey),
            description: worldElement.description ? System.decryptDataWithUserKey(worldElement.description, userEncryptionKey) : null
        }));

        const locationElements: LocationElementTable[] = encryptedLocationElements.map((locationElement: LocationElementTable): LocationElementTable => ({
            ...locationElement,
            element_name: System.decryptDataWithUserKey(locationElement.element_name, userEncryptionKey),
            element_description: locationElement.element_description ? System.decryptDataWithUserKey(locationElement.element_description, userEncryptionKey) : null
        }));

        const locationSubElements: LocationSubElementTable[] = encryptedLocationSubElements.map((locationSubElement: LocationSubElementTable): LocationSubElementTable => ({
            ...locationSubElement,
            sub_elem_name: System.decryptDataWithUserKey(locationSubElement.sub_elem_name, userEncryptionKey),
            sub_elem_description: locationSubElement.sub_elem_description ? System.decryptDataWithUserKey(locationSubElement.sub_elem_description, userEncryptionKey) : null
        }));

        const bookTools: BookToolsTable[] = bookToolsData ? [bookToolsData] : [];

        const spells: BookSpellsTable[] = encryptedSpells.map((spell: BookSpellsTable): BookSpellsTable => ({
            ...spell,
            name: System.decryptDataWithUserKey(spell.name, userEncryptionKey),
            description: spell.description ? System.decryptDataWithUserKey(spell.description, userEncryptionKey) : null,
            appearance: spell.appearance ? System.decryptDataWithUserKey(spell.appearance, userEncryptionKey) : null,
            tags: spell.tags ? System.decryptDataWithUserKey(spell.tags, userEncryptionKey) : null,
            power_level: spell.power_level ? System.decryptDataWithUserKey(spell.power_level, userEncryptionKey) : null,
            components: spell.components ? System.decryptDataWithUserKey(spell.components, userEncryptionKey) : null,
            limitations: spell.limitations ? System.decryptDataWithUserKey(spell.limitations, userEncryptionKey) : null,
            notes: spell.notes ? System.decryptDataWithUserKey(spell.notes, userEncryptionKey) : null
        }));

        const spellTags: BookSpellTagsTable[] = encryptedSpellTags.map((spellTag: BookSpellTagsTable): BookSpellTagsTable => ({
            ...spellTag,
            name: System.decryptDataWithUserKey(spellTag.name, userEncryptionKey)
        }));

        return {
            eritBooks,
            actSummaries,
            aiGuideLine,
            chapters,
            chapterContents,
            chapterInfos,
            characters,
            characterAttributes,
            guideLine,
            incidents,
            issues,
            locations,
            plotPoints,
            worlds,
            worldElements,
            locationElements,
            locationSubElements,
            bookTools,
            spells,
            spellTags
        };
    }
}
