<script lang="ts">
	import { onMount } from 'svelte';
	import { SvelteMap } from 'svelte/reactivity';
	import { client } from '$lib/graphql/client';
	import { NovelsDocument, type NovelsQuery, type NovelTagDto } from '$lib/graphql/__generated__/graphql';
	import NovelCard from './NovelCard.svelte';
	import NovelFilters from './NovelFilters.svelte';
	import ImportNovelModal from './ImportNovelModal.svelte';
	import { Button } from '$lib/components/ui/button';
	import { Card, CardContent, CardHeader, CardTitle } from '$lib/components/ui/card';
	import { isAuthenticated } from '$lib/auth/authStore';
	import {
		type NovelFilters as NovelFiltersType,
		parseFiltersFromURL,
		syncFiltersToURL,
		filtersToGraphQLWhere,
		sortToGraphQLOrder,
		hasActiveFilters,
		EMPTY_FILTERS
	} from '$lib/utils/filterParams';

	const PAGE_SIZE = 12;

	type NovelEdge = NonNullable<NovelsQuery['novels']>['edges'][number];

	let edges: NovelEdge[] = $state([]);
	let pageInfo: NonNullable<NovelsQuery['novels']>['pageInfo'] | null = $state(null);
	let fetching = $state(false);
	let error: string | null = $state(null);
	let initialLoad = $state(true);
	let filters: NovelFiltersType = $state({ ...EMPTY_FILTERS });
	let showImportModal = $state(false);

	const hasNextPage = $derived(pageInfo?.hasNextPage ?? false);
	const novels = $derived(edges.map((edge) => edge.node).filter(Boolean));

	// Extract unique tags from loaded novels for the tag filter dropdown
	const availableTags = $derived(() => {
		const tagMap = new SvelteMap<string, Pick<NovelTagDto, 'key' | 'displayName'>>();
		for (const novel of novels) {
			for (const tag of novel.tags ?? []) {
				if (!tagMap.has(tag.key)) {
					tagMap.set(tag.key, { key: tag.key, displayName: tag.displayName });
				}
			}
		}
		return Array.from(tagMap.values()).sort((a, b) => a.displayName.localeCompare(b.displayName));
	});

	async function fetchNovels(after: string | null = null) {
		fetching = true;
		error = null;

		try {
			const where = filtersToGraphQLWhere(filters);
			const order = sortToGraphQLOrder(filters.sort);
			const result = await client
				.query(NovelsDocument, { first: PAGE_SIZE, after, where, order })
				.toPromise();

			if (result.error) {
				error = result.error.message;
				return;
			}

			if (result.data?.novels) {
				if (after) {
					// Append for pagination
					edges = [...edges, ...result.data.novels.edges];
				} else {
					// Initial load or filter change
					edges = result.data.novels.edges;
				}
				pageInfo = result.data.novels.pageInfo;
			}
		} catch (e) {
			error = e instanceof Error ? e.message : 'Unknown error';
		} finally {
			fetching = false;
			initialLoad = false;
		}
	}

	function loadMore() {
		if (pageInfo?.endCursor) {
			fetchNovels(pageInfo.endCursor);
		}
	}

	function handleFilterChange(newFilters: NovelFiltersType) {
		filters = newFilters;
		// Reset pagination and refetch
		edges = [];
		pageInfo = null;
		syncFiltersToURL(filters);
		fetchNovels();
	}

	onMount(() => {
		// Parse filters from URL on initial load
		filters = parseFiltersFromURL();
		fetchNovels();

		// Listen for browser back/forward navigation
		const handlePopState = () => {
			filters = parseFiltersFromURL();
			edges = [];
			pageInfo = null;
			fetchNovels();
		};

		window.addEventListener('popstate', handlePopState);
		return () => window.removeEventListener('popstate', handlePopState);
	});
</script>

<div class="space-y-4">
	<Card class="shadow-md shadow-primary/10">
		<CardHeader>
			<div class="flex items-center justify-between">
				<CardTitle>Controls</CardTitle>
				{#if $isAuthenticated}
					<Button variant="outline" onclick={() => (showImportModal = true)}>
						Import Novel
					</Button>
				{/if}
			</div>
		</CardHeader>
		<CardContent>
			<NovelFilters {filters} onFilterChange={handleFilterChange} availableTags={availableTags()} />
		</CardContent>
	</Card>

	{#if fetching && initialLoad}
		<Card>
			<CardContent>
				<div class="flex items-center justify-center py-8">
					<div
						class="border-primary h-10 w-10 animate-spin rounded-full border-2 border-t-transparent"
						aria-label="Loading novels"
					></div>
				</div>
			</CardContent>
		</Card>
	{/if}

	{#if error}
		<Card class="border-destructive/40 bg-destructive/5">
			<CardContent>
				<p class="text-destructive py-4 text-sm">Could not load novels: {error}</p>
			</CardContent>
		</Card>
	{/if}

	{#if !fetching && novels.length === 0 && !error && !initialLoad}
		<Card>
			<CardContent>
				<p class="text-muted-foreground py-4 text-sm">
					{#if hasActiveFilters(filters)}
						No novels match your filters. Try adjusting your search criteria.
					{:else}
						No novels found yet. Try adding content to the gateway.
					{/if}
				</p>
			</CardContent>
		</Card>
	{/if}

	{#if novels.length > 0}
		<div class="grid gap-4 sm:grid-cols-2 lg:grid-cols-3">
			{#each novels as novel (novel.id)}
				<NovelCard {novel} />
			{/each}
		</div>
	{/if}

	{#if hasNextPage}
		<div class="flex justify-center">
			<Button onclick={loadMore} variant="outline" disabled={fetching} class="min-w-[160px]">
				{fetching ? 'Loading...' : 'Load more'}
			</Button>
		</div>
	{/if}
</div>

<ImportNovelModal
	bind:open={showImportModal}
	onClose={() => (showImportModal = false)}
	onSuccess={() => fetchNovels()}
/>
