import * as tauri from '@/lib/tauri';
import {useContext, useEffect, useRef, useState} from "react";
import System from "@/lib/models/System";
import {AlertContext} from "@/context/AlertContext";
import {BookContext} from "@/context/BookContext";
import SearchBook from "./SearchBook";
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
import {faBook, faChevronLeft, faChevronRight, faDownload, faGear, faTrash} from "@fortawesome/free-solid-svg-icons";
import {SessionContext} from "@/context/SessionContext";
import Book, {BookProps} from "@/lib/models/Book";
import BookCard from "@/components/book/BookCard";
import BookCardSkeleton from "@/components/book/BookCardSkeleton";
import GuideTour, {GuideStep} from "@/components/GuideTour";
import User from "@/lib/models/User";
import {useTranslations} from "next-intl";
import {LangContext, LangContextProps} from "@/context/LangContext";
import OfflineContext, {OfflineContextType} from "@/context/OfflineContext";
import {BooksSyncContext, BooksSyncContextProps, SyncType} from "@/context/BooksSyncContext";
import {SeriesSyncContext, SeriesSyncContextProps, SeriesSyncType} from "@/context/SeriesSyncContext";
import {BookSyncCompare, SyncedBook} from "@/lib/models/SyncedBook";
import {SeriesSyncCompare, SyncedSeries} from "@/lib/models/SyncedSeries";
import {SeriesListItemProps} from "@/lib/models/Series";
import SeriesCard, {SeriesCardProps} from "@/components/series/SeriesCard";
import SeriesSetting from "@/components/series/SeriesSetting";

interface CategoryItem {
    type: 'book' | 'series';
    book?: BookProps;
    series?: SeriesCardProps;
}

export default function BookList() {
    const {session, setSession} = useContext(SessionContext);
    const accessToken: string = session?.accessToken || '';
    const {errorMessage} = useContext(AlertContext);
    const {setBook} = useContext(BookContext);
    const t = useTranslations();
    const {lang} = useContext<LangContextProps>(LangContext);
    const {isCurrentlyOffline, offlineMode} = useContext<OfflineContextType>(OfflineContext);
    const {
        booksToSyncFromServer,
        booksToSyncToServer,
        serverOnlyBooks,
        localOnlyBooks,
        serverSyncedBooks
    } = useContext<BooksSyncContextProps>(BooksSyncContext);
    const {
        seriesToSyncFromServer,
        seriesToSyncToServer,
        serverOnlySeries,
        localOnlySeries
    } = useContext<SeriesSyncContextProps>(SeriesSyncContext);

    const [searchQuery, setSearchQuery] = useState<string>('');
    const [groupedItems, setGroupedItems] = useState<Record<string, CategoryItem[]>>({});
    const [isLoadingBooks, setIsLoadingBooks] = useState<boolean>(true);
    const [showSeriesSettingId, setShowSeriesSettingId] = useState<string | null>(null);
    const [isLocalSeries, setIsLocalSeries] = useState<boolean>(false);
    const carouselRefs = useRef<Record<string, HTMLDivElement | null>>({});

    const [bookGuide, setBookGuide] = useState<boolean>(false);

    const bookGuideSteps: GuideStep[] = [
        {
            id: 0,
            targetSelector: '[data-guide="book-category"]',
            position: 'left',
            highlightRadius: -200,
            title: `${t("bookList.guideStep0Title")} ${session.user?.name}`,
            content: (
                <div>
                    <p>{t("bookList.guideStep0Content")}</p>
                </div>
            ),
        },
        {
            id: 1,
            targetSelector: '[data-guide="book-card"]',
            position: 'left',
            title: t("bookList.guideStep1Title"),
            content: (
                <div>
                    <p>{t("bookList.guideStep1Content")}</p>
                </div>
            ),
        },
        {
            id: 2,
            targetSelector: '[data-guide="bottom-book-card"]',
            position: 'left',
            title: t("bookList.guideStep2Title"),
            content: (
                <div>
                    <p>
                        <FontAwesomeIcon icon={faGear} className="mr-2 text-primary w-5 h-5"/>
                        {t("bookList.guideStep2ContentGear")}
                    </p>
                    <p>
                        <FontAwesomeIcon icon={faDownload} className="mr-2 text-primary w-5 h-5"/>
                        {t("bookList.guideStep2ContentDownload")}
                    </p>
                    <p>
                        <FontAwesomeIcon icon={faTrash} className="mr-2 text-primary w-5 h-5"/>
                        {t("bookList.guideStep2ContentTrash")}
                    </p>
                </div>
            ),
        },
    ];

    useEffect((): void => {
        if (groupedItems && Object.keys(groupedItems).length > 0 && User.guideTourDone(session.user?.guideTour || [], 'new-first-book')) {
            setBookGuide(true);
        }
    }, [groupedItems]);

    useEffect((): void => {
        const shouldFetch: boolean | "" =
            (session.isConnected || accessToken) &&
            (!isCurrentlyOffline() || offlineMode.isDatabaseInitialized);

        if (shouldFetch) {
            loadBooksAndSeries().then();
        }
    }, [
        session.isConnected,
        accessToken,
        offlineMode.isDatabaseInitialized,
        offlineMode.isOffline,
        booksToSyncFromServer,
        booksToSyncToServer,
        serverOnlyBooks,
        localOnlyBooks,
        serverSyncedBooks
    ]);

    async function handleFirstBookGuide(): Promise<void> {
        try {
            if (!isCurrentlyOffline()) {
                const response: boolean = await System.authPostToServer<boolean>(
                    'logs/tour',
                    {plateforme: 'desktop', tour: 'new-first-book'},
                    session.accessToken, lang
                );
                if (response) {
                    setSession(User.setNewGuideTour(session, 'new-first-book'));
                    setBookGuide(false);
                }
            } else {
                const completedGuides = JSON.parse(localStorage.getItem('completedGuides') || '[]');
                if (!completedGuides.includes('new-first-book')) {
                    completedGuides.push('new-first-book');
                    localStorage.setItem('completedGuides', JSON.stringify(completedGuides));
                }
                setSession(User.setNewGuideTour(session, 'new-first-book'));
                setBookGuide(false);
            }
        } catch (e: unknown) {
            if (e instanceof Error) {
                errorMessage(e.message);
            } else {
                errorMessage(t("bookList.errorBookCreate"));
            }
        }
    }

    async function loadBooksAndSeries(): Promise<void> {
        setIsLoadingBooks(true);
        try {
            let booksResponse: (BookProps & { itIsLocal?: boolean })[] = [];
            let seriesResponse: SeriesListItemProps[] = [];

            // ═══════════════════════════════════════════════════════════════
            // PARTIE 1 : FETCH DES DONNÉES (dual logic)
            // ═══════════════════════════════════════════════════════════════

            if (!isCurrentlyOffline()) {
                // ONLINE : fetch serveur + local en parallèle
                const [onlineBooks, localBooks, onlineSeries, localSeries] = await Promise.all([
                    System.authGetQueryToServer<BookProps[]>('books', accessToken, lang),
                    offlineMode.isDatabaseInitialized
                        ? tauri.getBooks()
                        : Promise.resolve([]),
                    System.authGetQueryToServer<SeriesListItemProps[]>('series/list', accessToken, lang),
                    offlineMode.isDatabaseInitialized
                        ? tauri.getSeriesList() as Promise<SeriesListItemProps[]>
                        : Promise.resolve([])
                ]);

                // Merge des livres (serveur + locaux uniques)
                const onlineBookIds = new Set(onlineBooks.map(b => b.bookId));
                const uniqueLocalBooks = localBooks.filter(b => !onlineBookIds.has(b.bookId));
                booksResponse = [
                    ...onlineBooks.map(b => ({...b, itIsLocal: false})),
                    ...uniqueLocalBooks.map(b => ({...b, itIsLocal: true}))
                ];

                // Merge des séries (serveur + locales uniques)
                // Pour les séries synced, on merge les bookIds (serveur + local-only)
                const localSeriesMap = new Map(localSeries.map(s => [s.id, s]));
                const mergedOnlineSeries = onlineSeries.map(serverSeries => {
                    const localVersion = localSeriesMap.get(serverSeries.id);
                    if (localVersion) {
                        // Merger les bookIds : serveur + ceux du local qui ne sont pas sur le serveur
                        const serverBookIds = new Set(serverSeries.bookIds);
                        const localOnlyBookIds = localVersion.bookIds.filter(id => !serverBookIds.has(id));
                        return {
                            ...serverSeries,
                            bookIds: [...serverSeries.bookIds, ...localOnlyBookIds]
                        };
                    }
                    return serverSeries;
                });
                const onlineSeriesIds = new Set(onlineSeries.map(s => s.id));
                const uniqueLocalSeries = localSeries.filter(s => !onlineSeriesIds.has(s.id));
                seriesResponse = [...mergedOnlineSeries, ...uniqueLocalSeries];

            } else {
                // OFFLINE : local seulement
                if (!offlineMode.isDatabaseInitialized) {
                    setIsLoadingBooks(false);
                    return;
                }
                const [localBooks, localSeries] = await Promise.all([
                    tauri.getBooks(),
                    tauri.getSeriesList() as Promise<SeriesListItemProps[]>
                ]);
                booksResponse = localBooks.map(b => ({...b, itIsLocal: true}));
                seriesResponse = localSeries;
            }

            // ═══════════════════════════════════════════════════════════════
            // PARTIE 2 : CRÉATION DU MAPPING BOOK → SERIES
            // ═══════════════════════════════════════════════════════════════

            const bookToSeriesMap: Map<string, SeriesListItemProps> = new Map();
            seriesResponse.forEach((series: SeriesListItemProps): void => {
                series.bookIds.forEach((bookId: string): void => {
                    bookToSeriesMap.set(bookId, series);
                });
            });

            // ═══════════════════════════════════════════════════════════════
            // PARTIE 3 : TRANSFORMATION DES LIVRES
            // ═══════════════════════════════════════════════════════════════

            const transformedBooks: (BookProps & { itIsLocal?: boolean })[] = booksResponse.map(book => {
                const imageDataUrl = book.coverImage ? 'data:image/jpeg;base64,' + book.coverImage : '';
                return {
                    bookId: book.bookId,
                    type: Book.getBookTypeLabel(book.type),
                    title: book.title,
                    subTitle: book.subTitle,
                    summary: book.summary,
                    serie: book.serie,
                    publicationDate: book.publicationDate,
                    desiredWordCount: book.desiredWordCount,
                    totalWordCount: 0,
                    coverImage: imageDataUrl,
                    itIsLocal: book.itIsLocal
                };
            });

            // ═══════════════════════════════════════════════════════════════
            // PARTIE 4 : GROUPEMENT PAR CATÉGORIE AVEC SÉRIES
            // ═══════════════════════════════════════════════════════════════

            const itemsByCategory: Record<string, CategoryItem[]> = {};
            const processedSeriesIds: Set<string> = new Set();

            transformedBooks.forEach((book): void => {
                const categoryLabel: string = t(book.type);
                if (!itemsByCategory[categoryLabel]) {
                    itemsByCategory[categoryLabel] = [];
                }

                const seriesInfo = bookToSeriesMap.get(book.bookId);

                if (seriesInfo && !processedSeriesIds.has(seriesInfo.id)) {
                    // Livre fait partie d'une série non encore traitée
                    processedSeriesIds.add(seriesInfo.id);

                    // Récupérer tous les livres de cette série
                    const seriesBooks: BookProps[] = transformedBooks.filter(
                        b => seriesInfo.bookIds.includes(b.bookId)
                    );

                    const seriesCard: SeriesCardProps = {
                        id: seriesInfo.id,
                        name: seriesInfo.name,
                        coverImage: seriesInfo.coverImage,
                        books: seriesBooks
                    };

                    itemsByCategory[categoryLabel].push({
                        type: 'series',
                        series: seriesCard
                    });
                } else if (!seriesInfo) {
                    // Livre individuel (pas dans une série)
                    itemsByCategory[categoryLabel].push({
                        type: 'book',
                        book: book
                    });
                }
            });

            // Ajouter les séries vides (orphelines)
            seriesResponse.forEach((series): void => {
                if (series.bookIds.length === 0) {
                    const emptySeriesCategory = t('bookList.emptySeries');
                    if (!itemsByCategory[emptySeriesCategory]) {
                        itemsByCategory[emptySeriesCategory] = [];
                    }
                    itemsByCategory[emptySeriesCategory].push({
                        type: 'series',
                        series: {
                            id: series.id,
                            name: series.name,
                            coverImage: series.coverImage,
                            books: []
                        }
                    });
                }
            });

            setGroupedItems(itemsByCategory);

        } catch (error: unknown) {
            if (error instanceof Error) {
                errorMessage(error.message);
            } else {
                errorMessage(t("bookList.errorBooksFetch"));
            }
        } finally {
            setIsLoadingBooks(false);
        }
    }

    function getFilteredGroupedItems(): Record<string, CategoryItem[]> {
        if (!searchQuery) {
            return groupedItems;
        }

        const filtered: Record<string, CategoryItem[]> = {};

        Object.entries(groupedItems).forEach(([category, items]) => {
            const filteredItems = items.filter((item): boolean => {
                if (item.type === 'book' && item.book) {
                    return item.book.title.toLowerCase().includes(searchQuery.toLowerCase());
                }
                if (item.type === 'series' && item.series) {
                    const matchesSeriesName = item.series.name.toLowerCase().includes(searchQuery.toLowerCase());
                    const matchesBookTitle = item.series.books.some(
                        book => book.title.toLowerCase().includes(searchQuery.toLowerCase())
                    );
                    return matchesSeriesName || matchesBookTitle;
                }
                return false;
            });

            if (filteredItems.length > 0) {
                filtered[category] = filteredItems;
            }
        });

        return filtered;
    }

    function getTotalItemsCount(items: CategoryItem[]): number {
        return items.reduce((count, item) => {
            if (item.type === 'book') return count + 1;
            if (item.type === 'series' && item.series) return count + item.series.books.length;
            return count;
        }, 0);
    }

    function detectBookSyncStatus(bookId: string): SyncType {
        if (serverOnlyBooks.find((book: SyncedBook): boolean => book.id === bookId)) return 'server-only';
        if (localOnlyBooks.find((book: SyncedBook): boolean => book.id === bookId)) return 'local-only';
        if (booksToSyncFromServer.find((book: BookSyncCompare): boolean => book.id === bookId)) return 'to-sync-from-server';
        if (booksToSyncToServer.find((book: BookSyncCompare): boolean => book.id === bookId)) return 'to-sync-to-server';
        return 'synced';
    }

    function detectSeriesSyncStatus(seriesId: string): SeriesSyncType {
        if (serverOnlySeries.find((series: SyncedSeries): boolean => series.id === seriesId)) return 'server-only';
        if (localOnlySeries.find((series: SyncedSeries): boolean => series.id === seriesId)) return 'local-only';
        if (seriesToSyncFromServer.find((series: SeriesSyncCompare): boolean => series.id === seriesId)) return 'to-sync-from-server';
        if (seriesToSyncToServer.find((series: SeriesSyncCompare): boolean => series.id === seriesId)) return 'to-sync-to-server';
        return 'synced';
    }

    async function handleBookClick(bookId: string): Promise<void> {
        try {
            let localBookOnly: boolean = false;
            let bookResponse: BookProps | null = null;

            // DUAL LOGIC
            const isOfflineBook = localOnlyBooks.find((book: SyncedBook): boolean => book.id === bookId);
            if (isCurrentlyOffline() || isOfflineBook) {
                if (isCurrentlyOffline() && !offlineMode.isDatabaseInitialized) {
                    errorMessage(t("bookList.errorBookDetails"));
                    return;
                }
                bookResponse = await tauri.getBookBasicInformation(bookId);
                if (bookResponse) localBookOnly = true;
            }
            if (!bookResponse) {
                bookResponse = await System.authGetQueryToServer<BookProps>(
                    'book/basic-information', accessToken, lang, {id: bookId}
                );
            }

            if (!bookResponse) {
                errorMessage(t("bookList.errorBookDetails"));
                return;
            }

            if (setBook) {
                setBook({
                    bookId: bookId,
                    title: bookResponse.title || '',
                    subTitle: bookResponse.subTitle || '',
                    summary: bookResponse.summary || '',
                    type: bookResponse.type || '',
                    serie: bookResponse.serie,
                    seriesId: bookResponse.seriesId,
                    publicationDate: bookResponse.publicationDate || '',
                    desiredWordCount: bookResponse.desiredWordCount || 0,
                    totalWordCount: 0,
                    localBook: localBookOnly,
                    coverImage: bookResponse.coverImage ? 'data:image/jpeg;base64,' + bookResponse.coverImage : '',
                    quillsenseEnabled: bookResponse.quillsenseEnabled,
                    tools: bookResponse.tools,
                });
            }
        } catch (error: unknown) {
            if (error instanceof Error) {
                errorMessage(error.message);
            } else {
                errorMessage(t("bookList.errorUnknown"));
            }
        }
    }

    function scrollCarousel(category: string, direction: 'left' | 'right'): void {
        const container: HTMLDivElement | null = carouselRefs.current[category];
        if (!container) return;
        const cardWidth: number = container.querySelector<HTMLDivElement>(':scope > div')?.offsetWidth || 250;
        const scrollAmount: number = cardWidth * 2;
        container.scrollBy({
            left: direction === 'left' ? -scrollAmount : scrollAmount,
            behavior: 'smooth'
        });
    }

    function handleSeriesSettingsClick(seriesId: string): void {
        const isLocal: boolean = isCurrentlyOffline() ||
            Boolean(localOnlySeries.find((s: SyncedSeries): boolean => s.id === seriesId));
        setIsLocalSeries(isLocal);
        setShowSeriesSettingId(seriesId);
    }

    const filteredItems = getFilteredGroupedItems();

    return (
        <div
            className="flex flex-col items-center h-full overflow-hidden w-full text-text-primary font-['Lora']">
            {session?.user && (
                <div data-guide="search-bar" className="w-full max-w-3xl px-4 pt-6 pb-4">
                    <SearchBook searchQuery={searchQuery} setSearchQuery={setSearchQuery}/>
                </div>
            )}
            <div className="flex flex-col w-full overflow-y-auto overflow-x-hidden h-full min-h-0 flex-grow">
                {
                    isLoadingBooks ? (
                        <>
                            <div className="text-center mb-8 px-6">
                                <h1 className="font-['ADLaM_Display'] text-4xl mb-3 text-text-primary">{t("bookList.library")}</h1>
                                <p className="text-muted italic text-lg">{t("bookList.booksAreMirrors")}</p>
                            </div>

                            <div className="w-full mb-10">
                                <div className="flex justify-between items-center w-full max-w-5xl mx-auto mb-4 px-6">
                                    <div className="h-8 bg-secondary/30 rounded-xl w-32 animate-pulse"></div>
                                    <div className="h-6 bg-secondary/20 rounded-lg w-24 animate-pulse"></div>
                                </div>

                                <div className="flex flex-wrap justify-center items-start w-full px-4">
                                    {Array.from({length: 6}).map((_, id: number) => (
                                        <div key={id}
                                             className="w-full sm:w-1/3 md:w-1/4 lg:w-1/5 xl:w-1/6 p-2 box-border">
                                            <BookCardSkeleton/>
                                        </div>
                                    ))}
                                </div>
                            </div>
                        </>
                    ) : Object.entries(filteredItems).length > 0 ? (
                        <>
                            <div className="text-center mb-8 px-6">
                                <h1 className="font-['ADLaM_Display'] text-4xl mb-3 text-text-primary">{t("bookList.library")}</h1>
                                <p className="text-muted italic text-lg">{t("bookList.booksAreMirrors")}</p>
                            </div>

                            {Object.entries(filteredItems).map(([category, items], index) => (
                                <div key={category} {...(index === 0 && {'data-guide': 'book-category'})}
                                     className="w-full mb-10">
                                    <div
                                        className="flex justify-between items-center w-full max-w-5xl mx-auto mb-6 px-6">
                                        <h2 className="text-3xl text-text-primary capitalize font-['ADLaM_Display'] flex items-center gap-3">
                                            <span className="w-1 h-8 bg-primary rounded-full"></span>
                                            {category}
                                        </h2>
                                        <span
                                            className="text-muted text-lg font-medium bg-secondary/30 px-4 py-1.5 rounded-full">
                                            {getTotalItemsCount(items)} {t("bookList.works")}
                                        </span>
                                    </div>

                                    <div className="group relative w-full">
                                        <button
                                            onClick={() => scrollCarousel(category, 'left')}
                                            className="absolute left-3 top-1/2 -translate-y-1/2 z-10 bg-primary/80 backdrop-blur-sm hover:bg-primary text-white rounded-2xl w-12 h-12 flex items-center justify-center shadow-xl border border-primary-light/30 transition-all duration-200 opacity-0 group-hover:opacity-100 hover:scale-110"
                                        >
                                            <FontAwesomeIcon icon={faChevronLeft} className="w-5 h-5"/>
                                        </button>

                                        <div
                                            ref={(el: HTMLDivElement | null) => { carouselRefs.current[category] = el; }}
                                            className="flex items-start w-full overflow-hidden px-4 gap-2 scroll-smooth"
                                        >
                                            {items.map((item, idx) => {
                                                if (item.type === 'book' && item.book) {
                                                    return (
                                                        <div key={item.book.bookId}
                                                             {...(idx === 0 && {'data-guide': 'book-card'})}
                                                             className={`flex-shrink-0 w-64 sm:w-52 md:w-48 lg:w-56 xl:w-64 p-2 box-border ${User.guideTourDone(session.user?.guideTour || [], 'new-first-book') && 'mb-[200px]'}`}>
                                                            <BookCard
                                                                book={item.book}
                                                                syncStatus={detectBookSyncStatus(item.book.bookId)}
                                                                onClickCallback={handleBookClick}
                                                                index={idx}
                                                            />
                                                        </div>
                                                    );
                                                }
                                                if (item.type === 'series' && item.series) {
                                                    return (
                                                        <SeriesCard
                                                            key={item.series.id}
                                                            series={item.series}
                                                            onBookClick={handleBookClick}
                                                            onSettingsClick={handleSeriesSettingsClick}
                                                            getSyncStatus={detectBookSyncStatus}
                                                            seriesSyncStatus={detectSeriesSyncStatus(item.series.id)}
                                                        />
                                                    );
                                                }
                                                return null;
                                            })}
                                        </div>

                                        <button
                                            onClick={() => scrollCarousel(category, 'right')}
                                            className="absolute right-3 top-1/2 -translate-y-1/2 z-10 bg-primary/80 backdrop-blur-sm hover:bg-primary text-white rounded-2xl w-12 h-12 flex items-center justify-center shadow-xl border border-primary-light/30 transition-all duration-200 opacity-0 group-hover:opacity-100 hover:scale-110"
                                        >
                                            <FontAwesomeIcon icon={faChevronRight} className="w-5 h-5"/>
                                        </button>
                                    </div>
                                </div>
                            ))}
                        </>
                    ) : (
                        <div className="flex items-center justify-center h-full">
                            <div className="text-center p-8 max-w-lg">
                                <div
                                    className="w-24 h-24 bg-primary/20 text-primary rounded-2xl flex items-center justify-center mx-auto mb-6 shadow-lg animate-pulse">
                                    <FontAwesomeIcon icon={faBook} className={'w-12 h-12'}/>
                                </div>
                                <h2 className="text-4xl font-['ADLaM_Display'] mb-4 text-text-primary">{t("bookList.welcomeWritingWorkshop")}</h2>
                                <p className="text-muted mb-6 text-lg leading-relaxed">
                                    {t("bookList.whitePageText")}
                                </p>
                            </div>
                        </div>
                    )}
            </div>
            {
                bookGuide && <GuideTour stepId={0} steps={bookGuideSteps} onComplete={handleFirstBookGuide}
                                        onClose={() => setBookGuide(false)}/>
            }
            {showSeriesSettingId && (
                <SeriesSetting
                    seriesId={showSeriesSettingId}
                    localSeries={isLocalSeries}
                    onClose={() => {
                        setShowSeriesSettingId(null);
                        setIsLocalSeries(false);
                    }}
                />
            )}
        </div>
    );
}
