<script lang="ts">
	import { notebookStore } from '$lib/stores/notebook.svelte';
	import Button from '$lib/components/ui/button/button.svelte';
	import Textarea from '$lib/components/ui/textarea/textarea.svelte';
	import { Play, Square, Settings, Undo, Redo, RulerDimensionLine } from '@lucide/svelte';
	import { config } from '$lib/stores/settings.svelte';
	import DialogChatSettings from '$lib/components/app/dialogs/DialogChatSettings.svelte';
	import {
		ChatMessageStatistics,
		DialogChatError,
		KeyboardShortcutInfo,
		ModelsSelector
	} from '$lib/components/app';

	import { modelOptions, selectedModelId } from '$lib/stores/models.svelte';
	import { isRouterMode } from '$lib/stores/server.svelte';
	import * as Tooltip from '$lib/components/ui/tooltip';

	let settingsOpen = $state(false);

	import {
		AUTO_SCROLL_AT_BOTTOM_THRESHOLD,
		AUTO_SCROLL_INTERVAL,
		INITIAL_SCROLL_DELAY
	} from '$lib/constants/auto-scroll';
	import { onMount } from 'svelte';

	let disableAutoScroll = $derived(Boolean(config().disableAutoScroll));
	let showMessageStats = $derived(config().showMessageStats);
	let autoScrollEnabled = $state(true);
	let scrollContainer: HTMLTextAreaElement | null = $state(null);
	let lastScrollTop = $state(0);
	let scrollInterval: ReturnType<typeof setInterval> | undefined;
	let scrollTimeout: ReturnType<typeof setTimeout> | undefined;
	let userScrolledUp = $state(false);

	let isRouter = $derived(isRouterMode());

	let errorDialog = $derived(notebookStore.error);
	let canUndo = $derived(notebookStore.previousContent !== null && !notebookStore.isGenerating);
	let canRedo = $derived(notebookStore.undoneContent !== null && !notebookStore.isGenerating);

	function handleInput(e: Event) {
		const target = e.target as HTMLTextAreaElement;
		notebookStore.content = target.value;
		notebookStore.resetUndoRedo();
		if (activeModelId || !isRouter) {
			notebookStore.updateTokenCount(activeModelId);
		}
	}

	function handleErrorDialogOpenChange(open: boolean) {
		if (!open) {
			notebookStore.dismissError();
		}
	}

	async function handleGenerate() {
		if (!disableAutoScroll) {
			userScrolledUp = false;
			autoScrollEnabled = true;
			scrollToBottom();
		}

		if (activeModelId) {
			await notebookStore.generate(activeModelId);
		}
	}

	function handleUndo() {
		notebookStore.undo();
	}

	function handleRedo() {
		notebookStore.redo();
	}

	function handleStop() {
		notebookStore.stop();
	}

	let activeModelId = $derived.by(() => {
		const options = modelOptions();

		if (!isRouter) {
			return options.length > 0 ? options[0].model : null;
		}

		const selectedId = selectedModelId();
		if (selectedId) {
			const model = options.find((m) => m.id === selectedId);
			if (model) return model.model;
		}

		return null;
	});

	let hasModelSelected = $derived(!isRouter || !!selectedModelId());

	let generateTooltip = $derived.by(() => {
		if (!hasModelSelected) {
			return 'Please select a model first';
		}

		if (notebookStore.content.length == 0) {
			return 'Input some text first';
		}

		return '';
	});

	let canGenerate = $derived(notebookStore.content.length > 0 && hasModelSelected);
	let isDisabled = $derived(!canGenerate);

	function handleScroll() {
		if (disableAutoScroll || !scrollContainer) return;

		const { scrollTop, scrollHeight, clientHeight } = scrollContainer;
		const distanceFromBottom = scrollHeight - scrollTop - clientHeight;
		const isAtBottom = distanceFromBottom < AUTO_SCROLL_AT_BOTTOM_THRESHOLD;

		if (scrollTop < lastScrollTop && !isAtBottom) {
			userScrolledUp = true;
			autoScrollEnabled = false;
		} else if (isAtBottom && userScrolledUp) {
			userScrolledUp = false;
			autoScrollEnabled = true;
		}

		if (scrollTimeout) {
			clearTimeout(scrollTimeout);
		}

		scrollTimeout = setTimeout(() => {
			if (isAtBottom) {
				userScrolledUp = false;
				autoScrollEnabled = true;
			}
		}, AUTO_SCROLL_INTERVAL);

		lastScrollTop = scrollTop;
	}

	function scrollToBottom(behavior: ScrollBehavior = 'smooth') {
		if (disableAutoScroll) return;

		scrollContainer?.scrollTo({
			top: scrollContainer?.scrollHeight,
			behavior
		});
	}

	onMount(() => {
		if (!disableAutoScroll) {
			setTimeout(() => scrollToBottom('instant'), INITIAL_SCROLL_DELAY);
		}
	});

	$effect(() => {
		if (disableAutoScroll) {
			autoScrollEnabled = false;
			if (scrollInterval) {
				clearInterval(scrollInterval);
				scrollInterval = undefined;
			}
			return;
		}

		if (notebookStore.isGenerating && autoScrollEnabled) {
			scrollInterval = setInterval(() => scrollToBottom(), AUTO_SCROLL_INTERVAL);
		} else if (scrollInterval) {
			clearInterval(scrollInterval);
			scrollInterval = undefined;
		}
	});

	function handleBeforeUnload(event: BeforeUnloadEvent) {
		// This should prevent the browser from closing the tab if there is content in the notebook
		if (notebookStore.content.length > 0) {
			event.preventDefault();
			event.returnValue = '';
		}
	}

	function handleKeydown(event: KeyboardEvent) {
		const isCtrlOrCmd = event.ctrlKey || event.metaKey;

		if (event.shiftKey && event.key === 'Enter') {
			event.preventDefault();
			if (notebookStore.isGenerating) {
				handleStop();
			} else if (canGenerate) {
				handleGenerate();
			}
		}

		if (isCtrlOrCmd && event.key === 'z') {
			event.preventDefault();
			if (canUndo) {
				handleUndo();
			}
		}

		if (isCtrlOrCmd && event.key === 'y') {
			event.preventDefault();
			if (canRedo) {
				handleRedo();
			}
		}
	}
</script>

<svelte:window onbeforeunload={handleBeforeUnload} onkeydown={handleKeydown} />

<div class="flex h-full flex-col">
	<header
		class="flex items-center justify-between border-b border-border/40 bg-background/95 px-6 py-3 backdrop-blur supports-[backdrop-filter]:bg-background/60"
	>
		<div class="w-10"></div>
		<!-- Spacer for centering -->
		<h1 class="text-lg font-semibold">Notebook</h1>
		<Button variant="ghost" size="icon" onclick={() => (settingsOpen = true)}>
			<Settings class="h-5 w-5" />
		</Button>
	</header>

	<div class="flex-1 overflow-y-auto px-2 pt-2 pb-0 md:px-4 md:pt-4">
		<Textarea
			bind:ref={scrollContainer}
			onscroll={handleScroll}
			value={notebookStore.content}
			oninput={handleInput}
			class="h-full min-h-[100px] w-full resize-none rounded-xl border-none bg-muted p-4 text-base focus-visible:ring-0 md:p-6"
			placeholder="Enter your text here..."
		/>
	</div>

	<div class="bg-background p-2 md:p-4">
		<div class="flex flex-col-reverse gap-4 md:flex-row md:items-center md:justify-between">
			<div class="flex items-center gap-2">
				<Tooltip.Root>
					<Tooltip.Trigger>
						<Button variant="ghost" size="icon" disabled={!canUndo} onclick={handleUndo}>
							<Undo class="h-4 w-4" />
						</Button>
					</Tooltip.Trigger>
					<Tooltip.Content>
						<p>Undo last generation</p>
						<KeyboardShortcutInfo keys={['ctrl', 'z']} class="w-full justify-center opacity-100" />
					</Tooltip.Content>
				</Tooltip.Root>

				<Tooltip.Root>
					<Tooltip.Trigger>
						<Button variant="ghost" size="icon" disabled={!canRedo} onclick={handleRedo}>
							<Redo class="h-4 w-4" />
						</Button>
					</Tooltip.Trigger>
					<Tooltip.Content>
						<p>Redo last generation</p>
						<KeyboardShortcutInfo keys={['ctrl', 'y']} class="w-full justify-center opacity-100" />
					</Tooltip.Content>
				</Tooltip.Root>

				{#snippet generateButton()}
					<Button
						disabled={isDisabled}
						onclick={notebookStore.isGenerating ? handleStop : handleGenerate}
						size="sm"
						variant={notebookStore.isGenerating ? 'destructive' : 'default'}
						class="min-w-[120px] gap-2"
					>
						{#if notebookStore.isGenerating}
							<Square class="h-4 w-4 fill-current" />
							Stop
						{:else}
							<Play class="h-4 w-4 fill-current" />
							Generate
						{/if}
					</Button>
				{/snippet}

				<Tooltip.Root>
					<Tooltip.Trigger>
						{@render generateButton()}
					</Tooltip.Trigger>

					<Tooltip.Content>
						{#if generateTooltip}
							<p>{generateTooltip}</p>
						{:else}
							<div class="flex items-center justify-center py-1">
								<KeyboardShortcutInfo keys={['shift', 'enter']} class="opacity-100" />
							</div>
						{/if}
					</Tooltip.Content>
				</Tooltip.Root>

				<ModelsSelector
					forceForegroundText={true}
					useGlobalSelection={true}
					disabled={notebookStore.isGenerating}
				/>
			</div>

			{#if showMessageStats}
				<div class="flex w-full flex-col items-end justify-center gap-0.5 md:w-auto">
					{#if notebookStore.totalTokens > 0}
						<Tooltip.Root>
							<Tooltip.Trigger>
								<div class="flex items-center gap-1.5 pr-3.5 text-xs text-muted-foreground">
									<RulerDimensionLine class="h-3.5 w-3.5" />
									<span>{notebookStore.totalTokens} tokens</span>
								</div>
							</Tooltip.Trigger>
							<Tooltip.Content>
								<p>Total tokens</p>
							</Tooltip.Content>
						</Tooltip.Root>
					{/if}

					{#if notebookStore.promptTokens > 0 || notebookStore.predictedTokens > 0}
						<ChatMessageStatistics
							promptTokens={notebookStore.promptTokens}
							promptMs={notebookStore.promptMs}
							predictedTokens={notebookStore.predictedTokens}
							predictedMs={notebookStore.predictedMs}
							isLive={notebookStore.isGenerating}
							isProcessingPrompt={notebookStore.isGenerating && notebookStore.predictedTokens === 0}
						/>
					{/if}
				</div>
			{/if}
		</div>
	</div>

	<DialogChatSettings open={settingsOpen} onOpenChange={(open) => (settingsOpen = open)} />

	<DialogChatError
		message={errorDialog?.message ?? ''}
		contextInfo={errorDialog?.contextInfo}
		onOpenChange={handleErrorDialogOpenChange}
		open={Boolean(errorDialog)}
		type={errorDialog?.type ?? 'server'}
	/>
</div>
