import {Database, QueryResult, RunResult, SQLiteValue} from "node-sqlite3-wasm";
import System from "../System.js";

export interface ChapterContentQueryResult extends Record<string, SQLiteValue> {
    chapter_id: string;
    version: number;
    content: string;
    words_count: number;
    title: string;
    chapter_order: number;
}

export interface ContentQueryResult extends Record<string, SQLiteValue> {
    content: string;
}

export interface CompanionContentQueryResult extends Record<string, SQLiteValue> {
    version: number;
    content: string;
    words_count: number;
}

export interface BookChapterContentTable extends Record<string, SQLiteValue> {
    content_id: string;
    chapter_id: string;
    author_id: string;
    version: number;
    content: string | null;
    words_count: number;
    time_on_it: number;
    last_update: number;
}

export interface SyncedChapterContentResult extends Record<string, SQLiteValue> {
    content_id: string;
    chapter_id: string;
    last_update: number;
}

export default class ChapterContentRepository {
    /**
     * Fetches the last chapter content for a given book.
     * @param userId - The ID of the user/author.
     * @param bookId - The ID of the book.
     * @param lang - The language for error messages ('fr' or 'en').
     * @returns An array of chapter content results ordered by chapter order and version descending.
     */
    public static fetchLastChapterContent(userId: string, bookId: string, lang: 'fr' | 'en' = 'fr'): ChapterContentQueryResult[] {
        try {
            const db: Database = System.getDb();
            const query: string = `
                SELECT
                    book_chapters.chapter_id as chapter_id,
                    COALESCE(book_chapter_content.version, 2) AS version,
                    COALESCE(book_chapter_content.content, '') AS content,
                    COALESCE(book_chapter_content.words_count, 0) AS words_count,
                    book_chapters.title,
                    book_chapters.chapter_order
                FROM book_chapters
                LEFT JOIN book_chapter_content ON book_chapters.chapter_id = book_chapter_content.chapter_id
                WHERE book_chapters.author_id = ? AND book_chapters.book_id = ?
                ORDER BY book_chapters.chapter_order DESC, book_chapter_content.version DESC
                LIMIT 1
            `;
            const params: SQLiteValue[] = [userId, bookId];
            return db.all(query, params) as ChapterContentQueryResult[];
        } catch (error: unknown) {
            if (error instanceof Error) {
                console.error(`DB Error: ${error.message}`);
                throw new Error(lang === 'fr' ? `Impossible de récupérer le dernier chapitre.` : `Unable to retrieve last chapter.`);
            } else {
                console.error("An unknown error occurred.");
                throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
            }
        }
    }

    /**
     * Updates the content of a chapter. If no existing content is found, inserts a new record.
     * @param userId - The ID of the user/author.
     * @param chapterId - The ID of the chapter.
     * @param version - The version number of the content.
     * @param encryptContent - The encrypted content string.
     * @param wordsCount - The word count of the content.
     * @param lastUpdate - The timestamp of the last update.
     * @param lang - The language for error messages ('fr' or 'en').
     * @returns True if the update or insert was successful.
     */
    public static updateChapterContent(userId: string, chapterId: string, version: number, encryptContent: string, wordsCount: number, lastUpdate: number, lang: 'fr' | 'en' = 'fr'): boolean {
        try {
            const db: Database = System.getDb();
            const updateQuery: string = 'UPDATE book_chapter_content SET content=?, words_count=?, last_update=? WHERE chapter_id=? AND author_id=? AND version=?';
            const updateParams: SQLiteValue[] = [encryptContent, wordsCount, lastUpdate, chapterId, userId, version];
            const updateResult: RunResult = db.run(updateQuery, updateParams);

            if (updateResult.changes > 0) {
                return true;
            } else {
                const contentId: string = System.createUniqueId();
                const insertQuery: string = 'INSERT INTO book_chapter_content (content_id, chapter_id, author_id, version, content, words_count, last_update) VALUES (?,?,?,?,?,?,?)';
                const insertParams: SQLiteValue[] = [contentId, chapterId, userId, version, encryptContent, wordsCount, lastUpdate];
                const insertResult: RunResult = db.run(insertQuery, insertParams);
                return insertResult.changes > 0;
            }
        } catch (error: unknown) {
            if (error instanceof Error) {
                console.error(`DB Error: ${error.message}`);
                throw new Error(lang === 'fr' ? `Impossible de mettre à jour le contenu du chapitre.` : `Unable to update chapter content.`);
            } else {
                console.error("An unknown error occurred.");
                throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
            }
        }
    }

    /**
     * Fetches companion content for a specific chapter and version.
     * @param userId - The ID of the user/author.
     * @param chapterId - The ID of the chapter.
     * @param version - The version number to fetch.
     * @param lang - The language for error messages ('fr' or 'en').
     * @returns An array of companion content results.
     */
    static fetchCompanionContent(userId: string, chapterId: string, version: number, lang: 'fr' | 'en' = 'fr'): CompanionContentQueryResult[] {
        try {
            const db: Database = System.getDb();
            const query: string = 'SELECT version, content, words_count FROM book_chapter_content WHERE author_id=? AND chapter_id=? AND version=?';
            const params: SQLiteValue[] = [userId, chapterId, version];
            return db.all(query, params) as CompanionContentQueryResult[];
        } catch (error: unknown) {
            if (error instanceof Error) {
                console.error(`DB Error: ${error.message}`);
                throw new Error(lang === 'fr' ? `Impossible de récupérer le contenu compagnon.` : `Unable to retrieve companion content.`);
            } else {
                console.error("An unknown error occurred.");
                throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
            }
        }
    }

    /**
     * Fetches chapter content by its order position within a book.
     * @param userId - The ID of the user/author.
     * @param chapterOrder - The order position of the chapter.
     * @param bookId - The ID of the book.
     * @param lang - The language for error messages ('fr' or 'en').
     * @returns The content query result for the specified chapter.
     * @throws Error if no chapter is found with the specified order.
     */
    static fetchChapterContentByChapterOrder(userId: string, chapterOrder: number, bookId: string, lang: 'fr' | 'en' = 'fr'): ContentQueryResult {
        let chapterContent: ContentQueryResult | null;
        try {
            const db: Database = System.getDb();
            const query: string = `
                SELECT content.content
                FROM book_chapters as chapter
                INNER JOIN book_chapter_content AS content ON chapter.chapter_id=content.chapter_id
                WHERE chapter.chapter_order=? AND content.version=2 AND chapter.book_id=? AND chapter.author_id=?
            `;
            const params: SQLiteValue[] = [chapterOrder, bookId, userId];
            chapterContent = db.get(query, params) as ContentQueryResult | null;
        } catch (error: unknown) {
            if (error instanceof Error) {
                console.error(`DB Error: ${error.message}`);
                throw new Error(lang === 'fr' ? `Impossible de récupérer le contenu du chapitre.` : `Unable to retrieve chapter content.`);
            } else {
                console.error("An unknown error occurred.");
                throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
            }
        }
        if (!chapterContent) {
            throw new Error(lang === 'fr' ? `Aucun chapitre trouvé avec cet ordre.` : `No chapter found with this order.`);
        }
        return chapterContent;
    }

    /**
     * Fetches chapter content by chapter ID and version number.
     * @param userId - The ID of the user/author.
     * @param chapterId - The ID of the chapter.
     * @param version - The version number to fetch.
     * @param lang - The language for error messages ('fr' or 'en').
     * @returns The content query result for the specified version.
     * @throws Error if no chapter is found with the specified version.
     */
    static fetchChapterContentByVersion(userId: string, chapterId: string, version: number, lang: 'fr' | 'en' = 'fr'): ContentQueryResult {
        let chapterContent: ContentQueryResult | null;
        try {
            const db: Database = System.getDb();
            const query: string = 'SELECT content FROM book_chapter_content WHERE author_id=? AND chapter_id=? AND version=?';
            const params: SQLiteValue[] = [userId, chapterId, version];
            chapterContent = db.get(query, params) as ContentQueryResult | null;
        } catch (error: unknown) {
            if (error instanceof Error) {
                console.error(`DB Error: ${error.message}`);
                throw new Error(lang === 'fr' ? `Impossible de récupérer le contenu du chapitre.` : `Unable to retrieve chapter content.`);
            } else {
                console.error("An unknown error occurred.");
                throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
            }
        }
        if (!chapterContent) {
            throw new Error(lang === 'fr' ? `Aucun chapitre trouvé avec cette version.` : `No chapter found with this version.`);
        }
        return chapterContent;
    }

    /**
     * Checks whether chapter content exists for a given content ID and user.
     * @param userId - The ID of the user/author.
     * @param contentId - The ID of the content to check.
     * @param lang - The language for error messages ('fr' or 'en').
     * @returns True if the chapter content exists, false otherwise.
     */
    static isChapterContentExist(userId: string, contentId: string, lang: "fr" | "en"): boolean {
        try {
            const db: Database = System.getDb();
            const query: string = 'SELECT 1 FROM `book_chapter_content` WHERE `content_id`=? AND `author_id`=?';
            const params: SQLiteValue[] = [contentId, userId];
            const existenceCheck: QueryResult | null = db.get(query, params) || null;
            return existenceCheck !== null;
        } catch (error: unknown) {
            if (error instanceof Error) {
                console.error(`DB Error: ${error.message}`);
                throw new Error(lang === 'fr' ? `Impossible de vérifier l'existence du contenu du chapitre.` : `Unable to check chapter content existence.`);
            } else {
                console.error("An unknown error occurred.");
                throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
            }
        }
    }

    /**
     * Fetches all chapter contents for a specific chapter belonging to a user.
     * @param userId - The ID of the user/author.
     * @param chapterId - The ID of the chapter.
     * @param lang - The language for error messages ('fr' or 'en').
     * @returns A promise resolving to an array of book chapter content records.
     */
    static async fetchBookChapterContents(userId: string, chapterId: string, lang: 'fr' | 'en'): Promise<BookChapterContentTable[]> {
        try {
            const db: Database = System.getDb();
            const query: string = 'SELECT content_id, chapter_id, author_id, version, content, words_count, time_on_it, last_update FROM book_chapter_content WHERE author_id=? AND chapter_id=?';
            const params: SQLiteValue[] = [userId, chapterId];
            return db.all(query, params) as BookChapterContentTable[];
        } catch (error: unknown) {
            if (error instanceof Error) {
                console.error(`DB Error: ${error.message}`);
                throw new Error(lang === 'fr' ? `Impossible de récupérer le contenu des chapitres.` : `Unable to retrieve chapter contents.`);
            } else {
                throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
            }
        }
    }

    /**
     * Fetches all synced chapter contents for a user (content ID, chapter ID, and last update timestamp).
     * @param userId - The ID of the user/author.
     * @param lang - The language for error messages ('fr' or 'en').
     * @returns An array of synced chapter content results.
     */
    static fetchSyncedChapterContents(userId: string, lang: 'fr' | 'en'): SyncedChapterContentResult[] {
        try {
            const db: Database = System.getDb();
            const query: string = 'SELECT content_id, chapter_id, last_update FROM book_chapter_content WHERE author_id = ?';
            const params: SQLiteValue[] = [userId];
            return db.all(query, params) as SyncedChapterContentResult[];
        } catch (error: unknown) {
            if (error instanceof Error) {
                console.error(`DB Error: ${error.message}`);
                throw new Error(lang === 'fr' ? `Impossible de récupérer le contenu des chapitres synchronisés.` : `Unable to retrieve synced chapter contents.`);
            } else {
                console.error("An unknown error occurred.");
                throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
            }
        }
    }

    /**
     * Inserts a new chapter content record during synchronization.
     * @param contentId - The unique ID for the content.
     * @param chapterId - The ID of the chapter.
     * @param authorId - The ID of the author.
     * @param version - The version number of the content.
     * @param content - The content string (can be null).
     * @param wordsCount - The word count of the content.
     * @param timeOnIt - The time spent on this content.
     * @param lastUpdate - The timestamp of the last update.
     * @param lang - The language for error messages ('fr' or 'en').
     * @returns True if the insert was successful.
     */
    static insertSyncChapterContent(contentId: string, chapterId: string, authorId: string, version: number, content: string | null, wordsCount: number, timeOnIt: number, lastUpdate: number, lang: 'fr' | 'en'): boolean {
        try {
            const db: Database = System.getDb();
            const query: string = `INSERT INTO book_chapter_content (content_id, chapter_id, author_id, version, content, words_count, time_on_it, last_update) VALUES (?, ?, ?, ?, ?, ?, ?, ?)`;
            const params: SQLiteValue[] = [contentId, chapterId, authorId, version, content, wordsCount, timeOnIt, lastUpdate];
            const insertResult: RunResult = db.run(query, params);
            return insertResult.changes > 0;
        } catch (error: unknown) {
            if (error instanceof Error) {
                console.error(`DB Error: ${error.message}`);
                throw new Error(lang === 'fr' ? `Impossible d'insérer le contenu du chapitre.` : `Unable to insert chapter content.`);
            } else {
                throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
            }
        }
    }

    /**
     * Fetches the complete chapter content record by its content ID.
     * @param contentId - The ID of the content to fetch.
     * @param lang - The language for error messages ('fr' or 'en').
     * @returns A promise resolving to an array of book chapter content records.
     */
    static async fetchCompleteChapterContentById(contentId: string, lang: "fr" | "en"): Promise<BookChapterContentTable[]> {
        try {
            const db: Database = System.getDb();
            const query: string = 'SELECT content_id, chapter_id, author_id, version, content, words_count, time_on_it, last_update FROM book_chapter_content WHERE content_id = ?';
            const params: SQLiteValue[] = [contentId];
            return db.all(query, params) as BookChapterContentTable[];
        } catch (error: unknown) {
            if (error instanceof Error) {
                throw new Error(lang === 'fr' ? `Impossible de récupérer le contenu de chapitre complet.` : `Unable to retrieve complete chapter content.`);
            } else {
                throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
            }
        }
    }

    /**
     * Fetches a complete chapter with its content by joining chapters and chapter content tables.
     * @param userId - The ID of the user/author.
     * @param chapterId - The ID of the chapter.
     * @param version - The version number of the content to fetch.
     * @param lang - The language for error messages ('fr' or 'en').
     * @returns The chapter content query result with chapter metadata.
     * @throws Error if no chapter is found with the specified ID.
     */
    public static fetchWholeChapter(userId: string, chapterId: string, version: number, lang: 'fr' | 'en' = 'fr'): ChapterContentQueryResult {
        let wholeChapter: ChapterContentQueryResult | null;
        try {
            const db: Database = System.getDb();
            const query: string = `
                SELECT
                    chapter.chapter_id as chapter_id,
                    chapter.title as title,
                    chapter.chapter_order,
                    chapter.words_count,
                    content.content AS content,
                    content.version as version
                FROM book_chapters AS chapter
                LEFT JOIN book_chapter_content AS content ON content.chapter_id = chapter.chapter_id AND content.version = ?
                WHERE chapter.chapter_id = ? AND chapter.author_id = ?
            `;
            const params: SQLiteValue[] = [version, chapterId, userId];
            wholeChapter = db.get(query, params) as ChapterContentQueryResult | null;
        } catch (error: unknown) {
            if (error instanceof Error) {
                console.error(`DB Error: ${error.message}`);
                throw new Error(lang === 'fr' ? `Impossible de récupérer le chapitre.` : `Unable to retrieve chapter.`);
            } else {
                console.error("An unknown error occurred.");
                throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
            }
        }
        if (!wholeChapter) {
            throw new Error(lang === 'fr' ? `Aucun chapitre trouvé avec cet ID.` : `No chapter found with this ID.`);
        }
        return wholeChapter;
    }
}
