import {Dispatch, SetStateAction, useContext, useState} from 'react';
import {
    faFire,
    faFlag,
    faPuzzlePiece,
    faScaleBalanced,
    faTrophy,
    IconDefinition,
} from '@fortawesome/free-solid-svg-icons';
import {Act as ActType, Incident, PlotPoint} from '@/lib/models/Book';
import {ActChapter, ChapterListProps} from '@/lib/models/Chapter';
import System from '@/lib/models/System';
import {BookContext} from '@/context/BookContext';
import {SessionContext} from '@/context/SessionContext';
import {AlertContext} from '@/context/AlertContext';
import CollapsableArea from '@/components/CollapsableArea';
import ActDescription from '@/components/book/settings/story/act/ActDescription';
import ActChaptersSection from '@/components/book/settings/story/act/ActChaptersSection';
import ActIncidents from '@/components/book/settings/story/act/ActIncidents';
import ActPlotPoints from '@/components/book/settings/story/act/ActPlotPoints';
import {useTranslations} from 'next-intl';
import {LangContext, LangContextProps} from "@/context/LangContext";
import OfflineContext, {OfflineContextType} from "@/context/OfflineContext";
import {LocalSyncQueueContext, LocalSyncQueueContextProps} from "@/context/SyncQueueContext";
import {BooksSyncContext, BooksSyncContextProps} from "@/context/BooksSyncContext";
import {SyncedBook} from "@/lib/models/SyncedBook";

interface ActProps {
    acts: ActType[];
    setActs: Dispatch<SetStateAction<ActType[]>>;
    mainChapters: ChapterListProps[];
}

export default function Act({acts, setActs, mainChapters}: ActProps) {
    const t = useTranslations('actComponent');
    const {lang} = useContext<LangContextProps>(LangContext);
    const {isCurrentlyOffline} = useContext<OfflineContextType>(OfflineContext);
    const {addToQueue} = useContext<LocalSyncQueueContextProps>(LocalSyncQueueContext);
    const {localSyncedBooks} = useContext<BooksSyncContextProps>(BooksSyncContext);
    const {book} = useContext(BookContext);
    const {session} = useContext(SessionContext);
    const {errorMessage, successMessage} = useContext(AlertContext);
    
    const bookId: string | undefined = book?.bookId;
    const token: string = session.accessToken;
    
    const [expandedSections, setExpandedSections] = useState<{
        [key: string]: boolean;
    }>({});

    const [newIncidentTitle, setNewIncidentTitle] = useState<string>('');
    const [newPlotPointTitle, setNewPlotPointTitle] = useState<string>('');
    const [selectedIncidentId, setSelectedIncidentId] = useState<string>('');
    
    function toggleSection(sectionKey: string): void {
        setExpandedSections(prev => ({
            ...prev,
            [sectionKey]: !prev[sectionKey],
        }));
    }
    
    function updateActSummary(actId: number, summary: string): void {
        const updatedActs: ActType[] = acts.map((act: ActType): ActType => {
            if (act.id === actId) {
                return {...act, summary};
            }
            return act;
        });
        setActs(updatedActs);
    }
    
    function getIncidents(): Incident[] {
        const act2: ActType | undefined = acts.find((act: ActType): boolean => act.id === 2);
        return act2?.incidents || [];
    }
    
    async function addIncident(actId: number): Promise<void> {
        if (newIncidentTitle.trim() === '') return;

        try {
            let incidentId: string;
            if (isCurrentlyOffline() || book?.localBook) {
                incidentId = await window.electron.invoke<string>('db:book:incident:add', {
                    bookId,
                    name: newIncidentTitle,
                });
            } else {
                incidentId = await System.authPostToServer<string>('book/incident/new', {
                    bookId,
                    name: newIncidentTitle,
                }, token, lang);

                if (localSyncedBooks.find((syncedBook: SyncedBook): boolean => syncedBook.id === bookId)) {
                    addToQueue('db:book:incident:add', {
                        bookId,
                        incidentId,
                        name: newIncidentTitle,
                    });
                }
            }
            if (!incidentId) {
                errorMessage(t('errorAddIncident'));
                return;
            }
            const updatedActs: ActType[] = acts.map((act: ActType): ActType => {
                if (act.id === actId) {
                    const newIncident: Incident = {
                        incidentId: incidentId,
                        title: newIncidentTitle,
                        summary: '',
                        chapters: [],
                    };

                    return {
                        ...act,
                        incidents: [...(act.incidents || []), newIncident],
                    };
                }
                return act;
            });
            setActs(updatedActs);
            setNewIncidentTitle('');
        } catch (e: unknown) {
            if (e instanceof Error) {
                errorMessage(t('errorAddIncident'));
            } else {
                errorMessage(t('errorUnknownAddIncident'));
            }
        }
    }
    
    async function deleteIncident(actId: number, incidentId: string): Promise<void> {
        try {
            let response: boolean;
            const deleteData = { bookId, incidentId };
            if (isCurrentlyOffline() || book?.localBook) {
                response = await window.electron.invoke<boolean>('db:book:incident:remove', deleteData);
            } else {
                response = await System.authDeleteToServer<boolean>('book/incident/remove', deleteData, token, lang);

                if (localSyncedBooks.find((syncedBook: SyncedBook): boolean => syncedBook.id === bookId)) {
                    addToQueue('db:book:incident:remove', deleteData);
                }
            }
            if (!response) {
                errorMessage(t('errorDeleteIncident'));
                return;
            }
            const updatedActs: ActType[] = acts.map((act: ActType): ActType => {
                if (act.id === actId) {
                    return {
                        ...act,
                        incidents: (act.incidents || []).filter(
                            (inc: Incident): boolean => inc.incidentId !== incidentId,
                        ),
                    };
                }
                return act;
            });
            setActs(updatedActs);
        } catch (e: unknown) {
            if (e instanceof Error) {
                errorMessage(e.message);
            } else {
                errorMessage(t('errorUnknownDeleteIncident'));
            }
        }
    }
    
    async function addPlotPoint(actId: number): Promise<void> {
        if (newPlotPointTitle.trim() === '') return;
        try {
            let plotId: string;
            const plotData = {
                bookId,
                name: newPlotPointTitle,
                incidentId: selectedIncidentId,
            };
            if (isCurrentlyOffline() || book?.localBook) {
                plotId = await window.electron.invoke<string>('db:book:plot:add', plotData);
            } else {
                plotId = await System.authPostToServer<string>('book/plot/new', plotData, token, lang);

                if (localSyncedBooks.find((syncedBook: SyncedBook): boolean => syncedBook.id === bookId)) {
                    addToQueue('db:book:plot:add', {
                        ...plotData,
                        plotId,
                    });
                }
            }
            if (!plotId) {
                errorMessage(t('errorAddPlotPoint'));
                return;
            }
            const updatedActs: ActType[] = acts.map((act: ActType): ActType => {
                if (act.id === actId) {
                    const newPlotPoint: PlotPoint = {
                        plotPointId: plotId,
                        title: newPlotPointTitle,
                        summary: '',
                        linkedIncidentId: selectedIncidentId,
                        chapters: [],
                    };
                    return {
                        ...act,
                        plotPoints: [...(act.plotPoints || []), newPlotPoint],
                    };
                }
                return act;
            });
            setActs(updatedActs);
            setNewPlotPointTitle('');
            setSelectedIncidentId('');
        } catch (e: unknown) {
            if (e instanceof Error) {
                errorMessage(t('errorAddPlotPoint'));
            } else {
                errorMessage(t('errorUnknownAddPlotPoint'));
            }
        }
    }
    
    async function deletePlotPoint(actId: number, plotPointId: string): Promise<void> {
        try {
            let response: boolean;
            const deleteData = { plotId: plotPointId };
            if (isCurrentlyOffline() || book?.localBook) {
                response = await window.electron.invoke<boolean>('db:book:plot:remove', deleteData);
            } else {
                response = await System.authDeleteToServer<boolean>('book/plot/remove', deleteData, token, lang);

                if (localSyncedBooks.find((syncedBook: SyncedBook): boolean => syncedBook.id === bookId)) {
                    addToQueue('db:book:plot:remove', deleteData);
                }
            }
            if (!response) {
                errorMessage(t('errorDeletePlotPoint'));
                return;
            }
            const updatedActs: ActType[] = acts.map((act: ActType): ActType => {
                if (act.id === actId) {
                    return {
                        ...act,
                        plotPoints: (act.plotPoints || []).filter(
                            (pp: PlotPoint): boolean => pp.plotPointId !== plotPointId,
                        ),
                    };
                }
                return act;
            });
            setActs(updatedActs);
        } catch (e: unknown) {
            if (e instanceof Error) {
                errorMessage(e.message);
            } else {
                errorMessage(t('errorUnknownDeletePlotPoint'));
            }
        }
    }
    
    async function linkChapter(
        actId: number,
        chapterId: string,
        destination: 'act' | 'incident' | 'plotPoint',
        itemId?: string,
    ): Promise<void> {
        const chapterToLink: ChapterListProps | undefined = mainChapters.find((chapter: ChapterListProps): boolean => chapter.chapterId === chapterId);
        if (!chapterToLink) {
            errorMessage(t('errorChapterNotFound'));
            return;
        }
        try {
            let linkId: string;
            const linkData = {
                bookId,
                chapterId: chapterId,
                actId: actId,
                plotId: destination === 'plotPoint' ? itemId : null,
                incidentId: destination === 'incident' ? itemId : null,
            };
            if (isCurrentlyOffline() || book?.localBook) {
                linkId = await window.electron.invoke<string>('db:chapter:information:add', linkData);
            } else {
                linkId = await System.authPostToServer<string>('chapter/resume/add', linkData, token, lang);

                if (localSyncedBooks.find((syncedBook: SyncedBook): boolean => syncedBook.id === bookId)) {
                    addToQueue('db:chapter:information:add', {
                        ...linkData,
                        chapterInfoId: linkId,
                    });
                }
            }
            if (!linkId) {
                errorMessage(t('errorLinkChapter'));
                return;
            }
            const newChapter: ActChapter = {
                chapterInfoId: linkId,
                chapterId: chapterId,
                title: chapterToLink.title,
                chapterOrder: chapterToLink.chapterOrder || 0,
                actId: actId,
                incidentId: destination === 'incident' ? itemId : '0',
                plotPointId: destination === 'plotPoint' ? itemId : '0',
                summary: '',
                goal: '',
            };
            
            const updatedActs: ActType[] = acts.map((act: ActType): ActType => {
                if (act.id === actId) {
                    switch (destination) {
                        case 'act':
                            return {
                                ...act,
                                chapters: [...(act.chapters || []), newChapter],
                            };
                        case 'incident':
                            return {
                                ...act,
                                incidents:
                                    act.incidents?.map((incident: Incident): Incident =>
                                        incident.incidentId === itemId
                                            ? {
                                                ...incident,
                                                chapters: [...(incident.chapters || []), newChapter],
                                            }
                                            : incident,
                                    ) || [],
                            };
                        case 'plotPoint':
                            return {
                                ...act,
                                plotPoints:
                                    act.plotPoints?.map(
                                        (plotPoint: PlotPoint): PlotPoint =>
                                            plotPoint.plotPointId === itemId
                                                ? {
                                                    ...plotPoint,
                                                    chapters: [...(plotPoint.chapters || []), newChapter],
                                                }
                                                : plotPoint,
                                    ) || [],
                            };
                    }
                }
                return act;
            });
            
            setActs(updatedActs);
        } catch (e: unknown) {
            if (e instanceof Error) {
                errorMessage(e.message);
            } else {
                errorMessage(t('errorUnknownLinkChapter'));
            }
        }
    }
    
    async function unlinkChapter(
        chapterInfoId: string,
        actId: number,
        chapterId: string,
        destination: 'act' | 'incident' | 'plotPoint',
        itemId?: string,
    ): Promise<void> {
        try {
            let response: boolean;
            const unlinkData = { chapterInfoId };
            if (isCurrentlyOffline() || book?.localBook) {
                response = await window.electron.invoke<boolean>('db:chapter:information:remove', unlinkData);
            } else {
                response = await System.authDeleteToServer<boolean>('chapter/resume/remove', unlinkData, token, lang);

                if (localSyncedBooks.find((syncedBook: SyncedBook): boolean => syncedBook.id === bookId)) {
                    addToQueue('db:chapter:information:remove', unlinkData);
                }
            }
            if (!response) {
                errorMessage(t('errorUnlinkChapter'));
                return;
            }
            const updatedActs: ActType[] = acts.map((act: ActType): ActType => {
                if (act.id === actId) {
                    switch (destination) {
                        case 'act':
                            return {
                                ...act,
                                chapters: (act.chapters || []).filter(
                                    (ch: ActChapter): boolean => ch.chapterId !== chapterId,
                                ),
                            };
                        
                        case 'incident':
                            if (!itemId) return act;
                            
                            return {
                                ...act,
                                incidents:
                                    act.incidents?.map((incident: Incident): Incident => {
                                        if (incident.incidentId === itemId) {
                                            return {
                                                ...incident,
                                                chapters: (incident.chapters || []).filter(
                                                    (ch: ActChapter): boolean =>
                                                        ch.chapterId !== chapterId,
                                                ),
                                            };
                                        }
                                        return incident;
                                    }) || [],
                            };
                        
                        case 'plotPoint':
                            if (!itemId) return act;
                            
                            return {
                                ...act,
                                plotPoints:
                                    act.plotPoints?.map((plotPoint: PlotPoint): PlotPoint => {
                                        if (plotPoint.plotPointId === itemId) {
                                            return {
                                                ...plotPoint,
                                                chapters: (plotPoint.chapters || []).filter((chapter: ActChapter): boolean => chapter.chapterId !== chapterId),
                                            };
                                        }
                                        return plotPoint;
                                    }) || [],
                            };
                    }
                }
                return act;
            });
            
            setActs(updatedActs);
        } catch (e: unknown) {
            if (e instanceof Error) {
                errorMessage(e.message);
            } else {
                errorMessage(t('errorUnknownUnlinkChapter'));
            }
        }
    }
    
    function updateLinkedChapterSummary(
        actId: number,
        chapterId: string,
        summary: string,
        destination: 'act' | 'incident' | 'plotPoint',
        itemId?: string,
    ): void {
        const updatedActs: ActType[] = acts.map((act: ActType): ActType => {
            if (act.id === actId) {
                switch (destination) {
                    case 'act':
                        return {
                            ...act,
                            chapters: (act.chapters || []).map((chapter: ActChapter): ActChapter => {
                                if (chapter.chapterId === chapterId) {
                                    return {...chapter, summary};
                                }
                                return chapter;
                            }),
                        };
                    
                    case 'incident':
                        if (!itemId) return act;
                        
                        return {
                            ...act,
                            incidents:
                                act.incidents?.map((incident: Incident): Incident => {
                                    if (incident.incidentId === itemId) {
                                        return {
                                            ...incident,
                                            chapters: (incident.chapters || []).map((chapter: ActChapter) => {
                                                if (chapter.chapterId === chapterId) {
                                                    return {...chapter, summary};
                                                }
                                                return chapter;
                                            }),
                                        };
                                    }
                                    return incident;
                                }) || [],
                        };
                    
                    case 'plotPoint':
                        if (!itemId) return act;
                        
                        return {
                            ...act,
                            plotPoints:
                                act.plotPoints?.map((plotPoint: PlotPoint): PlotPoint => {
                                    if (plotPoint.plotPointId === itemId) {
                                        return {
                                            ...plotPoint,
                                            chapters: (plotPoint.chapters || []).map((chapter: ActChapter): ActChapter => {
                                                if (chapter.chapterId === chapterId) {
                                                    return {...chapter, summary};
                                                }
                                                return chapter;
                                            }),
                                        };
                                    }
                                    return plotPoint;
                                }) || [],
                        };
                }
            }
            return act;
        });
        setActs(updatedActs);
    }
    
    function getSectionKey(actId: number, section: string): string {
        return `section_${actId}_${section}`;
    }
    
    
    function renderActChapters(act: ActType) {
        if (act.id === 2 || act.id === 3) {
            return null;
        }

        const sectionKey: string = getSectionKey(act.id, 'chapters');
        const isExpanded: boolean = expandedSections[sectionKey];

        return (
            <ActChaptersSection
                actId={act.id}
                chapters={act.chapters || []}
                mainChapters={mainChapters}
                onLinkChapter={(actId, chapterId) => linkChapter(actId, chapterId, 'act')}
                onUpdateChapterSummary={(chapterId, summary) =>
                    updateLinkedChapterSummary(act.id, chapterId, summary, 'act')
                }
                onUnlinkChapter={(chapterInfoId, chapterId) =>
                    unlinkChapter(chapterInfoId, act.id, chapterId, 'act')
                }
                sectionKey={sectionKey}
                isExpanded={isExpanded}
                onToggleSection={toggleSection}
            />
        );
    }
    
    function renderActDescription(act: ActType) {
        if (act.id === 2 || act.id === 3) {
            return null;
        }
        
        return (
            <ActDescription
                actId={act.id}
                summary={act.summary || ''}
                onUpdateSummary={updateActSummary}
            />
        );
    }
    
    function renderIncidents(act: ActType) {
        if (act.id !== 2) return null;
        
        const sectionKey: string = getSectionKey(act.id, 'incidents');
        const isExpanded: boolean = expandedSections[sectionKey];
        
        return (
            <ActIncidents
                incidents={act.incidents || []}
                actId={act.id}
                mainChapters={mainChapters}
                newIncidentTitle={newIncidentTitle}
                setNewIncidentTitle={setNewIncidentTitle}
                onAddIncident={addIncident}
                onDeleteIncident={deleteIncident}
                onLinkChapter={(actId, chapterId, incidentId) =>
                    linkChapter(actId, chapterId, 'incident', incidentId)
                }
                onUpdateChapterSummary={(chapterId, summary, incidentId) =>
                    updateLinkedChapterSummary(act.id, chapterId, summary, 'incident', incidentId)
                }
                onUnlinkChapter={(chapterInfoId, chapterId, incidentId) =>
                    unlinkChapter(chapterInfoId, act.id, chapterId, 'incident', incidentId)
                }
                sectionKey={sectionKey}
                isExpanded={isExpanded}
                onToggleSection={toggleSection}
            />
        );
    }
    
    function renderPlotPoints(act: ActType) {
        if (act.id !== 3) return null;
        
        const sectionKey: string = getSectionKey(act.id, 'plotPoints');
        const isExpanded: boolean = expandedSections[sectionKey];
        
        return (
            <ActPlotPoints
                plotPoints={act.plotPoints || []}
                incidents={getIncidents()}
                actId={act.id}
                mainChapters={mainChapters}
                newPlotPointTitle={newPlotPointTitle}
                setNewPlotPointTitle={setNewPlotPointTitle}
                selectedIncidentId={selectedIncidentId}
                setSelectedIncidentId={setSelectedIncidentId}
                onAddPlotPoint={addPlotPoint}
                onDeletePlotPoint={deletePlotPoint}
                onLinkChapter={(actId, chapterId, plotPointId) =>
                    linkChapter(actId, chapterId, 'plotPoint', plotPointId)
                }
                onUpdateChapterSummary={(chapterId, summary, plotPointId) =>
                    updateLinkedChapterSummary(act.id, chapterId, summary, 'plotPoint', plotPointId)
                }
                onUnlinkChapter={(chapterInfoId, chapterId, plotPointId) =>
                    unlinkChapter(chapterInfoId, act.id, chapterId, 'plotPoint', plotPointId)
                }
                sectionKey={sectionKey}
                isExpanded={isExpanded}
                onToggleSection={toggleSection}
            />
        );
    }
    
    function renderActIcon(actId: number): IconDefinition {
        switch (actId) {
            case 1:
                return faFlag;
            case 2:
                return faFire;
            case 3:
                return faPuzzlePiece;
            case 4:
                return faScaleBalanced;
            case 5:
                return faTrophy;
            default:
                return faFlag;
        }
    }
    
    function renderActTitle(actId: number): string {
        switch (actId) {
            case 1:
                return t('act1Title');
            case 2:
                return t('act2Title');
            case 3:
                return t('act3Title');
            case 4:
                return t('act4Title');
            case 5:
                return t('act5Title');
            default:
                return '';
        }
    }
    
    return (
        <div className="space-y-6">
            {acts.map((act: ActType) => (
                <CollapsableArea key={`act-${act.id}`}
                                 title={renderActTitle(act.id)}
                                 icon={renderActIcon(act.id)}
                                 children={
                                     <>
                                         {renderActDescription(act)}
                                         {renderActChapters(act)}
                                         {renderIncidents(act)}
                                         {renderPlotPoints(act)}
                                     </>
                                 }
                />
            ))}
        </div>
    );
}