import { ipcMain, dialog, BrowserWindow } from 'electron';
import { writeFile } from 'fs/promises';
import { createHandler } from '../database/LocalSystem.js';
import Book, {BookSyncCompare, CompleteBook, CompleteBookData, SyncedBook} from '../database/models/Book.js';
import type { BookProps } from '../database/models/Book.js';
import Chapter, {ChapterExportInfo} from '../database/models/Chapter.js';
import type { ChapterProps } from '../database/models/Chapter.js';
import {ChapterSelectionParam} from "../database/repositories/chapter.repository.js";
import Act, {ActProps} from "../database/models/Act.js";
import Issue, {IssueProps} from "../database/models/Issue.js";
import Sync from "../database/models/Sync.js";
import Download from "../database/models/Download.js";
import Upload from "../database/models/Upload.js";
import GuideLine, {GuideLineAI} from "../database/models/GuideLine.js";
import Incident from "../database/models/Incident.js";
import PlotPoint from "../database/models/PlotPoint.js";
import World, {WorldListResponse, WorldProps} from "../database/models/World.js";
import Export, {ExportResult} from "../database/models/Export.js";

interface UpdateBookBasicData {
    title: string;
    subTitle: string;
    summary: string;
    publicationDate: string;
    wordCount: number;
    bookId: string;
}

interface UpdateGuideLineData {
    bookId: string;
    tone: string | null;
    atmosphere: string | null;
    writingStyle: string | null;
    themes: string | null;
    symbolism: string | null;
    motifs: string | null;
    narrativeVoice: string | null;
    pacing: string | null;
    keyMessages: string | null;
    intendedAudience: string | null;
}

interface StoryData {
    acts: ActProps[];
    issues: IssueProps[];
    mainChapter: ChapterProps[];
}

interface UpdateStoryData {
    bookId: string;
    acts: ActProps[];
    mainChapters: ChapterProps[];
}

interface CreateBookData {
    title: string;
    subTitle: string | null;
    summary: string | null;
    type: string;
    serieId: number | null;
    desiredReleaseDate: string | null;
    desiredWordCount: number | null;
}

interface AddIncidentData {
    bookId: string;
    name: string;
    incidentId?: string;
}

interface AddPlotPointData {
    bookId: string;
    name: string;
    incidentId: string;
    plotPointId?: string;
}

interface AddIssueData {
    bookId: string;
    name: string;
    issueId?: string;
}

interface AddWorldData {
    bookId: string;
    worldName: string;
    id?: string;
    seriesWorldId?: string | null;
}

interface AddWorldElementData {
    worldId: string;
    elementName: string;
    elementType: number;
    id?: string;
}

interface SetAIGuideLineData {
    bookId: string;
    narrativeType: number;
    dialogueType: number;
    plotSummary: string;
    toneAtmosphere: string;
    verbTense: number;
    language: number;
    themes: string;
}

interface GetGuidelineData {
    id: string;
}

interface UpdateWorldData {
    bookId: string;
    world: WorldProps;
}

interface UpdateBookToolData {
    bookId: string;
    toolName: 'characters' | 'worlds' | 'locations' | 'spells';
    enabled: boolean;
}

// GET /books - Get all books
ipcMain.handle('db:book:books', createHandler<void, BookProps[]>(
        async function(userId: string, _body: void, lang: 'fr' | 'en'):Promise<BookProps[]> {
            return await Book.getBooks(userId, lang);
        }
    )
);

// GET /books/synced - Get all synced books
ipcMain.handle('db:books:synced', createHandler<void, SyncedBook[]>(
        async function(userId: string, _body: void, lang: 'fr' | 'en'):Promise<SyncedBook[]> {
            return await Sync.getSyncedBooks(userId, lang);
        })
);

// POST /book/sync/save - Save complete book
ipcMain.handle('db:book:syncSave', createHandler<CompleteBook, boolean>(
        async function(userId: string, data: CompleteBook, lang: 'fr' | 'en'):Promise<boolean> {
            return await Download.saveCompleteBook(userId, data, lang);
        })
);

// GET /book/:id - Get single book
ipcMain.handle('db:book:bookBasicInformation', createHandler<string, BookProps>(
        async function(userId: string, bookId: string, lang: 'fr' | 'en'):Promise<BookProps> {
            return await Book.getBook(userId, bookId, lang);
        }
    )
);

// GET
ipcMain.handle('db:book:uploadToServer', createHandler<string, CompleteBook>(
        async function(userId: string, bookId: string, lang: 'fr' | 'en'):Promise<CompleteBook> {
            return await Upload.uploadBookForSync(userId, bookId, lang);
        }
    )
);

// POST /book/basic-information - Update book basic info
ipcMain.handle('db:book:updateBasicInformation', createHandler<UpdateBookBasicData, boolean>(
        function(userId: string, data: UpdateBookBasicData, lang: 'fr' | 'en') {
            return Book.updateBookBasicInformation(userId, data.title, data.subTitle, data.summary, data.publicationDate, data.wordCount, data.bookId, lang);
        }
    )
);

// GET /book/sync/to-client - Get book data to sync to client
ipcMain.handle('db:book:sync:toServer', createHandler<BookSyncCompare, CompleteBook>(
        async function(userId: string, data:BookSyncCompare, lang: 'fr' | 'en'):Promise<CompleteBook> {
            return await Sync.getCompleteSyncBook(userId, data, lang);
        }
    )
);

// GET /book/sync/from-server - Get book data to sync from server
ipcMain.handle('db:book:sync:toClient', createHandler<CompleteBook, boolean>(
        async function(userId: string, data:CompleteBook,  lang: 'fr' | 'en'):Promise<boolean> {
            return await Sync.syncBookFromServerToClient(userId, data, lang);
        }
    )
);

// GET /book/guide-line - Get guideline
ipcMain.handle('db:book:guideline:get',
    createHandler<GetGuidelineData, GuideLine | null>(async function(userId: string, data: GetGuidelineData, lang: 'fr' | 'en') {
            return await GuideLine.getGuideLine(userId, data.id, lang);
        }
    )
);

// POST /book/guide-line - Update guideline
ipcMain.handle('db:book:guideline:update', createHandler<UpdateGuideLineData, boolean>(
        async function(userId: string, data: UpdateGuideLineData, lang: 'fr' | 'en') {
            return await GuideLine.updateGuideLine(
                userId,
                data.bookId,
                data.tone,
                data.atmosphere,
                data.writingStyle,
                data.themes,
                data.symbolism,
                data.motifs,
                data.narrativeVoice,
                data.pacing,
                data.keyMessages,
                data.intendedAudience,
                lang
            );
        }
    )
);

// GET /book/story - Get story data (acts + issues + mainChapter)
interface GetStoryData {
    bookid: string;
}
ipcMain.handle('db:book:story:get', createHandler<GetStoryData, StoryData>(
        async function(userId: string, data: GetStoryData, lang: 'fr' | 'en'):Promise<StoryData> {
            const acts:ActProps[] = await Act.getActsData(userId, data.bookid, lang);
            const issues:IssueProps[] = await Issue.getIssuesFromBook(userId, data.bookid, lang);
            const mainChapter:ChapterProps[] = Chapter.getAllChaptersFromABook(userId, data.bookid, lang);
            return {
                acts,
                issues,
                mainChapter
            };
        }
    )
);

// POST /book/story - Update story (acts + mainChapters)
ipcMain.handle('db:book:story:update', createHandler<UpdateStoryData, boolean>(
        function(userId: string, data: UpdateStoryData, lang: 'fr' | 'en'):boolean {
            return Act.updateStory(userId, data.bookId, data.acts, data.mainChapters, lang);
        }
    )
);

// POST /book/add - Create new book
ipcMain.handle('db:book:create', createHandler<CreateBookData, string>(
        function(userId: string, data: CreateBookData, lang: 'fr' | 'en') {
            return Book.addBook(
                null,
                userId,
                data.title,
                data.subTitle || '',
                data.summary || '',
                data.type,
                data.serieId || 0,
                data.desiredReleaseDate || '',
                data.desiredWordCount || 0,
                lang
            );
        }
    )
);


// POST /book/incident/new - Add incident
ipcMain.handle(
    'db:book:incident:add',
    createHandler<AddIncidentData, string>(
        function(userId: string, data: AddIncidentData, lang: 'fr' | 'en') {
            return Incident.addNewIncident(userId, data.bookId, data.name, lang, data.incidentId);
        }
    )
);

// DELETE /book/incident/remove - Remove incident
interface RemoveIncidentData {
    bookId: string;
    incidentId: string;
    deletedAt: number;
}

ipcMain.handle('db:book:incident:remove', createHandler<RemoveIncidentData, boolean>(
        function(userId: string, data: RemoveIncidentData, lang: 'fr' | 'en') {
            return Incident.removeIncident(userId, data.bookId, data.incidentId, data.deletedAt, lang);
        }
    )
);

// POST /book/plot/new - Add plot point
ipcMain.handle('db:book:plot:add', createHandler<AddPlotPointData, string>(
        function(userId: string, data: AddPlotPointData, lang: 'fr' | 'en'):string {
            return PlotPoint.addNewPlotPoint(
                userId,
                data.bookId,
                data.incidentId,
                data.name,
                lang,
                data.plotPointId
            );
        }
    )
);

// DELETE /book/plot/remove - Remove plot point
interface RemovePlotData {
    plotId: string;
    bookId: string;
    deletedAt: number;
}
ipcMain.handle(
    'db:book:plot:remove',
    createHandler<RemovePlotData, boolean>(
        function(userId: string, data: RemovePlotData, lang: 'fr' | 'en') {
            return PlotPoint.removePlotPoint(userId, data.bookId, data.plotId, data.deletedAt, lang);
        }
    )
);

// POST /book/issue/add - Add issue
ipcMain.handle('db:book:issue:add', createHandler<AddIssueData, string>(
        function(userId: string, data: AddIssueData, lang: 'fr' | 'en') {
            return Issue.addNewIssue(userId, data.bookId, data.name, lang, data.issueId);
        }
    )
);

// DELETE /book/issue/remove - Remove issue
interface RemoveIssueData {
    bookId: string;
    issueId: string;
    deletedAt: number;
}
ipcMain.handle('db:book:issue:remove', createHandler<RemoveIssueData, boolean>(
        function(userId: string, data: RemoveIssueData, lang: 'fr' | 'en') {
            return Issue.removeIssue(userId, data.bookId, data.issueId, data.deletedAt, lang);
        }
    )
);

// GET /book/worlds - Get worlds for book
interface GetWorldsData {
    bookid: string;
}
ipcMain.handle('db:book:worlds:get', createHandler<GetWorldsData, WorldListResponse>(
        function(userId: string, data: GetWorldsData, lang: 'fr' | 'en'): WorldListResponse {
            return World.getWorlds(userId, data.bookid, lang);
        }
    )
);

// POST /book/world/add - Add world
ipcMain.handle('db:book:world:add', createHandler<AddWorldData, string>(
        function(userId: string, data: AddWorldData, lang: 'fr' | 'en') {
            return World.addNewWorld(userId, data.bookId, data.worldName, lang, data.id, data.seriesWorldId || null);
        }
    )
);

// POST /book/world/element/add - Add element to world
ipcMain.handle('db:book:world:element:add', createHandler<AddWorldElementData, string>(
        function(userId: string, data: AddWorldElementData, lang: 'fr' | 'en') {
            return World.addNewElementToWorld(
                userId,
                data.worldId,
                data.elementName,
                data.elementType.toString(),
                lang,
                data.id
            );
        }
    )
);
// DELETE /book/world/element/delete - Remove element from world
interface RemoveWorldElementData {
    elementId: string;
    bookId: string;
    deletedAt: number;
}
ipcMain.handle('db:book:world:element:remove', createHandler<RemoveWorldElementData, boolean>(
        function(userId: string, data: RemoveWorldElementData, lang: 'fr' | 'en') {
            return World.removeElementFromWorld(userId, data.bookId, data.elementId, data.deletedAt, lang);
        }
    )
);

// DELETE /book/delete - Delete book
interface DeleteBookData {
    id: string;
    deletedAt: number;
}
ipcMain.handle('db:book:delete', createHandler<DeleteBookData, boolean>(
        function(userId: string, data: DeleteBookData, lang: 'fr' | 'en') {
            return Book.removeBook(userId, data.id, data.deletedAt, lang);
        }
    )
);

// GET /book/ai/guideline - Get AI guideline
interface GetAIGuidelineData {
    id: string;
}
ipcMain.handle('db:book:guideline:ai:get', createHandler<GetAIGuidelineData, GuideLineAI>(
        function(userId: string, data: GetAIGuidelineData, lang: 'fr' | 'en') {
            return GuideLine.getGuideLineAI(userId, data.id, lang);
        }
    )
);

// POST /book/ai/guideline (set) - Set AI guideline
ipcMain.handle('db:book:guideline:ai:set', createHandler<SetAIGuideLineData, boolean>(
        function(userId: string, data: SetAIGuideLineData, lang: 'fr' | 'en') {
            return GuideLine.setAIGuideLine(
                userId,
                data.bookId,
                data.narrativeType,
                data.dialogueType,
                data.plotSummary,
                data.toneAtmosphere,
                data.verbTense,
                data.language,
                data.themes,
                lang
            );
        }
    )
);

// PUT /book/world/update - Update world
ipcMain.handle('db:book:world:update', createHandler<UpdateWorldData, boolean>(
        function(userId: string, data: UpdateWorldData, lang: 'fr' | 'en') {
            return World.updateWorld(userId, data.world, lang);
        }
    )
);

// PATCH /book/tool-setting - Update book tool setting
ipcMain.handle('db:book:tool:update', createHandler<UpdateBookToolData, boolean>(
        function(userId: string, data: UpdateBookToolData, lang: 'fr' | 'en') {
            return Book.updateBookToolSetting(userId, data.bookId, data.toolName, data.enabled, lang);
        }
    )
);

// GET /book/export/info - Get chapters export info (available versions)
interface ExportInfoData {
    bookId: string;
}
ipcMain.handle('db:book:export:info', createHandler<ExportInfoData, ChapterExportInfo[]>(
        function(userId: string, data: ExportInfoData, lang: 'fr' | 'en'): ChapterExportInfo[] {
            return Chapter.getChaptersExportInfo(userId, data.bookId, lang);
        }
    )
);

// POST /book/export - Export book to file (EPUB/PDF/DOCX)
type ExportFormat = 'epub' | 'pdf' | 'docx';

interface ExportRequestData {
    bookId: string;
    format: ExportFormat;
    selections: ChapterSelectionParam[] | null;
}

const formatExtensions: Record<ExportFormat, {ext: string; filterName: string}> = {
    epub: {ext: 'epub', filterName: 'EPUB'},
    pdf: {ext: 'pdf', filterName: 'PDF'},
    docx: {ext: 'docx', filterName: 'Word Document'}
};

ipcMain.handle('db:book:export', createHandler<ExportRequestData, boolean>(
        async function(userId: string, data: ExportRequestData, lang: 'fr' | 'en'): Promise<boolean> {
            const bookData: CompleteBookData = Chapter.getCompleteBookDataWithSelections(userId, data.bookId, data.selections, lang);

            let result: ExportResult;
            switch (data.format) {
                case 'epub':
                    result = await Export.transformToEpub(bookData);
                    break;
                case 'pdf':
                    result = await Export.transformToPDF(bookData);
                    break;
                case 'docx':
                    result = await Export.transformToDOCX(bookData);
                    break;
                default:
                    throw new Error(lang === 'fr' ? 'Format non supporté.' : 'Unsupported format.');
            }

            const formatInfo = formatExtensions[data.format];
            const focusedWindow: BrowserWindow | null = BrowserWindow.getFocusedWindow();
            const dialogResult = await dialog.showSaveDialog(focusedWindow!, {
                defaultPath: result.fileName,
                filters: [{name: formatInfo.filterName, extensions: [formatInfo.ext]}]
            });

            if (dialogResult.canceled || !dialogResult.filePath) {
                return false;
            }

            await writeFile(dialogResult.filePath, result.buffer);
            return true;
        }
    )
);
