import { getUserEncryptionKey } from "../keyManager.js";
import System from "../System.js";
import PlotPoint, { PlotPointProps, PlotPointStory } from "./PlotPoint.js";
import Incident, { IncidentProps, IncidentStory } from "./Incident.js";
import ActRepository, { ActQuery } from "../repositories/act.repository.js";
import Chapter, { ChapterProps } from "./Chapter.js";
import IncidentRepository from "../repositories/incident.repository.js";
import PlotPointRepository from "../repositories/plotpoint.repository.js";
import ChapterRepo from "../repositories/chapter.repository.js";

export interface ActProps {
    id: number;
    summary: string | null;
    incidents?: IncidentProps[];
    plotPoints?: PlotPointProps[];
    chapters?: ActChapter[];
}

export interface ActStory {
    actId: number;
    summary: string;
    chapterSummary: string;
    chapterGoal: string;
    incidents: IncidentStory[];
    plotPoints: PlotPointStory[];
}

export interface ActChapter {
    chapterInfoId: number;
    chapterId: string;
    title: string;
    chapterOrder: number;
    actId: number;
    incidentId: string | null;
    plotPointId: string | null;
    summary: string;
    goal: string;
}

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

export default class Act {
    /**
     * Retrieves all acts data for a specific book, including chapters, incidents, and plot points.
     * Decrypts summaries using the user's encryption key.
     * @param userId - The unique identifier of the user
     * @param bookId - The unique identifier of the book
     * @param lang - The language for localization ('fr' or 'en'), defaults to 'fr'
     * @returns A promise resolving to an array of Act objects with their associated data
     */
    public static async getActsData(userId: string, bookId: string, lang: 'fr' | 'en' = 'fr'): Promise<ActProps[]> {
        const userEncryptionKey: string = getUserEncryptionKey(userId);
        const actChapters: ActChapter[] = Chapter.getAllChapterFromActs(userId, bookId, lang);
        const actQueries: ActQuery[] = ActRepository.fetchAllActs(userId, bookId, lang);
        const bookIncidents: IncidentProps[] = await Incident.getIncitentsIncidents(userId, bookId, actChapters);
        const bookPlotPoints: PlotPointProps[] = await PlotPoint.getPlotPoints(userId, bookId, actChapters);

        const acts: ActProps[] = [];

        acts.push({
            id: 1,
            summary: '',
            chapters: actChapters.filter((chapter: ActChapter) => chapter.actId === 1)
        });

        acts.push({
            id: 2,
            summary: '',
            incidents: bookIncidents ? bookIncidents : [],
        });

        acts.push({
            id: 3,
            summary: '',
            plotPoints: bookPlotPoints ? bookPlotPoints : [],
        });

        acts.push({
            id: 4,
            summary: '',
            chapters: actChapters.filter((chapter: ActChapter) => chapter.actId === 4)
        });

        acts.push({
            id: 5,
            summary: '',
            chapters: actChapters.filter((chapter: ActChapter) => chapter.actId === 5)
        });

        if (actQueries.length > 0) {
            for (const actQuery of actQueries) {
                acts[actQuery.act_index - 1].summary = actQuery.summary && userEncryptionKey
                    ? System.decryptDataWithUserKey(actQuery.summary, userEncryptionKey)
                    : '';
            }
        }

        return acts;
    }

    /**
     * Updates multiple acts including their summaries, incidents, plot points, and chapter information.
     * Encrypts all sensitive data before storing in the database.
     * @param acts - Array of act properties to update
     * @param userId - The unique identifier of the user
     * @param bookId - The unique identifier of the book
     * @param userKey - The user's encryption key for data encryption
     * @param lang - The language for localization ('fr' or 'en'), defaults to 'fr'
     * @returns A promise resolving to true when all updates are complete
     */
    public static async updateAct(acts: ActProps[], userId: string, bookId: string, userKey: string, lang: 'fr' | 'en' = 'fr'): Promise<boolean> {
        for (const act of acts) {
            const actIncidents: IncidentProps[] = act.incidents ? act.incidents : [];
            const actId: number = act.id;

            if (actId === 1 || actId === 4 || actId === 5) {
                const encryptedActSummary: string = act.summary ? System.encryptDataWithUserKey(act.summary, userKey) : '';
                try {
                    ActRepository.updateActSummary(userId, bookId, actId, encryptedActSummary, System.timeStampInSeconds(), lang);
                } catch (error: unknown) {
                    const newActSummaryId: string = System.createUniqueId();
                    ActRepository.insertActSummary(newActSummaryId, userId, bookId, actId, encryptedActSummary, lang);
                }
                if (act.chapters) {
                    Chapter.updateChapterInfos(act.chapters, userId, actId, bookId, null, null, lang);
                }
            } else if (actId === 2) {
                for (const incident of actIncidents) {
                    const encryptedIncidentSummary: string = incident.summary ? System.encryptDataWithUserKey(incident.summary, userKey) : '';
                    const incidentId: string = incident.incidentId;
                    const incidentTitle: string = incident.title;
                    const hashedIncidentTitle: string = System.hashElement(incidentTitle);
                    const encryptedIncidentTitle: string = System.encryptDataWithUserKey(incidentTitle, userKey);
                    IncidentRepository.updateIncident(userId, bookId, incidentId, encryptedIncidentTitle, hashedIncidentTitle, encryptedIncidentSummary, System.timeStampInSeconds(), lang);
                    if (incident.chapters) {
                        Chapter.updateChapterInfos(incident.chapters, userId, actId, bookId, incidentId, null, lang);
                    }
                }
            } else {
                const actPlotPoints: PlotPointProps[] = act.plotPoints ? act.plotPoints : [];
                for (const plotPoint of actPlotPoints) {
                    const encryptedPlotPointSummary: string = plotPoint.summary ? System.encryptDataWithUserKey(plotPoint.summary, userKey) : '';
                    const plotPointId: string = plotPoint.plotPointId;
                    const plotPointTitle: string = plotPoint.title;
                    const hashedPlotPointTitle: string = System.hashElement(plotPointTitle);
                    const encryptedPlotPointTitle: string = System.encryptDataWithUserKey(plotPointTitle, userKey);
                    PlotPointRepository.updatePlotPoint(userId, bookId, plotPointId, encryptedPlotPointTitle, hashedPlotPointTitle, encryptedPlotPointSummary, System.timeStampInSeconds(), lang);
                    if (plotPoint.chapters) {
                        Chapter.updateChapterInfos(plotPoint.chapters, userId, actId, bookId, null, plotPointId, lang);
                    }
                }
            }
        }
        return true;
    }

    /**
     * Updates the story structure including acts and main chapters.
     * Encrypts chapter titles and updates their order in the database.
     * @param userId - The unique identifier of the user
     * @param bookId - The unique identifier of the book
     * @param acts - Array of act properties to update
     * @param mainChapters - Array of main chapter properties to update
     * @param lang - The language for localization ('fr' or 'en'), defaults to 'fr'
     * @returns True when all updates are complete
     */
    public static updateStory(userId: string, bookId: string, acts: ActProps[], mainChapters: ChapterProps[], lang: 'fr' | 'en' = 'fr'): boolean {
        const userEncryptionKey: string = getUserEncryptionKey(userId);
        Act.updateAct(acts, userId, bookId, userEncryptionKey, lang).then();

        for (const chapter of mainChapters) {
            const chapterId: string = chapter.chapterId;
            const chapterTitle: string = chapter.title;
            const chapterOrder: number = chapter.chapterOrder;
            Chapter.updateChapter(userId, chapterId, chapterTitle, chapterOrder, lang);
        }

        return true;
    }
}
