import {useContext} from 'react';
import {apiGet, apiPatch, apiPost} from '@/lib/api/client';
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/types/book-tables';
import {BookSyncCompare, SyncedBook} from '@/lib/types/synced-book';
import {useTranslations} from '@/lib/i18n';
import * as tauri from '@/lib/tauri';

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 tauri.uploadBookToServer(bookId) as CompleteBook;
            if (!bookToSync) {
                errorMessage(t('bookCard.uploadError'));
                return false;
            }
            const response: boolean = await apiPost('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 apiGet('book/sync/download', session.accessToken, lang, {bookId});
            if (!response) {
                errorMessage(t('bookCard.downloadError'));
                return false;
            }
            const syncStatus: boolean = await tauri.syncSaveBook(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 apiPost('book/sync/server-to-client', {
                bookToSync: bookToFetch
            }, session.accessToken, lang);
            if (!response) {
                errorMessage(t('bookCard.syncFromServerError'));
                return false;
            }
            const syncStatus: boolean = await tauri.syncBookToClient(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 tauri.syncBookToServer(bookToFetch) as CompleteBook;
            if (!bookToSync) {
                errorMessage(t('bookCard.syncToServerError'));
                return false;
            }
            const response: boolean = await apiPatch('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 tauri.getSyncedBooks() as SyncedBook[];

                    const lastOnlineStr: string | null = localStorage.getItem('lastOnlineTimestamp');
                    const lastOnlineTimestamp: number = lastOnlineStr ? parseInt(lastOnlineStr, 10) : 0;

                    const localTombstones: RemovedItemRecord[] = await tauri.getTombstonesSince(lastOnlineTimestamp) as RemovedItemRecord[];

                    const serverResponse: SyncedBooksResponse = await apiPost<SyncedBooksResponse>(
                        'books/synced',
                        { lastOnlineTimestamp, tombstones: localTombstones },
                        session.accessToken,
                        lang
                    );

                    serverBooksResponse = serverResponse.books;

                    await tauri.applyBookTombstones(serverResponse.tombstones as tauri.TombstoneRecord[]);
                } else {
                    // No local DB but online - just get server books without tombstones
                    const serverResponse: SyncedBooksResponse = await apiPost<SyncedBooksResponse>(
                        'books/synced',
                        { lastOnlineTimestamp: 0, tombstones: [] },
                        session.accessToken,
                        lang
                    );
                    serverBooksResponse = serverResponse.books;
                }
            } else {
                if (offlineMode.isDatabaseInitialized) {
                    localBooksResponse = await tauri.getSyncedBooks() as SyncedBook[];
                }
            }

            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
    };
}
