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

export interface BookAIGuideLineTable extends Record<string, SQLiteValue> {
    user_id: string;
    book_id: string;
    global_resume: string | null;
    themes: string | null;
    verbe_tense: number | null;
    narrative_type: number | null;
    langue: number | null;
    dialogue_type: number | null;
    tone: string | null;
    atmosphere: string | null;
    current_resume: string | null;
    last_update: number;
}

export interface BookGuideLineTable extends Record<string, SQLiteValue> {
    user_id: string;
    book_id: string;
    tone: string | null;
    atmosphere: string | null;
    writing_style: string | null;
    themes: string | null;
    symbolism: string | null;
    motifs: string | null;
    narrative_voice: string | null;
    pacing: string | null;
    intended_audience: string | null;
    key_messages: string | null;
    last_update: number;
}

export interface SyncedGuideLineResult extends Record<string, SQLiteValue> {
    book_id: string;
    last_update: number;
}

export interface SyncedAIGuideLineResult extends Record<string, SQLiteValue> {
    book_id: string;
    last_update: number;
}

export interface GuideLineQuery extends Record<string, SQLiteValue> {
    tone: string;
    atmosphere: string;
    writing_style: string;
    themes: string;
    symbolism: string;
    motifs: string;
    narrative_voice: string;
    pacing: string;
    intended_audience: string;
    key_messages: string;
}

export interface GuideLineAIQuery extends Record<string, SQLiteValue> {
    user_id: string;
    book_id: string;
    global_resume: string | null;
    themes: string | null;
    verbe_tense: number | null;
    narrative_type: number | null;
    langue: number | null;
    dialogue_type: number | null;
    tone: string | null;
    atmosphere: string | null;
    current_resume: string | null;
    meta: string;
}

export default class GuidelineRepo {
    /**
     * Fetches the guideline for a specific book.
     * @param userId - The user identifier
     * @param bookId - The book identifier
     * @param lang - The language for error messages ('fr' or 'en')
     * @returns An array of guideline query results
     * @throws Error if the guideline cannot be retrieved
     */
    public static fetchGuideLine(userId: string, bookId: string, lang: 'fr' | 'en'): GuideLineQuery[] {
        let guidelines: GuideLineQuery[];
        try {
            const db: Database = System.getDb();
            const query: string = 'SELECT * FROM book_guide_line WHERE book_id=? AND user_id=?';
            const params: SQLiteValue[] = [bookId, userId];
            guidelines = db.all(query, params) as GuideLineQuery[];
        } catch (error: unknown) {
            if (error instanceof Error) {
                console.error(`DB Error: ${error.message}`);
                throw new Error(lang === 'fr' ? `Impossible de récupérer la ligne directrice.` : `Unable to retrieve guideline.`);
            } else {
                console.error("An unknown error occurred.");
                throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
            }
        }
        return guidelines;
    }

    /**
     * Updates or inserts a guideline for a specific book.
     * If the guideline exists, it updates it; otherwise, it inserts a new one.
     * @param userId - The user identifier
     * @param bookId - The book identifier
     * @param encryptedTone - The encrypted tone value
     * @param encryptedAtmosphere - The encrypted atmosphere value
     * @param encryptedWritingStyle - The encrypted writing style value
     * @param encryptedThemes - The encrypted themes value
     * @param encryptedSymbolism - The encrypted symbolism value
     * @param encryptedMotifs - The encrypted motifs value
     * @param encryptedNarrativeVoice - The encrypted narrative voice value
     * @param encryptedPacing - The encrypted pacing value
     * @param encryptedKeyMessages - The encrypted key messages value
     * @param encryptedIntendedAudience - The encrypted intended audience value
     * @param lang - The language for error messages ('fr' or 'en')
     * @returns True if the operation was successful
     * @throws Error if the guideline cannot be updated or inserted
     */
    /**
     * Updates or inserts a guideline for a specific book.
     * If the guideline exists, it updates it; otherwise, it inserts a new one.
     * @param userId - The user identifier
     * @param bookId - The book identifier
     * @param encryptedTone - The encrypted tone value
     * @param encryptedAtmosphere - The encrypted atmosphere value
     * @param encryptedWritingStyle - The encrypted writing style value
     * @param encryptedThemes - The encrypted themes value
     * @param encryptedSymbolism - The encrypted symbolism value
     * @param encryptedMotifs - The encrypted motifs value
     * @param encryptedNarrativeVoice - The encrypted narrative voice value
     * @param encryptedPacing - The encrypted pacing value
     * @param encryptedKeyMessages - The encrypted key messages value
     * @param encryptedIntendedAudience - The encrypted intended audience value
     * @param lastUpdate - The timestamp of the last update
     * @param lang - The language for error messages ('fr' or 'en')
     * @returns True if the operation was successful
     * @throws Error if the guideline cannot be updated or inserted
     */
    static updateGuideLine(userId: string, bookId: string, encryptedTone: string | null, encryptedAtmosphere: string | null, encryptedWritingStyle: string | null, encryptedThemes: string | null, encryptedSymbolism: string | null, encryptedMotifs: string | null, encryptedNarrativeVoice: string | null, encryptedPacing: string | null, encryptedKeyMessages: string | null, encryptedIntendedAudience: string | null, lastUpdate: number, lang: 'fr' | 'en'): boolean {
        try {
            const db: Database = System.getDb();
            const updateQuery: string = 'UPDATE book_guide_line SET tone=?, atmosphere=?, writing_style=?, themes=?, symbolism=?, motifs=?, narrative_voice=?, pacing=?, intended_audience=?, key_messages=?, last_update=? WHERE user_id=? AND book_id=?';
            const updateParams: SQLiteValue[] = [
                encryptedTone,
                encryptedAtmosphere,
                encryptedWritingStyle,
                encryptedThemes,
                encryptedSymbolism,
                encryptedMotifs,
                encryptedNarrativeVoice,
                encryptedPacing,
                encryptedIntendedAudience,
                encryptedKeyMessages,
                lastUpdate,
                userId,
                bookId
            ];
            const updateResult: RunResult = db.run(updateQuery, updateParams);

            if (updateResult.changes > 0) {
                return true;
            } else {
                const insertQuery: string = 'INSERT INTO book_guide_line (user_id, book_id, tone, atmosphere, writing_style, themes, symbolism, motifs, narrative_voice, pacing, intended_audience, key_messages, last_update) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)';
                const insertParams: SQLiteValue[] = [
                    userId,
                    bookId,
                    encryptedTone,
                    encryptedAtmosphere,
                    encryptedWritingStyle,
                    encryptedThemes,
                    encryptedSymbolism,
                    encryptedMotifs,
                    encryptedNarrativeVoice,
                    encryptedPacing,
                    encryptedIntendedAudience,
                    encryptedKeyMessages,
                    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 la ligne directrice.` : `Unable to update guideline.`);
            } else {
                console.error("An unknown error occurred.");
                throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
            }
        }
    }

    /**
     * Inserts or updates an AI guideline for a specific book.
     * If the AI guideline exists, it updates it; otherwise, it inserts a new one.
     * @param userId - The user identifier
     * @param bookId - The book identifier
     * @param narrativeType - The narrative type identifier
     * @param dialogueType - The dialogue type identifier
     * @param encryptedPlotSummary - The encrypted plot summary
     * @param encryptedToneAtmosphere - The encrypted tone and atmosphere value
     * @param verbTense - The verb tense identifier
     * @param language - The language identifier
     * @param encryptedThemes - The encrypted themes value
     * @param lastUpdate - The timestamp of the last update
     * @param lang - The language for error messages ('fr' or 'en')
     * @returns True if the operation was successful
     * @throws Error if the AI guideline cannot be inserted or updated
     */
    static insertAIGuideLine(userId: string, bookId: string, narrativeType: number | null, dialogueType: number | null, encryptedPlotSummary: string | null, encryptedToneAtmosphere: string | null, verbTense: number | null, language: number | null, encryptedThemes: string | null, lastUpdate: number, lang: 'fr' | 'en'): boolean {
        try {
            const db: Database = System.getDb();
            const updateQuery: string = 'UPDATE book_ai_guide_line SET narrative_type=?, dialogue_type=?, global_resume=?, atmosphere=?, verbe_tense=?, langue=?, themes=?, last_update=? WHERE user_id=? AND book_id=?';
            const updateParams: SQLiteValue[] = [
                narrativeType,
                dialogueType,
                encryptedPlotSummary,
                encryptedToneAtmosphere,
                verbTense,
                language,
                encryptedThemes,
                lastUpdate,
                userId,
                bookId
            ];
            const updateResult: RunResult = db.run(updateQuery, updateParams);

            if (updateResult.changes > 0) {
                return true;
            } else {
                const insertQuery: string = 'INSERT INTO book_ai_guide_line (user_id, book_id, global_resume, themes, verbe_tense, narrative_type, langue, dialogue_type, tone, atmosphere, current_resume, last_update) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)';
                const insertParams: SQLiteValue[] = [
                    userId,
                    bookId,
                    encryptedPlotSummary,
                    encryptedThemes,
                    verbTense,
                    narrativeType,
                    language,
                    dialogueType,
                    encryptedToneAtmosphere,
                    encryptedToneAtmosphere,
                    encryptedPlotSummary,
                    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 d'insérer la ligne directrice IA.` : `Unable to insert AI guideline.`);
            } else {
                console.error("An unknown error occurred.");
                throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
            }
        }
    }

    /**
     * Fetches the AI guideline for a specific book.
     * @param userId - The user identifier
     * @param bookId - The book identifier
     * @param lang - The language for error messages ('fr' or 'en')
     * @returns The AI guideline query result
     * @throws Error if the AI guideline cannot be retrieved or is not found
     */
    static fetchGuideLineAI(userId: string, bookId: string, lang: 'fr' | 'en'): GuideLineAIQuery {
        let aiGuideline: GuideLineAIQuery | null;
        try {
            const db: Database = System.getDb();
            const query: string = 'SELECT narrative_type, dialogue_type, global_resume, atmosphere, verbe_tense, langue, themes, current_resume FROM book_ai_guide_line WHERE user_id=? AND book_id=?';
            const params: SQLiteValue[] = [userId, bookId];
            aiGuideline = db.get(query, params) as GuideLineAIQuery | null;
        } catch (error: unknown) {
            if (error instanceof Error) {
                console.error(`DB Error: ${error.message}`);
                throw new Error(lang === 'fr' ? `Impossible de récupérer la ligne directrice IA.` : `Unable to retrieve AI guideline.`);
            } else {
                console.error("An unknown error occurred.");
                throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
            }
        }
        if (!aiGuideline) {
            throw new Error(lang === 'fr' ? `Ligne directrice IA non trouvée.` : `AI guideline not found.`);
        }
        return aiGuideline;
    }

    /**
     * Fetches the book AI guideline table data for a specific book.
     * @param userId - The user identifier
     * @param bookId - The book identifier
     * @param lang - The language for error messages ('fr' or 'en')
     * @returns A promise resolving to an array of book AI guideline table entries
     * @throws Error if the AI guideline cannot be retrieved
     */
    static async fetchBookAIGuideLine(userId: string, bookId: string, lang: 'fr' | 'en'): Promise<BookAIGuideLineTable[]> {
        try {
            const db: Database = System.getDb();
            const query: string = 'SELECT user_id, book_id, global_resume, themes, verbe_tense, narrative_type, langue, dialogue_type, tone, atmosphere, current_resume, last_update FROM book_ai_guide_line WHERE user_id=? AND book_id=?';
            const params: SQLiteValue[] = [userId, bookId];
            const aiGuidelines: BookAIGuideLineTable[] = db.all(query, params) as BookAIGuideLineTable[];
            return aiGuidelines;
        } catch (error: unknown) {
            if (error instanceof Error) {
                console.error(`DB Error: ${error.message}`);
                throw new Error(lang === 'fr' ? `Impossible de récupérer la ligne directrice IA.` : `Unable to retrieve AI guideline.`);
            } else {
                throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
            }
        }
    }

    /**
     * Fetches the book guideline table data for a specific book.
     * @param userId - The user identifier
     * @param bookId - The book identifier
     * @param lang - The language for error messages ('fr' or 'en')
     * @returns A promise resolving to an array of book guideline table entries
     * @throws Error if the guideline cannot be retrieved
     */
    static async fetchBookGuideLineTable(userId: string, bookId: string, lang: 'fr' | 'en'): Promise<BookGuideLineTable[]> {
        try {
            const db: Database = System.getDb();
            const query: string = 'SELECT user_id, book_id, tone, atmosphere, writing_style, themes, symbolism, motifs, narrative_voice, pacing, intended_audience, key_messages, last_update FROM book_guide_line WHERE user_id=? AND book_id=?';
            const params: SQLiteValue[] = [userId, bookId];
            const guidelines: BookGuideLineTable[] = db.all(query, params) as BookGuideLineTable[];
            return guidelines;
        } catch (error: unknown) {
            if (error instanceof Error) {
                console.error(`DB Error: ${error.message}`);
                throw new Error(lang === 'fr' ? `Impossible de récupérer la ligne directrice.` : `Unable to retrieve guideline.`);
            } else {
                throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
            }
        }
    }

    /**
     * Fetches all synced guidelines for a specific user.
     * @param userId - The user identifier
     * @param lang - The language for error messages ('fr' or 'en')
     * @returns An array of synced guideline results containing book_id and last_update
     * @throws Error if the synced guidelines cannot be retrieved
     */
    static fetchSyncedGuideLine(userId: string, lang: 'fr' | 'en'): SyncedGuideLineResult[] {
        try {
            const db: Database = System.getDb();
            const query: string = 'SELECT book_id, last_update FROM book_guide_line WHERE user_id = ?';
            const params: SQLiteValue[] = [userId];
            const syncedGuidelines: SyncedGuideLineResult[] = db.all(query, params) as SyncedGuideLineResult[];
            return syncedGuidelines;
        } catch (error: unknown) {
            if (error instanceof Error) {
                console.error(`DB Error: ${error.message}`);
                throw new Error(lang === 'fr' ? `Impossible de récupérer les lignes directrices synchronisées.` : `Unable to retrieve synced guidelines.`);
            } else {
                console.error("An unknown error occurred.");
                throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
            }
        }
    }

    /**
     * Fetches all synced AI guidelines for a specific user.
     * @param userId - The user identifier
     * @param lang - The language for error messages ('fr' or 'en')
     * @returns An array of synced AI guideline results containing book_id and last_update
     * @throws Error if the synced AI guidelines cannot be retrieved
     */
    static fetchSyncedAIGuideLine(userId: string, lang: 'fr' | 'en'): SyncedAIGuideLineResult[] {
        try {
            const db: Database = System.getDb();
            const query: string = 'SELECT book_id, last_update FROM book_ai_guide_line WHERE user_id = ?';
            const params: SQLiteValue[] = [userId];
            const syncedAIGuidelines: SyncedAIGuideLineResult[] = db.all(query, params) as SyncedAIGuideLineResult[];
            return syncedAIGuidelines;
        } catch (error: unknown) {
            if (error instanceof Error) {
                console.error(`DB Error: ${error.message}`);
                throw new Error(lang === 'fr' ? `Impossible de récupérer les lignes directrices IA synchronisées.` : `Unable to retrieve synced AI guidelines.`);
            } else {
                console.error("An unknown error occurred.");
                throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
            }
        }
    }

    /**
     * Checks if a guideline exists for a specific book.
     * @param userId - The unique identifier of the user.
     * @param bookId - The unique identifier of the book.
     * @param lang - The language for error messages ('fr' or 'en').
     * @returns True if the guideline exists, false otherwise.
     */
    static guideLineExist(userId: string, bookId: string, lang: 'fr' | 'en'): boolean {
        try {
            const db: Database = System.getDb();
            const query: string = 'SELECT 1 FROM book_guide_line WHERE user_id=? AND book_id=?';
            const params: SQLiteValue[] = [userId, bookId];
            const result: Record<string, SQLiteValue> | undefined = db.get(query, params) as Record<string, SQLiteValue> | undefined;
            return result !== undefined;
        } catch (error: unknown) {
            if (error instanceof Error) {
                console.error(`DB Error: ${error.message}`);
                throw new Error(lang === 'fr' ? `Impossible de vérifier l'existence de la ligne directrice.` : `Unable to check guideline existence.`);
            } else {
                throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
            }
        }
    }

    /**
     * Checks if an AI guideline exists for a specific book.
     * @param userId - The unique identifier of the user.
     * @param bookId - The unique identifier of the book.
     * @param lang - The language for error messages ('fr' or 'en').
     * @returns True if the AI guideline exists, false otherwise.
     */
    static aiGuideLineExist(userId: string, bookId: string, lang: 'fr' | 'en'): boolean {
        try {
            const db: Database = System.getDb();
            const query: string = 'SELECT 1 FROM book_ai_guide_line WHERE user_id=? AND book_id=?';
            const params: SQLiteValue[] = [userId, bookId];
            const result: Record<string, SQLiteValue> | undefined = db.get(query, params) as Record<string, SQLiteValue> | undefined;
            return result !== undefined;
        } catch (error: unknown) {
            if (error instanceof Error) {
                console.error(`DB Error: ${error.message}`);
                throw new Error(lang === 'fr' ? `Impossible de vérifier l'existence de la ligne directrice IA.` : `Unable to check AI guideline existence.`);
            } else {
                throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
            }
        }
    }

    /**
     * Inserts a synced AI guideline for a specific book.
     * @param userId - The user identifier
     * @param bookId - The book identifier
     * @param globalResume - The global resume value (nullable)
     * @param themes - The themes value (nullable)
     * @param verbeTense - The verb tense identifier (nullable)
     * @param narrativeType - The narrative type identifier (nullable)
     * @param langue - The language identifier (nullable)
     * @param dialogueType - The dialogue type identifier (nullable)
     * @param tone - The tone value (nullable)
     * @param atmosphere - The atmosphere value (nullable)
     * @param currentResume - The current resume value (nullable)
     * @param lastUpdate - The last update timestamp
     * @param lang - The language for error messages ('fr' or 'en')
     * @returns True if the insertion was successful
     * @throws Error if the AI guideline cannot be inserted
     */
    static insertSyncAIGuideLine(
        userId: string,
        bookId: string,
        globalResume: string | null,
        themes: string | null,
        verbeTense: number | null,
        narrativeType: number | null,
        langue: number | null,
        dialogueType: number | null,
        tone: string | null,
        atmosphere: string | null,
        currentResume: string | null,
        lastUpdate: number,
        lang: 'fr' | 'en'
    ): boolean {
        try {
            const db: Database = System.getDb();
            const query: string = `INSERT INTO book_ai_guide_line (user_id, book_id, global_resume, themes, verbe_tense, narrative_type, langue, dialogue_type, tone, atmosphere, current_resume, last_update)
                 VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`;
            const params: SQLiteValue[] = [
                userId,
                bookId,
                globalResume,
                themes,
                verbeTense,
                narrativeType,
                langue,
                dialogueType,
                tone,
                atmosphere,
                currentResume,
                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 la ligne directrice IA.` : `Unable to insert AI guideline.`);
            } else {
                throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
            }
        }
    }

    /**
     * Inserts a synced guideline for a specific book.
     * @param userId - The user identifier
     * @param bookId - The book identifier
     * @param tone - The tone value (nullable)
     * @param atmosphere - The atmosphere value (nullable)
     * @param writingStyle - The writing style value (nullable)
     * @param themes - The themes value (nullable)
     * @param symbolism - The symbolism value (nullable)
     * @param motifs - The motifs value (nullable)
     * @param narrativeVoice - The narrative voice value (nullable)
     * @param pacing - The pacing value (nullable)
     * @param intendedAudience - The intended audience value (nullable)
     * @param keyMessages - The key messages value (nullable)
     * @param lastUpdate - The last update timestamp
     * @param lang - The language for error messages ('fr' or 'en')
     * @returns True if the insertion was successful
     * @throws Error if the guideline cannot be inserted
     */
    static insertSyncGuideLine(
        userId: string,
        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,
        intendedAudience: string | null,
        keyMessages: string | null,
        lastUpdate: number,
        lang: 'fr' | 'en'
    ): boolean {
        try {
            const db: Database = System.getDb();
            const query: string = `INSERT INTO book_guide_line (user_id, book_id, tone, atmosphere, writing_style, themes, symbolism, motifs, narrative_voice, pacing, intended_audience, key_messages, last_update)
                 VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`;
            const params: SQLiteValue[] = [
                userId,
                bookId,
                tone,
                atmosphere,
                writingStyle,
                themes,
                symbolism,
                motifs,
                narrativeVoice,
                pacing,
                intendedAudience,
                keyMessages,
                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 la ligne directrice.` : `Unable to insert guideline.`);
            } else {
                throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
            }
        }
    }
}
