<script lang="ts">
	import { ProgressRadial, getToastStore, RadioGroup, RadioItem } from '@skeletonlabs/skeleton';

	let inputText = '';
	let imageUrl = '';

	const models = [
		{
			name: "Playground v2.5",
			value: "playground-v2.5",
		},
		{
			name: "Dall-E 3",
			value: "dall-e-3",
		},
		{
			name: "Stable Diffusion 3",
			value: "stable-diffusion-3",
		},
		{
			name: "DreamShaper XL Turbo",
			value: "dreamshaper-xl-turbo"
		},
		{
			name: "RealVis XL 4",
			value: "realvisxl-v4",
		},
		{
			name: "CyberRealistic",
			value: "cyberrealistic-v3.3",
		}
	]

	let currentModel = models[0].value;

	let generateFutureAbortController = new AbortController();
	let generateFutureAbortSignal = generateFutureAbortController.signal;

	function resetAbortController() {
		generateFutureAbortController = new AbortController();
		generateFutureAbortSignal = generateFutureAbortController.signal;
	}

	function promiseState(p) {
		const t = {};
		return Promise.race([p, t]).then(
			(v) => (v === t ? 'pending' : 'fulfilled'),
			() => 'rejected'
		);
	}

	let generateFuture: Promise<string> | null = null;
	let isGenerating = false;

	const toastStore = getToastStore();

	async function generateImage(signal?: AbortSignal): Promise<string> {
		if (isGenerating) {
			throw Error('Bilderzeugung bereits im Gange');
		}

		isGenerating = true;
		try {
			const response = await fetch(
				`/api/forward/gen_image?prompt=${encodeURIComponent(inputText)}&model=${encodeURIComponent(currentModel)}`,

				{ signal }
			);
			if (response.ok) {
				const data = await response.json();
				return data.url;
			} else {
				throw new Error(
					`Fehler beim Generieren des Bildes<br\><b>Grund:</b> ${await response.text()}`
				);
			}
		} catch (error) {
			if (error.name === 'AbortError') {
				console.log('Anfrage wurde abgebrochen');
				throw Error('Bilderzeugung abgebrochen');
			} else {
				console.error('Fehler:', error);
				toastStore.trigger({
					message: `Generierung fehlgeschlagen: ${(error as Error).message}`,
					background: 'variant-filled-error'
				});

				// rethrow
				throw error;
			}
		} finally {
			isGenerating = false;
		}
	}

	async function handleSubmit() {
		try {
			if (generateFuture !== null && (await promiseState(generateFuture)) === 'pending') {
				console.log('Wird bereits generiert');
				toastStore.trigger({
					message: `Generierung bereits im Gange`,
					background: 'variant-filled-warning',
					action: {
						label: 'Abbrechen',
						response: () => {
							generateFutureAbortController.abort();
							resetAbortController();
							toastStore.trigger({
								message: 'Bilderzeugung erfolgreich abgebrochen',
								background: 'variant-filled-success'
							});
						}
					}
				});
			} else {
				generateFuture = generateImage(generateFutureAbortSignal);
			}
		} catch (error) {
			toastStore.trigger({
				message: `Generierung fehlgeschlagen: ${(error as Error).message}`,
				background: 'variant-filled-error'
			});
		} finally {
		}
	}

	let improvePromptRunning: boolean = false;

	async function improvePrompt() {
		if (improvePromptRunning) {
			toastStore.trigger({
				message: 'Prompt-Verbesserung läuft. Bitte warten...',
				background: 'variant-filled-warning'
			});
		}
		improvePromptRunning = true;
		try {
			const response = await fetch(
				`/api/forward/gen_prompt?prompt=${encodeURIComponent(inputText)}`
			);
			if (response.ok) {
				const data = await response.json();
				inputText = data.text;
			} else {
				throw new Error(
					`Fehler beim Verbessern des Prompts<br\><b>Grund:</b> ${await response.text()}`
				);
			}
		} catch (error) {
			toastStore.trigger({
				message: `Prompt-Verbesserung fehlgeschlagen: ${(error as Error).message}`,
				background: 'variant-filled-error'
			});
		} finally {
			improvePromptRunning = false;
		}
	}
</script>

<div class="container mx-auto p-8">
	<h1 class="mb-4 text-2xl font-bold">Bildgenerator</h1>

	<center>
		<div
			class="rounded-container-token flex aspect-square w-full items-center justify-center bg-gray-800 md:m-16 md:w-[70%]"
		>
			{#await generateFuture}
				<ProgressRadial />
			{:then url}
				{#if url !== null}
					<div class="h-full w-full">
						<img src={url} alt="Generiertes Bild" class="h-full w-full object-cover" />
					</div>
				{:else}
					<p>Generiere ein Bild!</p>
				{/if}
			{:catch error}
				<div class="card variant-glass-error p-4">
					<p>{@html error.message}</p>
				</div>
			{/await}
		</div>
		<div class="mb-4 px-4">
			<p>Bildgenerations-Model</p>
			<RadioGroup active="variant-filled-primary" hover="hover:variant-soft-primary">
				{#each models as model}
					<RadioItem bind:group={currentModel} value={model.value}>
						{model.name}
					</RadioItem>
				{/each}
			</RadioGroup>
		</div>
	</center>

	<form on:submit|preventDefault={handleSubmit} class="flex flex-col md:flex-row space-x-4">
		<label class="label flex-1">
			<span>Prompt eingeben:</span>
			<textarea class="textarea p-2" rows="4" bind:value={inputText} disabled={improvePromptRunning}
			></textarea>
		</label>
		<div class="flex flex-col items-center justify-center space-y-4 pt-2">
			<button class="btn variant-filled-primary mt-4" type="submit">
				{#if isGenerating}
					Generiere...
				{:else}
					Bild generieren
				{/if}
			</button>
			<button class="btn variant-filled-secondary" on:click|preventDefault={improvePrompt}>
				{#if improvePromptRunning}
					Verbessere...
				{:else}
					Prompt verbessern
				{/if}
			</button>
		</div>
	</form>
</div>
