<script lang="ts">
	//  TODO: Add type hints to the rest of this file

	import { browser } from '$app/environment';
	import { onDestroy, onMount } from 'svelte';

	import { Book, Rendition } from 'epubjs';

	import '../../../app.css';
	import * as Sidebar from '$lib/components/ui/sidebar/index';
	import * as Collapsible from '$lib/components/ui/collapsible/index';

	import { ChevronDown, ChevronRight, ChevronLeft } from '@lucide/svelte';

	import { Spinner } from '$lib/components/ui/spinner/index';
	import type { DisplayedLocation } from 'epubjs/types/rendition';

	let { bookUrl, bookId, initialProgress = 0, initialEpubLoc = null } = $props();

	let epubViewer = $state<HTMLElement>();
	let containerWidth = $state(0);
	let containerHeight = $state(0);
	let isReaderVisible = $state(false);

	let hasNextPage = $state(true);
	let hasPrevPage = $state(false);

	let book: Book | undefined = $state();
	let rendition = $state<Rendition>();
	let chapters = $state([]);

	let isMounted = $state(false);

	let currentLocation = $state(initialEpubLoc);
	let currentProgress = $state(initialProgress);

	let isSidebarOpen = $state(false);
	let debounceTimeout = $state<NodeJS.Timeout>();

	// Function to update dimensions
	function updateDimensions() {
		if (epubViewer) {
			// Get parent element dimensions
			const parent = epubViewer.parentElement;

			containerWidth = parent?.clientWidth! * 0.9 - 240;
			containerHeight = window.innerHeight * 0.8;
		}
	}

	// Handle window resize
	function handleResize() {
		updateDimensions();
		if (rendition) {
			rendition.resize(containerWidth, containerHeight);
		}
	}

	async function handleKeydown(e: KeyboardEvent) {
		if (e.key === 'ArrowLeft') {
			await prevPage();
		} else if (e.key === 'ArrowRight') {
			await nextPage();
		}
	}

	async function nextPage() {
		if (hasNextPage) await rendition!.next();
	}

	async function prevPage() {
		if (hasPrevPage) await rendition!.prev();
	}

	async function setUserBookProgress() {
		clearTimeout(debounceTimeout);

		debounceTimeout = setTimeout(async () => {
			await fetch(`/api/books/progress/${bookId}`, {
				method: 'POST',
				body: JSON.stringify({
					percentage: currentProgress,
					epub_cfi: currentLocation,
					completed: currentProgress === 1
				})
			});
		}, 3000);
	}

	const getChapters = async (book: Book) => {
		await book.ready;
		// Get spine items (basic chapter structure)
		const spineItems = book.spine.items.map((item, index) => ({
			id: item.idref,
			href: item.href,
			index: index,
			label: item.label || `Chapter ${index + 1}`,
			cfi: book.spine.get(index).cfiBase // Get CFI from spine
		}));

		// Get table of contents for better labels
		const toc = await book.loaded.navigation;

		// Combine spine items with TOC information
		const chapters = toc.toc.map((chapter) => {
			const spineItem = spineItems.find((item: any) => item.href === chapter.href);
			return {
				...spineItem,
				label: chapter.label || spineItem?.label,
				subitems: chapter.subitems,
				href: chapter.href,
				cfi: spineItem?.cfi || book.spine.get(chapter.href)?.cfiBase
			};
		});

		return chapters;
	};
	async function navigateToChapter(chapter: any) {
		try {
			if (!rendition || !book) return;

			// Ensure book is ready
			await book.ready;

			// Try different navigation methods
			if (chapter.href) {
				await rendition.display(chapter.href);
			} else {
				console.error('No valid navigation target found for chapter:', chapter);
			}
		} catch (error) {
			console.error('Error navigating to chapter:', error);
		}
	}

	onMount(async () => {
		if (browser) {
			// Initial setup
			updateDimensions();

			try {
				// Fetch the EPUB file
				const response = await fetch(bookUrl);
				const arrayBuffer = await response.arrayBuffer();

				// Create book from array buffer
				book = new Book();
				await book.open(arrayBuffer, 'binary');

				// Generate locations if they do not exist in localStorage
				let existingLocations = localStorage.getItem(`${bookId}-locations`);
				let locations;
				if (existingLocations) {
					locations = JSON.parse(existingLocations);
					book.locations.load(locations);
				} else {
					locations = await book.locations.generate(1600);
					// Save locations to localStorage
					localStorage.setItem(`${bookId}-locations`, JSON.stringify(locations));
				}

				await book.ready;

				// Render the book to the viewer element
				rendition = book.renderTo('epub-viewer', {
					width: containerWidth,
					height: containerHeight,
					spread: 'auto',
					snap: true,
					manager: 'continuous',
					flow: 'paginated'
				});

				// Set the key listener on the iframe element
				let keyListener = async function (e: any) {
					// Left Key
					if ((e.keyCode || e.which) == 37) {
						await prevPage();
					}
					// Right Key
					if ((e.keyCode || e.which) == 39) {
						await nextPage();
					}
				};

				// Add resize listener
				window.addEventListener('resize', handleResize);

				// Add Key listener
				rendition.on('keydown', keyListener);

				// Listen to location changes
				rendition.on('locationChanged', async (location: DisplayedLocation) => {
					if (!location?.start) return;

					currentLocation = rendition!.currentLocation().start.cfi;
					currentProgress = book?.locations.percentageFromCfi(currentLocation);

					await setUserBookProgress();

					hasNextPage = !rendition!.location.atEnd;
					hasPrevPage = !rendition!.location.atStart;
				});

				chapters = await getChapters(book);

				let initialLocationCfi =
					currentLocation || book.locations.cfiFromPercentage(currentProgress);

				if (initialLocationCfi) {
					await rendition.display(initialLocationCfi);
				} else {
					await rendition.display();
				}

				isMounted = true;
				isReaderVisible = true;
			} catch (error) {
				console.error('Error loading EPUB', error);
			}
		}
	});

	onDestroy(() => {
		if (browser) {
			window.removeEventListener('resize', handleResize);
			book.destroy();
		}
	});
</script>

<svelte:document onkeydown={handleKeydown} />

<Sidebar.Provider bind:open={isSidebarOpen}>
	<Sidebar.Root class={!isReaderVisible ? 'hidden' : ''}>
		<Sidebar.Header />
		<Sidebar.Content>
			<Sidebar.GroupLabel>Chapters</Sidebar.GroupLabel>
			<Sidebar.GroupContent>
				<Sidebar.Menu>
					{#each chapters as chapter}
						{#if chapter.subitems.length > 0}
							<Collapsible.Root class="group/collapsible">
								<div class="flex w-full items-center gap-1">
									<Sidebar.MenuItem class="min-w-0 flex-1">
										<Sidebar.MenuButton
											class="w-full"
											onclick={async () => await navigateToChapter(chapter)}
										>
											<span class="block truncate" title={chapter.label}>{chapter.label}</span>
										</Sidebar.MenuButton>
									</Sidebar.MenuItem>
									<Collapsible.Trigger class="flex-shrink-0 p-2">
										<ChevronDown
											class="h-4 w-4 transition-transform group-data-[state=open]/collapsible:rotate-180"
										/>
									</Collapsible.Trigger>
								</div>
								<Collapsible.Content>
									<Sidebar.MenuSub>
										{#each chapter.subitems as subchapter}
											<Sidebar.MenuSubItem class="min-w-0">
												<Sidebar.MenuButton
													class="w-full"
													onclick={async () => await navigateToChapter(subchapter)}
												>
													<span class="block truncate" title={subchapter.label}
														>{subchapter.label}</span
													>
												</Sidebar.MenuButton>
											</Sidebar.MenuSubItem>
										{/each}
									</Sidebar.MenuSub>
								</Collapsible.Content>
							</Collapsible.Root>
						{:else}
							<Sidebar.MenuItem class="min-w-0">
								<Sidebar.MenuButton
									class="w-full"
									onclick={async () => await navigateToChapter(chapter)}
								>
									<span class="block truncate">{chapter.label}</span>
								</Sidebar.MenuButton>
							</Sidebar.MenuItem>
						{/if}
					{/each}
				</Sidebar.Menu>
			</Sidebar.GroupContent>
			<Sidebar.Group />
		</Sidebar.Content>
		<Sidebar.Footer />
	</Sidebar.Root>
	<main class="flex w-full overflow-hidden">
		{#if browser}
			{#if !isReaderVisible}
				<div class="absolute inset-0 flex items-center justify-center">
					<Spinner />
				</div>
			{/if}

			<div class="h-full w-full {isReaderVisible ? '' : 'opacity-0'}">
				<div class="flex">
					<Sidebar.Trigger />
				</div>

				<div class="mt-[-30px] flex h-full w-full items-center justify-center">
					<ChevronLeft
						onclick={prevPage}
						class={hasPrevPage
							? 'text-primary/70 hover:cursor-pointer hover:text-primary'
							: 'text-muted'}
					/>
					<div
						id="epub-viewer"
						bind:this={epubViewer}
						class="h-[{containerHeight}px] w-[{containerWidth}] epub-content mx-8 rounded border-2 p-8 shadow-md"
					></div>
					<ChevronRight
						onclick={nextPage}
						class={hasNextPage
							? 'text-primary/70 hover:cursor-pointer hover:text-primary'
							: 'text-muted'}
					/>
				</div>
			</div>
		{/if}
	</main>
</Sidebar.Provider>

<style>
	.epub-content {
		position: relative;
		background: white;
	}

	/* Position separator relative to epub content */
	@media (min-width: 1209px) {
		.epub-content:after {
			/* Calculate position based on content width */
			--separator-position: calc(var(--content-width, 100%) / 2);
			position: absolute;
			width: 1px;
			border-right: 1px #000 solid;
			height: 90%;
			z-index: 1;
			left: 50%;
			transform: translateX(-50%);
			top: 5%;
			opacity: 0.15;
			box-shadow: -2px 0 15px rgba(0, 0, 0, 1);
			content: '';
		}
	}
</style>
