import { invalidate } from '$app/navigation';
import { page } from '$app/state';
import {
	deleteBookFiles,
	deleteBooks,
	listBooks,
	updateBookProgress
} from '$lib/api';
import {
	type Book,
	type UpdateBookProgress,
	type BookQuery,
	type PaginatedResponse,
} from '$lib/schema';
import { getContext, setContext } from 'svelte';
import { toast } from 'svelte-sonner';

export class BookOperationsState {
	public libraryId = $state<string>('');

	// Delete dialog related state
	deleteDialogOpen = $state(false);
	deleteDialogTitle = $state('Delete books?');
	deleteFn = $state((deleteFiles: boolean) => {});

	// Edit dialog related state
	editDialogOpen = $state(false);
	bookToEdit = $state<Book>();

	// Upload dialog related state
	uploadDialogOpen = $state(false);

	constructor(libraryId: string | number) {
		this.libraryId = libraryId.toString();
	}

	async listBooks(queryData: BookQuery): Promise<PaginatedResponse<Book>> {
		const cleanParams = Object.fromEntries(
			Object.entries(queryData).filter(([_, value]) => {
				// Remove empty values
				if (value === undefined || value === null) return false;
				if (Array.isArray(value) && value.length === 0) return false;
				if (value === '') return false;
				return true;
			})
		);

		return await listBooks({
			// cacheTimestamp: this.cacheTimestamp,
			libraries: [this.libraryId],
			...cleanParams
		});
	}

	async deleteBooks(bookIds: string[] | number[], deleteFiles: boolean, reload: boolean = true) {
		try {
			await deleteBooks({
				book_ids: bookIds,
				delete_files: deleteFiles,
				library_id: this.libraryId
			});

			if (bookIds.length > 1) toast.success(`Deleted ${bookIds.length} books!`);
			else toast.success(`Book deleted!`);

			this.deleteDialogOpen = false;

			// Reload the page after deleting a book to get fresh data
			if (reload) await this.reload();
		} catch (error) {
			toast.error('Failed to delete.');
			console.error('Failed to delete book(s): ', error);
		}
	}

	async deleteBookFiles(bookId: number, fileIds: number[], deleteFiles: boolean) {
		try {
			await deleteBookFiles({
				book_id: bookId,
				file_ids: fileIds,
				delete_files: deleteFiles
			});

			await invalidate('app:books');

			if (fileIds.length > 1) toast.success(`Deleted ${fileIds.length} files!`);
			else toast.success(`Deleted file!`);
		} catch (error) {
			toast.error('Failed to delete file(s)');
			console.error('Failed to delete book files: ', error);
		}
	}

	async updateBookProgress(bookIds: string[] | number[], progressRecord: UpdateBookProgress) {
		await updateBookProgress({
			book_ids: bookIds,
			...progressRecord
		});
	}

	async markBooksAsComplete(bookIds: string[] | number[]) {
		try {
			await this.updateBookProgress(bookIds, {
				progress: 1,
				completed: true
			});

			if (bookIds.length > 1) toast.success('Book marked as complete!');
			else toast.success(`${bookIds.length} books marked as complete!`);

			// Get fresh data
			await this.reload();
		} catch (error) {
			toast.error('Failed to mark books as complete.');
			console.error('Mark as complete operation failed: ', error);
		}
	}

	async markBooksAsIncomplete(bookIds: number[]) {
		try {
			await this.updateBookProgress(bookIds, {
				progress: 0,
				completed: false
			});

			if (bookIds.length > 1) toast.success('Book progress reset!');
			else toast.success(`Reset progress for ${bookIds.length} books!`);

			// get fresh data
			await this.reload();
		} catch (error) {
			toast.error('Failed to reset progress.');
			console.error('Mark as incomplete operation failed: ', error);
		}
	}

	async downloadBooks(bookIds: string[] | number[], filename?: string) {
		// Construct the download URL
		const downloadUrl = new URL(`/api/books/download`, page.url.origin);
		downloadUrl.searchParams.set('library_id', this.libraryId);
		bookIds.forEach((id) => {
			downloadUrl.searchParams.set('book_ids', id.toString());
		});

		this.download(downloadUrl, filename || 'download.zip');
	}

	async downloadBookFile(bookId: number, fileId: number, filename: string) {
		// Construct the download URL
		const downloadUrl = new URL(`/api/books/download/${bookId}/${fileId}`, page.url.origin);
		this.download(downloadUrl, filename);
	}

	private download(downloadUrl: URL, filename: string) {
		// Create a download anchor element, click it, then remove it
		const link = document.createElement('a');
		link.download = filename;
		link.href = downloadUrl.href;
		link.style.display = 'none';
		document.body.appendChild(link);
		link.click();
		document.body.removeChild(link);
	}

	private async reload() {
		await invalidate('app:books');
	}
}

const BOOK_OPS_KEY = Symbol('BOOK_OPERATIONS');

export function setBookOperationsState(libraryId: string | number) {
	return setContext(BOOK_OPS_KEY, new BookOperationsState(libraryId));
}

export function getBookOperationsState() {
	return getContext<ReturnType<typeof setBookOperationsState>>(BOOK_OPS_KEY);
}
