import System from '../System.js';
import { getUserEncryptionKey } from '../keyManager.js';
import BookRepo, { BookQuery, BookToolsTable, EritBooksTable } from "../repositories/book.repository.js";
import { BookActSummariesTable } from "../repositories/act.repository.js";
import { BookAIGuideLineTable, BookGuideLineTable } from "../repositories/guideline.repository.js";
import ChapterRepo, {
    BookChapterInfosTable,
    BookChaptersTable,
    ChapterBookResult
} from "../repositories/chapter.repository.js";
import { BookChapterContentTable } from "../repositories/chaptercontent.repository.js";
import {
    BookCharactersAttributesTable,
    BookCharactersTable
} from "../repositories/character.repository.js";
import { BookIncidentsTable } from "../repositories/incident.repository.js";
import { BookIssuesTable } from "../repositories/issue.repository.js";
import {
    BookLocationTable,
    LocationElementTable,
    LocationSubElementTable
} from "../repositories/location.repository.js";
import { BookPlotPointsTable } from "../repositories/plotpoint.repository.js";
import { BookWorldElementsTable, BookWorldTable } from "../repositories/world.repository.js";
import { BookSpellsTable } from "../repositories/spell.repo.js";
import { BookSpellTagsTable } from "../repositories/spelltag.repo.js";
import { SyncedSpell, SyncedSpellTag } from "./Spell.js";
import { CompleteChapterContent, SyncedChapter } from "./Chapter.js";
import { SyncedCharacter } from "./Character.js";
import { SyncedLocation } from "./Location.js";
import { SyncedWorld } from "./World.js";
import { SyncedIncident } from "./Incident.js";
import { SyncedPlotPoint } from "./PlotPoint.js";
import { SyncedIssue } from "./Issue.js";
import { SyncedActSummary } from "./Act.js";
import { SyncedAIGuideLine, SyncedGuideLine } from "./GuideLine.js";
import Cover from "./Cover.js";
import UserRepo from "../repositories/user.repository.js";
import RemovedItem from "./RemovedItem.js";

export interface SyncedBookTools {
    lastUpdate: number;
    charactersEnabled: boolean;
    worldsEnabled: boolean;
    locationsEnabled: boolean;
    spellsEnabled: boolean;
}

export interface BookToolsSettings {
    characters: boolean;
    worlds: boolean;
    locations: boolean;
    spells: boolean;
}

export interface BookProps {
    bookId: string;
    type: string;
    authorId: string;
    title: string;
    subTitle?: string;
    summary?: string;
    serieId?: number | null;
    seriesId?: string | null;
    desiredReleaseDate?: string | null;
    desiredWordCount?: number | null;
    wordCount?: number;
    coverImage?: string | null;
    bookMeta?: string;
    tools?: BookToolsSettings;
}

export interface CompleteBook {
    eritBooks: EritBooksTable[];
    actSummaries: BookActSummariesTable[];
    aiGuideLine: BookAIGuideLineTable[];
    chapters: BookChaptersTable[];
    chapterContents: BookChapterContentTable[];
    chapterInfos: BookChapterInfosTable[];
    characters: BookCharactersTable[];
    characterAttributes: BookCharactersAttributesTable[];
    guideLine: BookGuideLineTable[];
    incidents: BookIncidentsTable[];
    issues: BookIssuesTable[];
    locations: BookLocationTable[];
    plotPoints: BookPlotPointsTable[];
    worlds: BookWorldTable[];
    worldElements: BookWorldElementsTable[];
    locationElements: LocationElementTable[];
    locationSubElements: LocationSubElementTable[];
    bookTools: BookToolsTable[];
    spells: BookSpellsTable[];
    spellTags: BookSpellTagsTable[];
}

export interface SyncedBook {
    id: string;
    type: string;
    title: string;
    subTitle: string | null;
    lastUpdate: number;
    chapters: SyncedChapter[];
    characters: SyncedCharacter[];
    locations: SyncedLocation[];
    worlds: SyncedWorld[];
    incidents: SyncedIncident[];
    plotPoints: SyncedPlotPoint[];
    issues: SyncedIssue[];
    actSummaries: SyncedActSummary[];
    guideLine: SyncedGuideLine | null;
    aiGuideLine: SyncedAIGuideLine | null;
    bookTools: SyncedBookTools | null;
    spells: SyncedSpell[];
    spellTags: SyncedSpellTag[];
}

export interface BookSyncCompare {
    id: string;
    chapters: string[];
    chapterContents: string[];
    chapterInfos: string[];
    characters: string[];
    characterAttributes: string[];
    locations: string[];
    locationElements: string[];
    locationSubElements: string[];
    worlds: string[];
    worldElements: string[];
    incidents: string[];
    plotPoints: string[];
    issues: string[];
    actSummaries: string[];
    guideLine: boolean;
    aiGuideLine: boolean;
    bookTools: boolean;
    spells: string[];
    spellTags: string[];
}

export interface CompleteBookData {
    bookId: string;
    title: string;
    subTitle: string;
    summary: string;
    coverImage: string;
    userInfos: {
        firstName: string;
        lastName: string;
        authorName: string;
    },
    chapters: CompleteChapterContent[];
}

// ===== SERIES TABLE INTERFACES (for sync) =====

export interface SeriesTable {
    series_id: string;
    user_id: string;
    name: string;
    hashed_name: string;
    description: string | null;
    cover_image: string | null;
    last_update: number;
}

export interface SeriesBooksTable {
    series_id: string;
    book_id: string;
    book_order: number;
    last_update: number;
}

export interface SeriesCharactersTable {
    character_id: string;
    series_id: string;
    user_id: string;
    first_name: string;
    last_name: string | null;
    nickname: string | null;
    age: number | null;
    gender: string | null;
    species: string | null;
    nationality: string | null;
    status: string | null;
    title: string | null;
    category: string;
    image: string | null;
    role: string | null;
    biography: string | null;
    history: string | null;
    speech_pattern: string | null;
    catchphrase: string | null;
    residence: string | null;
    notes: string | null;
    color: string | null;
    last_update: number;
}

export interface SeriesCharacterAttributesTable {
    attr_id: string;
    character_id: string;
    user_id: string;
    attribute_name: string;
    attribute_value: string;
    last_update: number;
}

export interface SeriesWorldsTable {
    world_id: string;
    series_id: string;
    user_id: string;
    name: string;
    hashed_name: string;
    history: string | null;
    politics: string | null;
    economy: string | null;
    religion: string | null;
    languages: string | null;
    last_update: number;
}

export interface SeriesWorldElementsTable {
    element_id: string;
    world_id: string;
    user_id: string;
    element_type: number;
    name: string;
    original_name: string;
    description: string | null;
    last_update: number;
}

export interface SeriesLocationsTable {
    loc_id: string;
    series_id: string;
    user_id: string;
    loc_name: string;
    loc_original_name: string;
    last_update: number;
}

export interface SeriesLocationElementsTable {
    element_id: string;
    location_id: string;
    user_id: string;
    element_name: string;
    original_name: string;
    element_description: string | null;
    last_update: number;
}

export interface SeriesLocationSubElementsTable {
    sub_element_id: string;
    element_id: string;
    user_id: string;
    sub_elem_name: string;
    original_name: string;
    sub_elem_description: string | null;
    last_update: number;
}

export interface SeriesSpellsTable {
    spell_id: string;
    series_id: string;
    user_id: string;
    name: string;
    name_hash: string;
    description: string;
    appearance: string;
    tags: string;
    power_level: string | null;
    components: string | null;
    limitations: string | null;
    notes: string | null;
    last_update: number;
}

export interface SeriesSpellTagsTable {
    tag_id: string;
    series_id: string;
    user_id: string;
    name: string;
    hashed_name: string;
    color: string | null;
    last_update: number;
}

// ===== COMPLETE SERIES INTERFACE (for full sync) =====

export interface CompleteSeries {
    series: SeriesTable[];
    seriesBooks: SeriesBooksTable[];
    seriesCharacters: SeriesCharactersTable[];
    seriesCharacterAttributes: SeriesCharacterAttributesTable[];
    seriesWorlds: SeriesWorldsTable[];
    seriesWorldElements: SeriesWorldElementsTable[];
    seriesLocations: SeriesLocationsTable[];
    seriesLocationElements: SeriesLocationElementsTable[];
    seriesLocationSubElements: SeriesLocationSubElementsTable[];
    seriesSpells: SeriesSpellsTable[];
    seriesSpellTags: SeriesSpellTagsTable[];
}

// ===== SYNCED SERIES INTERFACES (lightweight, for comparison) =====

export interface SyncedSeriesBook {
    bookId: string;
    order: number;
    lastUpdate: number;
}

export interface SyncedSeriesCharacterAttribute {
    id: string;
    name: string;
    lastUpdate: number;
}

export interface SyncedSeriesCharacter {
    id: string;
    name: string;
    lastUpdate: number;
    attributes: SyncedSeriesCharacterAttribute[];
}

export interface SyncedSeriesWorldElement {
    id: string;
    name: string;
    lastUpdate: number;
}

export interface SyncedSeriesWorld {
    id: string;
    name: string;
    lastUpdate: number;
    elements: SyncedSeriesWorldElement[];
}

export interface SyncedSeriesLocationSubElement {
    id: string;
    name: string;
    lastUpdate: number;
}

export interface SyncedSeriesLocationElement {
    id: string;
    name: string;
    lastUpdate: number;
    subElements: SyncedSeriesLocationSubElement[];
}

export interface SyncedSeriesLocation {
    id: string;
    name: string;
    lastUpdate: number;
    elements: SyncedSeriesLocationElement[];
}

export interface SyncedSeriesSpell {
    id: string;
    name: string;
    lastUpdate: number;
}

export interface SyncedSeriesSpellTag {
    id: string;
    name: string;
    lastUpdate: number;
}

export interface SyncedSeries {
    id: string;
    name: string;
    description: string | null;
    lastUpdate: number;
    books: SyncedSeriesBook[];
    characters: SyncedSeriesCharacter[];
    worlds: SyncedSeriesWorld[];
    locations: SyncedSeriesLocation[];
    spells: SyncedSeriesSpell[];
    spellTags: SyncedSeriesSpellTag[];
}

export default class Book {
    private readonly id: string;
    private type: string;
    private authorId: string;
    private title: string;
    private subTitle: string;
    private summary: string;
    private serieId: number;
    private desiredReleaseDate: string;
    private desiredWordCount: number;
    private wordCount: number;
    private cover: string;

    /**
     * Creates a new Book instance.
     * @param id - The unique identifier of the book
     * @param authorId - The unique identifier of the author (optional)
     */
    constructor(id: string, authorId?: string) {
        this.id = id;
        if (authorId) {
            this.authorId = authorId;
        } else {
            this.authorId = '';
        }
        this.title = '';
        this.subTitle = '';
        this.summary = '';
        this.serieId = 0;
        this.desiredReleaseDate = '';
        this.desiredWordCount = 0;
        this.wordCount = 0;
        this.cover = '';
        this.type = '';
    }

    /**
     * Retrieves all books for a specific user.
     * @param userId - The unique identifier of the user
     * @param lang - The language for error messages ('fr' or 'en')
     * @returns A promise resolving to an array of book properties
     * @throws Error if the user encryption key is not found
     */
    public static async getBooks(userId: string, lang: 'fr' | 'en' = 'fr'): Promise<BookProps[]> {
        const userKey: string | null = getUserEncryptionKey(userId);
        if (!userKey) {
            throw new Error(
                lang === 'fr' ? "Clé d'encryption utilisateur non trouvée." : 'User encryption key not found.'
            );
        }

        const books: BookQuery[] = BookRepo.fetchBooks(userId, lang);
        if (!books || books.length === 0) {
            return [];
        }

        return await Promise.all(
            books.map(async (book: BookQuery): Promise<BookProps> => {
                return {
                    bookId: book.book_id,
                    type: book.type,
                    authorId: book.author_id,
                    title: System.decryptDataWithUserKey(book.title, userKey),
                    subTitle: book.sub_title ? System.decryptDataWithUserKey(book.sub_title, userKey) : '',
                    summary: book.summary ? System.decryptDataWithUserKey(book.summary, userKey) : '',
                    serieId: book.serie_id || 0,
                    desiredReleaseDate: book.desired_release_date || '',
                    desiredWordCount: book.desired_word_count || 0,
                    wordCount: book.words_count || 0,
                    coverImage: book.cover_image ? Cover.getPicture(userId, userKey, book.cover_image) : '',
                };
            }) ?? []
        );
    }

    /**
     * Adds a new book to the database.
     * @param bookId - The unique identifier for the book (optional, will be generated if null)
     * @param userId - The unique identifier of the user
     * @param title - The title of the book
     * @param subTitle - The subtitle of the book
     * @param summary - The summary of the book
     * @param type - The type/genre of the book
     * @param serie - The series identifier
     * @param publicationDate - The desired publication date
     * @param desiredWordCount - The target word count
     * @param lang - The language for error messages ('fr' or 'en')
     * @returns A promise resolving to the book ID
     * @throws Error if a book with the same title already exists
     */
    public static async addBook(bookId: string | null, userId: string, title: string, subTitle: string, summary: string, type: string, serie: number, publicationDate: string, desiredWordCount: number, lang: 'fr' | 'en' = 'fr'): Promise<string> {
        let newBookId: string = '';
        const userKey: string | null = getUserEncryptionKey(userId);

        const encryptedTitle: string = System.encryptDataWithUserKey(title, userKey);
        const encryptedSubTitle: string = subTitle ? System.encryptDataWithUserKey(subTitle, userKey) : '';
        const encryptedSummary: string = summary ? System.encryptDataWithUserKey(summary, userKey) : '';
        const hashedTitle: string = System.hashElement(title);
        const hashedSubTitle: string = subTitle ? System.hashElement(subTitle) : '';

        if (BookRepo.verifyBookExist(hashedTitle, hashedSubTitle, userId, lang)) {
            throw new Error(lang === "fr" ? `Tu as déjà un livre intitulé ${title} - ${subTitle}.` : `You already have a book named ${title} - ${subTitle}.`);
        }
        if (bookId) {
            newBookId = bookId;
        } else {
            newBookId = System.createUniqueId();
        }
        return BookRepo.insertBook(newBookId, userId, encryptedTitle, hashedTitle, encryptedSubTitle, hashedSubTitle, encryptedSummary, type, serie, publicationDate, desiredWordCount, lang);
    }

    /**
     * Retrieves a single book by its ID.
     * @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 A promise resolving to the book properties
     */
    public static async getBook(userId: string, bookId: string, lang: 'fr' | 'en'): Promise<BookProps> {
        const book: Book = new Book(bookId);
        book.getBookInfos(userId);
        const bookTools: BookToolsTable | null = BookRepo.fetchBookTools(userId, bookId, lang);
        // Récupérer le seriesId depuis series_books
        const seriesId: string | null = BookRepo.fetchBookSeriesId(bookId, lang);
        return {
            bookId: book.getId(),
            type: book.getType(),
            authorId: book.getAuthorId(),
            title: book.getTitle(),
            subTitle: book.getSubTitle(),
            summary: book.getSummary(),
            serieId: book.getSerieId(),
            seriesId: seriesId,
            desiredReleaseDate: book.getDesiredReleaseDate(),
            desiredWordCount: book.getDesiredWordCount(),
            wordCount: book.getWordCount(),
            coverImage: book.getCover(),
            tools: {
                characters: bookTools ? bookTools.characters_enabled === 1 : false,
                worlds: bookTools ? bookTools.worlds_enabled === 1 : false,
                locations: bookTools ? bookTools.locations_enabled === 1 : false,
                spells: bookTools ? bookTools.spells_enabled === 1 : false
            }
        };
    }

    /**
     * Updates basic information for a book.
     * @param userId - The unique identifier of the user
     * @param title - The new title
     * @param subTitle - The new subtitle
     * @param summary - The new summary
     * @param publicationDate - The new publication date
     * @param wordCount - The new word count
     * @param bookId - The unique identifier of the book
     * @param lang - The language for error messages ('fr' or 'en')
     * @returns True if the update was successful, false otherwise
     */
    static updateBookBasicInformation(userId: string, title: string, subTitle: string, summary: string, publicationDate: string, wordCount: number, bookId: string, lang: 'fr' | 'en' = 'fr'): boolean {
        const userKey: string = getUserEncryptionKey(userId);
        const encryptedTitle: string = System.encryptDataWithUserKey(title, userKey);
        const encryptedSubTitle: string = subTitle ? System.encryptDataWithUserKey(subTitle, userKey) : '';
        const encryptedSummary: string = summary ? System.encryptDataWithUserKey(summary, userKey) : '';
        const hashedTitle: string = System.hashElement(title);
        const hashedSubTitle: string = subTitle ? System.hashElement(subTitle) : '';
        return BookRepo.updateBookBasicInformation(userId, encryptedTitle, hashedTitle, encryptedSubTitle, hashedSubTitle, encryptedSummary, publicationDate, wordCount, bookId, lang);
    }

    /**
     * Removes a book from the database.
     * @param userId - The unique identifier of the user
     * @param bookId - The unique identifier of the book to remove
     * @param deletedAt - The timestamp of deletion (from UI via System.timeStampInSeconds())
     * @param lang - The language for error messages ('fr' or 'en')
     * @returns True if the book was removed, false otherwise
     */
    public static removeBook(userId: string, bookId: string, deletedAt: number, lang: 'fr' | 'en' = 'fr'): boolean {
        const deleted: boolean = BookRepo.deleteBook(userId, bookId, lang);
        if (deleted) {
            RemovedItem.deleteTracker(userId, bookId, 'erit_books', bookId, deletedAt, lang);
        }
        return deleted;
    }

    public static updateBookToolSetting(userId: string, bookId: string, toolName: 'characters' | 'worlds' | 'locations' | 'spells', enabled: boolean, lang: 'fr' | 'en' = 'fr'): boolean {
        const columnName: 'characters_enabled' | 'worlds_enabled' | 'locations_enabled' | 'spells_enabled' = `${toolName}_enabled` as 'characters_enabled' | 'worlds_enabled' | 'locations_enabled' | 'spells_enabled';
        return BookRepo.updateBookToolSetting(userId, bookId, columnName, enabled, System.timeStampInSeconds(), lang);
    }

    /**
     * Gets the book ID.
     * @returns The book's unique identifier
     */
    public getId(): string {
        return this.id;
    }

    /**
     * Gets the author ID.
     * @returns The author's unique identifier
     */
    public getAuthorId(): string {
        return this.authorId;
    }

    /**
     * Gets the book title.
     * @returns The decrypted book title
     */
    public getTitle(): string {
        return this.title;
    }

    /**
     * Gets the book subtitle.
     * @returns The decrypted book subtitle
     */
    public getSubTitle(): string {
        return this.subTitle;
    }

    /**
     * Gets the book summary.
     * @returns The decrypted book summary
     */
    public getSummary(): string {
        return this.summary;
    }

    /**
     * Gets the series ID.
     * @returns The series identifier
     */
    public getSerieId(): number {
        return this.serieId;
    }

    /**
     * Gets the desired release date.
     * @returns The desired release date string
     */
    public getDesiredReleaseDate(): string {
        return this.desiredReleaseDate;
    }

    /**
     * Gets the desired word count.
     * @returns The target word count
     */
    public getDesiredWordCount(): number {
        return this.desiredWordCount;
    }

    /**
     * Gets the current word count.
     * @returns The current word count
     */
    public getWordCount(): number {
        return this.wordCount;
    }

    /**
     * Gets the cover image.
     * @returns The cover image data
     */
    public getCover(): string {
        return this.cover;
    }

    /**
     * Gets the book type.
     * @returns The book type/genre
     */
    public getType(): string {
        return this.type;
    }

    /**
     * Retrieves complete book data including chapters and user information.
     * @param userId - The unique identifier of the user
     * @param id - The unique identifier of the book
     * @param lang - The language for error messages ('fr' or 'en')
     * @returns The complete book data with decrypted content
     */
    static completeBookData(userId: string, id: string, lang: 'fr' | 'en' = 'fr'): CompleteBookData {
        const bookData: BookQuery = BookRepo.fetchBook(id, userId, lang);
        const chapters: ChapterBookResult[] = ChapterRepo.fetchCompleteBookChapters(id, lang);
        const userKey: string = getUserEncryptionKey(userId);
        const userInfos = UserRepo.fetchAccountInformation(userId, lang);

        const bookTitle: string = bookData.title ? System.decryptDataWithUserKey(bookData.title, userKey) : '';
        const decryptedChapters: CompleteChapterContent[] = [];

        for (const chapter of chapters) {
            decryptedChapters.push({
                id: '',
                title: chapter.title ? System.decryptDataWithUserKey(chapter.title, userKey) : '',
                content: chapter.content ? System.decryptDataWithUserKey(chapter.content, userKey) : '',
                order: chapter.chapter_order
            })
        }
        const coverImage: string = bookData.cover_image ? Cover.getPicture(userId, userKey, bookData.cover_image, lang) : '';
        return {
            bookId: id,
            title: bookTitle,
            subTitle: bookData.sub_title ? System.decryptDataWithUserKey(bookData.sub_title, userKey) : '',
            summary: bookData.summary ? System.decryptDataWithUserKey(bookData.summary, userKey) : '',
            coverImage: coverImage,
            userInfos: {
                firstName: userInfos.first_name ? System.decryptDataWithUserKey(userInfos.first_name, userKey) : '',
                lastName: userInfos.last_name ? System.decryptDataWithUserKey(userInfos.last_name, userKey) : '',
                authorName: userInfos.author_name ? System.decryptDataWithUserKey(userInfos.author_name, userKey) : '',
            },
            chapters: decryptedChapters
        };
    }

    /**
     * Loads book information from the database into the instance.
     * @param userId - The unique identifier of the user
     * @param lang - The language for error messages ('fr' or 'en')
     */
    public getBookInfos(userId: string, lang: 'fr' | 'en' = 'fr'): void {
        const bookData: BookQuery = BookRepo.fetchBook(this.id, userId, lang);
        const userKey: string = getUserEncryptionKey(userId);
        if (bookData) {
            this.authorId = bookData.author_id;
            this.type = bookData.type;
            this.title = bookData.title ? System.decryptDataWithUserKey(bookData.title, userKey) : '';
            this.subTitle = bookData.sub_title ? System.decryptDataWithUserKey(bookData.sub_title, userKey) : '';
            this.summary = bookData.summary ? System.decryptDataWithUserKey(bookData.summary, userKey) : '';
            this.serieId = bookData.serie_id ?? 0;
            this.desiredReleaseDate = bookData.desired_release_date ?? '';
            this.desiredWordCount = bookData.desired_word_count ?? 0;
            this.wordCount = bookData.words_count ?? 0;
            this.cover = bookData.cover_image ? Cover.getPicture(userId, userKey, bookData.cover_image, lang) : '';
        } else {
            this.authorId = '';
            this.title = '';
            this.subTitle = '';
            this.summary = '';
            this.serieId = 0;
            this.desiredReleaseDate = '';
            this.wordCount = 0;
            this.cover = '';
        }
    }
}
