import React, {Dispatch, SetStateAction, useContext, useState} from 'react';
import {Flag, Flame, LucideIcon, Puzzle, Scale, Trophy} from 'lucide-react';
import {Act as ActType, Incident, PlotPoint} from '@/lib/types/book';
import {ActChapter, ChapterListProps} from '@/lib/types/chapter';
import {apiDelete, apiPost} from '@/lib/api/client';
import {isDesktop} from '@/lib/configs';
import * as tauri from '@/lib/tauri';
import OfflineContext, {OfflineContextType} from '@/context/OfflineContext';
import {BooksSyncContext, BooksSyncContextProps} from '@/context/BooksSyncContext';
import {SyncedBook} from '@/lib/types/synced-book';
import {LocalSyncQueueContext, LocalSyncQueueContextProps} from '@/context/SyncQueueContext';
import {BookContext, BookContextProps} from '@/context/BookContext';
import {SessionContext, SessionContextProps} from '@/context/SessionContext';
import {AlertContext, AlertContextProps} from '@/context/AlertContext';
import Collapse from '@/components/ui/Collapse';
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 '@/lib/i18n';
import {LangContext, LangContextProps} from "@/context/LangContext";

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

export default function Act({acts, setActs, mainChapters}: ActProps) {
    const t = useTranslations('actComponent');
    const {lang}: LangContextProps = useContext<LangContextProps>(LangContext);
    const {book}: BookContextProps = useContext<BookContextProps>(BookContext);
    const {session}: SessionContextProps = useContext<SessionContextProps>(SessionContext);
    const {errorMessage, successMessage}: AlertContextProps = useContext<AlertContextProps>(AlertContext);
    const {isCurrentlyOffline}: OfflineContextType = useContext<OfflineContextType>(OfflineContext);
    const {addToQueue}: LocalSyncQueueContextProps = useContext<LocalSyncQueueContextProps>(LocalSyncQueueContext);
    const {localSyncedBooks}: BooksSyncContextProps = useContext<BooksSyncContextProps>(BooksSyncContext);

    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 (isDesktop && (isCurrentlyOffline() || book?.localBook)) {
                incidentId = await tauri.addIncident(bookId ?? '', newIncidentTitle);
            } else {
                const addData = {bookId, name: newIncidentTitle};
                incidentId = await apiPost<string>('book/incident/new', addData, token, lang);

                if (isDesktop && bookId && localSyncedBooks.find((sb: SyncedBook): boolean => sb.id === bookId)) {
                    addToQueue('add_incident', addData);
                }
            }
            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;
            if (isDesktop && (isCurrentlyOffline() || book?.localBook)) {
                response = await tauri.removeIncident(bookId ?? '', incidentId, Date.now());
            } else {
                const deleteData = {bookId, incidentId};
                response = await apiDelete<boolean>('book/incident/remove', deleteData, token, lang);

                if (isDesktop && bookId && localSyncedBooks.find((sb: SyncedBook): boolean => sb.id === bookId)) {
                    addToQueue('remove_incident', {...deleteData, deletedAt: Date.now()});
                }
            }
            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;
            if (isDesktop && (isCurrentlyOffline() || book?.localBook)) {
                plotId = await tauri.addPlotPoint(bookId ?? '', newPlotPointTitle, selectedIncidentId);
            } else {
                const plotData = {bookId, name: newPlotPointTitle, incidentId: selectedIncidentId};
                plotId = await apiPost<string>('book/plot/new', plotData, token, lang);

                if (isDesktop && bookId && localSyncedBooks.find((sb: SyncedBook): boolean => sb.id === bookId)) {
                    addToQueue('add_plot_point', plotData);
                }
            }
            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;
            if (isDesktop && (isCurrentlyOffline() || book?.localBook)) {
                response = await tauri.removePlotPoint(plotPointId, bookId ?? '', Date.now());
            } else {
                const deleteData = {plotId: plotPointId};
                response = await apiDelete<boolean>('book/plot/remove', deleteData, token, lang);

                if (isDesktop && bookId && localSyncedBooks.find((sb: SyncedBook): boolean => sb.id === bookId)) {
                    addToQueue('remove_plot_point', {...deleteData, bookId, deletedAt: Date.now()});
                }
            }
            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;
            if (isDesktop && (isCurrentlyOffline() || book?.localBook)) {
                linkId = await tauri.addChapterInformation({
                    chapterId: chapterId,
                    actId: actId,
                    bookId: bookId ?? '',
                    plotId: destination === 'plotPoint' ? itemId : undefined,
                    incidentId: destination === 'incident' ? itemId : undefined,
                });
            } else {
                const linkData = {bookId, chapterId, actId, plotId: destination === 'plotPoint' ? itemId : null, incidentId: destination === 'incident' ? itemId : null};
                linkId = await apiPost<string>('chapter/resume/add', linkData, token, lang);

                if (isDesktop && bookId && localSyncedBooks.find((sb: SyncedBook): boolean => sb.id === bookId)) {
                    addToQueue('add_chapter_information', linkData);
                }
            }
            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;
            if (isDesktop && (isCurrentlyOffline() || book?.localBook)) {
                response = await tauri.removeChapterInformation(chapterInfoId, bookId ?? '', Date.now());
            } else {
                const removeData = {chapterInfoId};
                response = await apiDelete<boolean>('chapter/resume/remove', removeData, token, lang);

                if (isDesktop && bookId && localSyncedBooks.find((sb: SyncedBook): boolean => sb.id === bookId)) {
                    addToQueue('remove_chapter_information', {...removeData, bookId, deletedAt: Date.now()});
                }
            }
            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): LucideIcon {
        switch (actId) {
            case 1:
                return Flag;
            case 2:
                return Flame;
            case 3:
                return Puzzle;
            case 4:
                return Scale;
            case 5:
                return Trophy;
            default:
                return Flag;
        }
    }
    
    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) => (
                <Collapse variant="card" key={`act-${act.id}`}
                                 title={renderActTitle(act.id)}
                                 icon={renderActIcon(act.id)}
                                 children={
                                     <>
                                         {renderActDescription(act)}
                                         {renderActChapters(act)}
                                         {renderIncidents(act)}
                                         {renderPlotPoints(act)}
                                     </>
                                 }
                />
            ))}
        </div>
    );
}