import {useContext} from 'react';
import System from '@/lib/models/System';
import {SessionContext} from '@/context/SessionContext';
import {LangContext} from '@/context/LangContext';
import {AlertContext} from '@/context/AlertContext';
import OfflineContext from '@/context/OfflineContext';
import {BooksSyncContext} from '@/context/BooksSyncContext';
import {CompleteBook} from '@/lib/models/Book';
import {BookSyncCompare, SyncedBook} from '@/lib/models/SyncedBook';
import {useTranslations} from 'next-intl';

interface RemovedItemRecord {
    removal_id: string;
    table_name: string;
    entity_id: string;
    book_id: string | null;
    user_id: string;
    deleted_at: number;
}

interface SyncedBooksResponse {
    books: SyncedBook[];
    tombstones: RemovedItemRecord[];
}

export default function useSyncBooks() {
    const t = useTranslations();
    const {session} = useContext(SessionContext);
    const {lang} = useContext(LangContext);
    const {errorMessage} = useContext(AlertContext);
    const {isCurrentlyOffline, offlineMode} = useContext(OfflineContext);
    const {
        booksToSyncToServer,
        booksToSyncFromServer,
        localOnlyBooks,
        serverOnlyBooks,
        setLocalOnlyBooks,
        setServerOnlyBooks,
        setServerSyncedBooks,
        setLocalSyncedBooks,
        setBooksToSyncFromServer,
        setBooksToSyncToServer
    } = useContext(BooksSyncContext);

    async function upload(bookId: string): Promise<boolean> {
        if (isCurrentlyOffline()) return false;

        try {
            const bookToSync: CompleteBook = await window.electron.invoke<CompleteBook>('db:book:uploadToServer', bookId);
            if (!bookToSync) {
                errorMessage(t('bookCard.uploadError'));
                return false;
            }
            const response: boolean = await System.authPostToServer('book/sync/upload', {
                book: bookToSync
            }, session.accessToken, lang);
            if (!response) {
                errorMessage(t('bookCard.uploadError'));
                return false;
            }
            const uploadedBook: SyncedBook | undefined = localOnlyBooks.find((book: SyncedBook): boolean => book.id === bookId);
            setLocalOnlyBooks((prevBooks: SyncedBook[]): SyncedBook[] => {
                return prevBooks.filter((book: SyncedBook): boolean => book.id !== bookId);
            });
            if (uploadedBook) {
                setLocalSyncedBooks((prev: SyncedBook[]): SyncedBook[] => [...prev, uploadedBook]);
                setServerSyncedBooks((prev: SyncedBook[]): SyncedBook[] => [...prev, uploadedBook]);
            }
            return true;
        } catch (e: unknown) {
            if (e instanceof Error) {
                errorMessage(e.message);
            } else {
                errorMessage(t('bookCard.uploadError'));
            }
            return false;
        }
    }

    async function download(bookId: string): Promise<boolean> {
        if (isCurrentlyOffline()) return false;

        try {
            const response: CompleteBook = await System.authGetQueryToServer('book/sync/download', session.accessToken, lang, {bookId});
            if (!response) {
                errorMessage(t('bookCard.downloadError'));
                return false;
            }
            const syncStatus: boolean = await window.electron.invoke<boolean>('db:book:syncSave', response);
            if (!syncStatus) {
                errorMessage(t('bookCard.downloadError'));
                return false;
            }
            const downloadedBook: SyncedBook | undefined = serverOnlyBooks.find((book: SyncedBook): boolean => book.id === bookId);
            setServerOnlyBooks((prevBooks: SyncedBook[]): SyncedBook[] => {
                return prevBooks.filter((book: SyncedBook): boolean => book.id !== bookId);
            });
            if (downloadedBook) {
                setLocalSyncedBooks((prev: SyncedBook[]): SyncedBook[] => [...prev, downloadedBook]);
                setServerSyncedBooks((prev: SyncedBook[]): SyncedBook[] => [...prev, downloadedBook]);
            }
            return true;
        } catch (e: unknown) {
            if (e instanceof Error) {
                errorMessage(e.message);
            } else {
                errorMessage(t('bookCard.downloadError'));
            }
            return false;
        }
    }

    async function syncFromServer(bookId: string): Promise<boolean> {
        if (isCurrentlyOffline()) return false;

        try {
            const bookToFetch: BookSyncCompare | undefined = booksToSyncFromServer.find((book: BookSyncCompare): boolean => book.id === bookId);
            if (!bookToFetch) {
                errorMessage(t('bookCard.syncFromServerError'));
                return false;
            }
            const response: CompleteBook = await System.authPostToServer('book/sync/server-to-client', {
                bookToSync: bookToFetch
            }, session.accessToken, lang);
            if (!response) {
                errorMessage(t('bookCard.syncFromServerError'));
                return false;
            }
            const syncStatus: boolean = await window.electron.invoke<boolean>('db:book:sync:toClient', response);
            if (!syncStatus) {
                errorMessage(t('bookCard.syncFromServerError'));
                return false;
            }
            setBooksToSyncFromServer((prev: BookSyncCompare[]): BookSyncCompare[] =>
                prev.filter((book: BookSyncCompare): boolean => book.id !== bookId)
            );
            return true;
        } catch (e: unknown) {
            if (e instanceof Error) {
                errorMessage(e.message);
            } else {
                errorMessage(t('bookCard.syncFromServerError'));
            }
            return false;
        }
    }

    async function syncToServer(bookId: string): Promise<boolean> {
        if (isCurrentlyOffline()) return false;

        try {
            const bookToFetch: BookSyncCompare | undefined = booksToSyncToServer.find((book: BookSyncCompare): boolean => book.id === bookId);
            if (!bookToFetch) {
                errorMessage(t('bookCard.syncToServerError'));
                return false;
            }
            const bookToSync: CompleteBook = await window.electron.invoke<CompleteBook>('db:book:sync:toServer', bookToFetch);
            if (!bookToSync) {
                errorMessage(t('bookCard.syncToServerError'));
                return false;
            }
            const response: boolean = await System.authPatchToServer('book/sync/client-to-server', {
                book: bookToSync
            }, session.accessToken, lang);
            if (!response) {
                errorMessage(t('bookCard.syncToServerError'));
                return false;
            }
            setBooksToSyncToServer((prev: BookSyncCompare[]): BookSyncCompare[] =>
                prev.filter((book: BookSyncCompare): boolean => book.id !== bookId)
            );
            return true;
        } catch (e: unknown) {
            if (e instanceof Error) {
                errorMessage(e.message);
            } else {
                errorMessage(t('bookCard.syncToServerError'));
            }
            return false;
        }
    }

    async function syncAllToServer(): Promise<void> {
        for (const diff of booksToSyncToServer) {
            await syncToServer(diff.id);
        }
    }

    async function syncAllFromServer(): Promise<void> {
        for (const diff of booksToSyncFromServer) {
            await syncFromServer(diff.id);
        }
    }

    async function refreshBooks(): Promise<void> {
        try {
            let localBooksResponse: SyncedBook[] = [];
            let serverBooksResponse: SyncedBook[] = [];

            if (!isCurrentlyOffline()) {
                if (offlineMode.isDatabaseInitialized) {
                    localBooksResponse = await window.electron.invoke<SyncedBook[]>('db:books:synced');

                    // Get lastOnlineTimestamp from localStorage (or 0 if not set)
                    const lastOnlineStr: string | null = localStorage.getItem('lastOnlineTimestamp');
                    const lastOnlineTimestamp: number = lastOnlineStr ? parseInt(lastOnlineStr, 10) : 0;

                    // Get local tombstones since lastOnlineTimestamp via IPC
                    const localTombstones: RemovedItemRecord[] = await window.electron.invoke<RemovedItemRecord[]>(
                        'db:tombstones:since',
                        lastOnlineTimestamp
                    );

                    // Call server with POST and tombstones
                    const serverResponse: SyncedBooksResponse = await System.authPostToServer<SyncedBooksResponse>(
                        'books/synced',
                        { lastOnlineTimestamp, tombstones: localTombstones },
                        session.accessToken,
                        lang
                    );

                    serverBooksResponse = serverResponse.books;

                    // Apply server tombstones locally via IPC
                    await window.electron.invoke<void>('db:tombstones:apply:books', serverResponse.tombstones);
                } else {
                    // No local DB but online - just get server books without tombstones
                    const serverResponse: SyncedBooksResponse = await System.authPostToServer<SyncedBooksResponse>(
                        'books/synced',
                        { lastOnlineTimestamp: 0, tombstones: [] },
                        session.accessToken,
                        lang
                    );
                    serverBooksResponse = serverResponse.books;
                }
            } else {
                if (offlineMode.isDatabaseInitialized) {
                    localBooksResponse = await window.electron.invoke<SyncedBook[]>('db:books:synced');
                }
            }

            setServerSyncedBooks(serverBooksResponse);
            setLocalSyncedBooks(localBooksResponse);
        } catch (e: unknown) {
            if (e instanceof Error) {
                errorMessage(e.message);
            } else {
                errorMessage(t('bookCard.refreshError'));
            }
        }
    }

    return {
        upload,
        download,
        syncFromServer,
        syncToServer,
        syncAllToServer,
        syncAllFromServer,
        refreshBooks,
        localOnlyBooks,
        serverOnlyBooks,
        booksToSyncToServer,
        booksToSyncFromServer
    };
}
