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

export interface SpellTagResult extends Record<string, SQLiteValue> {
    tag_id: string;
    book_id: string;
    name: string;
    color: string | null;
}

export interface BookSpellTagsTable extends Record<string, SQLiteValue> {
    tag_id: string;
    book_id: string;
    user_id: string;
    name: string;
    name_hash: string;
    color: string | null;
    last_update: number;
}

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

export default class SpellTagRepo {
    /**
     * Fetches all spell tags for a specific book owned by the user.
     * @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 An array of spell tag results
     */
    static fetchSpellTags(userId: string, bookId: string, lang: 'fr' | 'en' = 'fr'): SpellTagResult[] {
        try {
            const db: Database = System.getDb();
            const query: string = 'SELECT tag_id, book_id, name, color FROM book_spell_tags WHERE user_id=? AND book_id=?';
            const params: SQLiteValue[] = [userId, bookId];
            return db.all(query, params) as SpellTagResult[];
        } catch (error: unknown) {
            if (error instanceof Error) {
                console.error(`[SpellTagRepo] DB Error: ${error.message}`);
                throw new Error(lang === 'fr' ? `Impossible de récupérer les tags de sorts.` : `Unable to retrieve spell tags.`);
            } else {
                console.error('[SpellTagRepo] An unknown error occurred.');
                throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : 'An unknown error occurred.');
            }
        }
    }

    /**
     * Inserts a new spell tag.
     * @param tagId - The unique identifier for the new tag
     * @param bookId - The unique identifier of the book
     * @param userId - The unique identifier of the user
     * @param name - The encrypted name of the tag
     * @param nameHash - The hashed name for duplicate detection
     * @param color - The optional color hex code
     * @param lang - The language for error messages ('fr' or 'en')
     * @returns The tag ID if successful
     */
    static insertSpellTag(tagId: string, bookId: string, userId: string, name: string, nameHash: string, color: string | null, lang: 'fr' | 'en' = 'fr'): string {
        let result: RunResult;
        try {
            const db: Database = System.getDb();
            const query: string = 'INSERT INTO book_spell_tags (tag_id, book_id, user_id, name, name_hash, color, last_update) VALUES (?,?,?,?,?,?,?)';
            const params: SQLiteValue[] = [tagId, bookId, userId, name, nameHash, color, System.timeStampInSeconds()];
            result = db.run(query, params);
        } catch (error: unknown) {
            if (error instanceof Error) {
                console.error(`[SpellTagRepo] DB Error: ${error.message}`);
            } else {
                console.error('[SpellTagRepo] An unknown error occurred.');
            }
            throw new Error(lang === 'fr' ? `Impossible d'ajouter le tag de sort.` : `Unable to add spell tag.`);
        }
        if (!result || result.changes === 0) {
            throw new Error(lang === 'fr' ? `Une erreur s'est produite lors de l'ajout du tag.` : `Error adding tag.`);
        }
        return tagId;
    }

    /**
     * Updates an existing spell tag.
     * @param userId - The unique identifier of the user
     * @param tagId - The unique identifier of the tag
     * @param name - The encrypted name of the tag
     * @param nameHash - The hashed name
     * @param color - The optional color hex code
     * @param lang - The language for error messages ('fr' or 'en')
     * @returns True if the update was successful
     */
    static updateSpellTag(userId: string, tagId: string, name: string, nameHash: string, color: string | null, lang: 'fr' | 'en' = 'fr'): boolean {
        try {
            const db: Database = System.getDb();
            const query: string = 'UPDATE book_spell_tags SET name=?, name_hash=?, color=?, last_update=? WHERE tag_id=? AND user_id=?';
            const params: SQLiteValue[] = [name, nameHash, color, System.timeStampInSeconds(), tagId, userId];
            const result: RunResult = db.run(query, params);
            return result.changes > 0;
        } catch (error: unknown) {
            if (error instanceof Error) {
                console.error(`[SpellTagRepo] DB Error: ${error.message}`);
                throw new Error(lang === 'fr' ? `Impossible de mettre à jour le tag de sort.` : `Unable to update spell tag.`);
            } else {
                console.error('[SpellTagRepo] An unknown error occurred.');
                throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : 'An unknown error occurred.');
            }
        }
    }

    /**
     * Deletes a spell tag.
     * @param userId - The unique identifier of the user
     * @param tagId - The unique identifier of the tag
     * @param lang - The language for error messages ('fr' or 'en')
     * @returns True if the deletion was successful
     */
    static deleteSpellTag(userId: string, tagId: string, lang: 'fr' | 'en' = 'fr'): boolean {
        try {
            const db: Database = System.getDb();
            const query: string = 'DELETE FROM book_spell_tags WHERE tag_id=? AND user_id=?';
            const params: SQLiteValue[] = [tagId, userId];
            const result: RunResult = db.run(query, params);
            return result.changes > 0;
        } catch (error: unknown) {
            if (error instanceof Error) {
                console.error(`[SpellTagRepo] DB Error: ${error.message}`);
                throw new Error(lang === 'fr' ? `Impossible de supprimer le tag de sort.` : `Unable to delete spell tag.`);
            } else {
                console.error('[SpellTagRepo] An unknown error occurred.');
                throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : 'An unknown error occurred.');
            }
        }
    }

    /**
     * Fetches all spell tags for a book with full table data for sync.
     * @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 An array of book spell tags table records
     */
    static fetchBookSpellTagsTable(userId: string, bookId: string, lang: 'fr' | 'en' = 'fr'): BookSpellTagsTable[] {
        try {
            const db: Database = System.getDb();
            const query: string = 'SELECT tag_id, book_id, user_id, name, name_hash, color, last_update FROM book_spell_tags WHERE user_id=? AND book_id=?';
            const params: SQLiteValue[] = [userId, bookId];
            return db.all(query, params) as BookSpellTagsTable[];
        } catch (error: unknown) {
            if (error instanceof Error) {
                console.error(`[SpellTagRepo] DB Error: ${error.message}`);
                throw new Error(lang === 'fr' ? `Impossible de récupérer les tags de sorts.` : `Unable to retrieve spell tags.`);
            } else {
                console.error('[SpellTagRepo] An unknown error occurred.');
                throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : 'An unknown error occurred.');
            }
        }
    }

    /**
     * Fetches a complete spell tag record by its ID.
     * @param userId - The unique identifier of the user
     * @param tagId - The unique identifier of the tag
     * @param lang - The language for error messages ('fr' or 'en')
     * @returns The spell tag table record or null
     */
    static fetchSpellTagTableById(userId: string, tagId: string, lang: 'fr' | 'en' = 'fr'): BookSpellTagsTable | null {
        try {
            const db: Database = System.getDb();
            const query: string = 'SELECT tag_id, book_id, user_id, name, name_hash, color, last_update FROM book_spell_tags WHERE user_id=? AND tag_id=?';
            const params: SQLiteValue[] = [userId, tagId];
            const spellTags: BookSpellTagsTable[] = db.all(query, params) as BookSpellTagsTable[];
            return spellTags.length > 0 ? spellTags[0] : null;
        } catch (error: unknown) {
            if (error instanceof Error) {
                console.error(`[SpellTagRepo] DB Error: ${error.message}`);
                throw new Error(lang === 'fr' ? `Impossible de récupérer le tag de sort.` : `Unable to retrieve spell tag.`);
            } else {
                console.error('[SpellTagRepo] An unknown error occurred.');
                throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : 'An unknown error occurred.');
            }
        }
    }

    /**
     * Fetches all synced spell tags for a user.
     * @param userId - The unique identifier of the user
     * @param lang - The language for error messages ('fr' or 'en')
     * @returns An array of synced spell tag results
     */
    static fetchSyncedSpellTags(userId: string, lang: 'fr' | 'en' = 'fr'): SyncedSpellTagResult[] {
        try {
            const db: Database = System.getDb();
            const query: string = 'SELECT tag_id, book_id, name, last_update FROM book_spell_tags WHERE user_id=?';
            const params: SQLiteValue[] = [userId];
            return db.all(query, params) as SyncedSpellTagResult[];
        } catch (error: unknown) {
            if (error instanceof Error) {
                console.error(`[SpellTagRepo] DB Error: ${error.message}`);
                throw new Error(lang === 'fr' ? `Impossible de récupérer les tags de sorts synchronisés.` : `Unable to retrieve synced spell tags.`);
            } else {
                console.error('[SpellTagRepo] An unknown error occurred.');
                throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : 'An unknown error occurred.');
            }
        }
    }

    /**
     * Inserts or updates a spell tag from synchronization data.
     * @param tagId - The unique identifier for the tag
     * @param bookId - The unique identifier of the book
     * @param userId - The unique identifier of the user
     * @param name - The encrypted name
     * @param nameHash - The hashed name
     * @param color - The optional color hex code
     * @param lastUpdate - The timestamp of the last update
     * @param lang - The language for error messages ('fr' or 'en')
     * @returns True if the insertion was successful
     */
    static insertSyncSpellTag(tagId: string, bookId: string, userId: string, name: string, nameHash: string, color: string | null, lastUpdate: number, lang: 'fr' | 'en' = 'fr'): boolean {
        try {
            const db: Database = System.getDb();
            const query: string = 'INSERT OR REPLACE INTO book_spell_tags (tag_id, book_id, user_id, name, name_hash, color, last_update) VALUES (?,?,?,?,?,?,?)';
            const params: SQLiteValue[] = [tagId, bookId, userId, name, nameHash, color, lastUpdate];
            const result: RunResult = db.run(query, params);
            return result.changes > 0;
        } catch (error: unknown) {
            if (error instanceof Error) {
                console.error(`[SpellTagRepo] DB Error: ${error.message}`);
                throw new Error(lang === 'fr' ? `Impossible d'insérer le tag de sort.` : `Unable to insert spell tag.`);
            } else {
                console.error('[SpellTagRepo] An unknown error occurred.');
                throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : 'An unknown error occurred.');
            }
        }
    }

    /**
     * Checks if a spell tag exists.
     * @param userId - The unique identifier of the user
     * @param tagId - The unique identifier of the tag
     * @param lang - The language for error messages ('fr' or 'en')
     * @returns True if the tag exists
     */
    static isSpellTagExist(userId: string, tagId: string, lang: 'fr' | 'en' = 'fr'): boolean {
        try {
            const db: Database = System.getDb();
            const query: string = 'SELECT 1 FROM book_spell_tags WHERE tag_id=? AND user_id=?';
            const params: SQLiteValue[] = [tagId, userId];
            const existenceCheck = db.all(query, params);
            return existenceCheck.length > 0;
        } catch (error: unknown) {
            if (error instanceof Error) {
                console.error(`[SpellTagRepo] DB Error: ${error.message}`);
                throw new Error(lang === 'fr' ? `Impossible de vérifier l'existence du tag.` : `Unable to check tag existence.`);
            } else {
                console.error('[SpellTagRepo] An unknown error occurred.');
                throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : 'An unknown error occurred.');
            }
        }
    }

    /**
     * Updates a spell tag with timestamp for sync.
     * @param userId - The unique identifier of the user
     * @param tagId - The unique identifier of the tag
     * @param name - The encrypted name
     * @param nameHash - The hashed name
     * @param color - The optional color hex code
     * @param lastUpdate - The timestamp of the last update
     * @param lang - The language for error messages ('fr' or 'en')
     * @returns True if the update was successful
     */
    static updateSyncSpellTag(userId: string, tagId: string, name: string, nameHash: string, color: string | null, lastUpdate: number, lang: 'fr' | 'en' = 'fr'): boolean {
        try {
            const db: Database = System.getDb();
            const query: string = 'UPDATE book_spell_tags SET name=?, name_hash=?, color=?, last_update=? WHERE tag_id=? AND user_id=?';
            const params: SQLiteValue[] = [name, nameHash, color, lastUpdate, tagId, userId];
            const result: RunResult = db.run(query, params);
            return result.changes > 0;
        } catch (error: unknown) {
            if (error instanceof Error) {
                console.error(`[SpellTagRepo] DB Error: ${error.message}`);
                throw new Error(lang === 'fr' ? `Impossible de mettre à jour le tag de sort.` : `Unable to update spell tag.`);
            } else {
                console.error('[SpellTagRepo] An unknown error occurred.');
                throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : 'An unknown error occurred.');
            }
        }
    }
}
