<script lang="ts">
	import { invalidateAll } from '$app/navigation';
	import { tick } from 'svelte';
	import { Bot, Dumbbell, ImagePlus, SendHorizonal, Sparkles, X } from '@lucide/svelte';

	type ChatRole = 'user' | 'assistant';
	type MealType = 'breakfast' | 'lunch' | 'dinner' | 'snack';

	type Message = {
		role: ChatRole;
		content: string;
		image?: string;
		imageName?: string;
	};

	type Draft = {
		food_name?: string;
		meal_type?: MealType;
		entry_date?: string;
		quantity?: number;
		unit?: string;
		calories?: number;
		protein?: number;
		carbs?: number;
		fat?: number;
		sugar?: number;
		fiber?: number;
		note?: string;
	};

	type DraftBundle = Draft[];

	interface Props {
		open: boolean;
		onclose: () => void;
		entryDate?: string | null;
	}

	let { open = $bindable(), onclose, entryDate = null }: Props = $props();

	const intro = `Tell me what you ate, including multiple items or a nutrition label photo, then correct me naturally until it looks right.`;

	let messages = $state<Message[]>([
		{ role: 'assistant', content: intro }
	]);
	let draft = $state<Draft | null>(null);
	let drafts = $state<DraftBundle>([]);
	let input = $state('');
	let sending = $state(false);
	let error = $state('');
	let composerEl: HTMLInputElement | null = $state(null);
	let fileInputEl: HTMLInputElement | null = $state(null);
	let threadEl: HTMLElement | null = $state(null);
	let threadEndEl: HTMLDivElement | null = $state(null);
	let photoPreview = $state<string | null>(null);
	let photoName = $state('');
	let attachedPhoto = $state<string | null>(null);
	let attachedPhotoName = $state('');

	async function scrollThreadToBottom(behavior: ScrollBehavior = 'smooth') {
		await tick();
		requestAnimationFrame(() => {
			threadEndEl?.scrollIntoView({ block: 'end', behavior });
		});
	}

	function resetThread() {
		messages = [{ role: 'assistant', content: intro }];
		draft = null;
		drafts = [];
		input = '';
		error = '';
		photoPreview = null;
		photoName = '';
		attachedPhoto = null;
		attachedPhotoName = '';
		if (fileInputEl) fileInputEl.value = '';
	}

	function beginRevision() {
		if (!input.trim()) {
			input = 'Actually ';
		}
		requestAnimationFrame(() => composerEl?.focus());
	}

	function retryCurrentGuess() {
		void sendMessage("That's not right. Try again.", 'chat');
	}

	async function sendMessage(content: string, action: 'chat' | 'apply' = 'chat') {
		const clean = content.trim();
		if (!clean && !photoPreview && action === 'chat') return;

		error = '';
		sending = true;
		const outgoingPhoto = action === 'chat' ? photoPreview : null;
		const outgoingPhotoName = action === 'chat' ? photoName : '';

		const nextMessages =
			action === 'chat'
				? [
						...messages,
						{
							role: 'user' as const,
							content: clean,
							image: outgoingPhoto || undefined,
							imageName: outgoingPhotoName || undefined
						}
				  ]
				: messages;

		if (action === 'chat') {
			messages = nextMessages;
			input = '';
			if (outgoingPhoto) {
				attachedPhoto = outgoingPhoto;
				attachedPhotoName = outgoingPhotoName;
				photoPreview = null;
				photoName = '';
				if (fileInputEl) fileInputEl.value = '';
			}
			void scrollThreadToBottom('auto');
		}

		try {
			const response = await fetch('/assistant/fitness', {
				method: 'POST',
				headers: { 'content-type': 'application/json' },
				body: JSON.stringify({
					action,
					messages: nextMessages,
					draft,
					drafts,
					entryDate,
					imageDataUrl: action === 'chat' ? outgoingPhoto || attachedPhoto : null
				})
			});

			const data = await response.json();
			if (!response.ok) {
				throw new Error(data?.error || 'Assistant request failed');
			}

			if (data.draft) {
				draft = data.draft;
			}

			if (Array.isArray(data.drafts)) {
				drafts = data.drafts;
				if (data.drafts.length > 0) {
					draft = null;
				}
			} else if (data.draft) {
				drafts = [];
			}

			if (data.reply) {
				messages = [...nextMessages, { role: 'assistant', content: data.reply }];
			}

			if (data.applied) {
				photoPreview = null;
				photoName = '';
				attachedPhoto = null;
				attachedPhotoName = '';
				if (fileInputEl) fileInputEl.value = '';
				draft = null;
				drafts = [];
				window.dispatchEvent(
					new CustomEvent('fitnessassistantapplied', {
						detail: { entryDate: data.draft?.entry_date || data.drafts?.[0]?.entry_date || entryDate || null }
					})
				);
				await invalidateAll();
			}

			await scrollThreadToBottom();
		} catch (err) {
			error = err instanceof Error ? err.message : 'Assistant request failed';
		} finally {
			sending = false;
			requestAnimationFrame(() => composerEl?.focus());
		}
	}

	function handleSubmit() {
		void sendMessage(input, 'chat');
	}

	function openPhotoPicker() {
		fileInputEl?.click();
	}

	function loadPhoto(file: File | null) {
		if (!file) return;
		photoName = file.name;
		const reader = new FileReader();
		reader.onload = () => {
			if (typeof reader.result === 'string') {
				photoPreview = reader.result;
			}
		};
		reader.readAsDataURL(file);
	}

	function clearPhoto() {
		photoPreview = null;
		photoName = '';
		if (fileInputEl) fileInputEl.value = '';
	}

	function handleKeydown(event: KeyboardEvent) {
		if (event.key === 'Escape') {
			onclose();
			return;
		}
		if (event.key === 'Enter' && !event.shiftKey) {
			event.preventDefault();
			handleSubmit();
		}
	}

	$effect(() => {
		if (open) {
			requestAnimationFrame(() => composerEl?.focus());
		}
	});

	$effect(() => {
		if (!open) return;

		document.body.classList.add('assistant-open');

		return () => {
			document.body.classList.remove('assistant-open');
		};
	});
</script>

{#if open}
	<button class="assistant-backdrop" aria-label="Close assistant" onclick={onclose}></button>

	<div class="assistant-drawer" role="dialog" aria-label="Fitness assistant">
		<header class="assistant-head">
			<div class="assistant-title-wrap">
				<div class="assistant-mark">
					<Dumbbell size={16} strokeWidth={1.9} />
				</div>
				<div>
					<div class="assistant-kicker">Assistant</div>
					<div class="assistant-title">Fitness chat</div>
				</div>
			</div>

			<div class="assistant-head-actions">
				<button class="assistant-ghost" onclick={resetThread}>Reset</button>
				<button class="assistant-close" onclick={onclose} aria-label="Close assistant">
					<X size={17} strokeWidth={1.9} />
				</button>
			</div>
		</header>

		<div class="assistant-body">
			<section class="thread" bind:this={threadEl}>
				<div class="thread-spacer" aria-hidden="true"></div>
				{#each messages as message}
					<div class="bubble-row" class:user={message.role === 'user'}>
						{#if message.role === 'assistant'}
							<div class="bubble-icon">
								<Bot size={14} strokeWidth={1.8} />
							</div>
						{/if}
						<div class="bubble-stack" class:user={message.role === 'user'}>
							{#if message.image}
								<div class="image-bubble" class:user={message.role === 'user'}>
									<img src={message.image} alt={message.imageName || 'Sent food photo'} />
									{#if message.imageName}
										<div class="image-bubble-name">{message.imageName}</div>
									{/if}
								</div>
							{/if}
							{#if message.content}
								<div class="bubble" class:user={message.role === 'user'}>
									{message.content}
								</div>
							{/if}
						</div>
					</div>
				{/each}

				{#if drafts.length > 1}
					<div class="bubble-row draft-row">
						<div class="bubble-icon">
							<Sparkles size={14} strokeWidth={1.8} />
						</div>
						<div class="draft-card bundle-card">
							<div class="draft-card-top">
								<div>
									<div class="draft-card-kicker">Ready to add</div>
									<div class="draft-card-title">{drafts.length} items</div>
								</div>
								<div class="draft-meal">{drafts[0]?.meal_type || 'meal'}</div>
							</div>
							<div class="bundle-list">
								{#each drafts as item}
									<div class="bundle-row">
										<div class="bundle-name">{item.food_name}</div>
										<div class="bundle-calories">{Math.round(item.calories || 0)} cal</div>
									</div>
								{/each}
							</div>
							<div class="draft-inline-metrics">
								<span>{drafts.reduce((sum, item) => sum + Math.round(item.calories || 0), 0)} cal total</span>
							</div>
							<div class="draft-card-actions">
								<button class="draft-apply subtle" onclick={beginRevision} disabled={sending}>
									Edit
								</button>
								<button class="draft-apply" onclick={() => void sendMessage('', 'apply')} disabled={sending}>
									{sending ? 'Adding...' : 'Add all'}
								</button>
							</div>
						</div>
					</div>
				{:else if draft?.food_name}
					<div class="bubble-row draft-row">
						<div class="bubble-icon">
							<Sparkles size={14} strokeWidth={1.8} />
						</div>
						<div class="draft-card">
							<div class="draft-card-top">
								<div>
									<div class="draft-card-kicker">Ready to add</div>
									<div class="draft-card-title">{draft.food_name}</div>
								</div>
								{#if draft.meal_type}
									<div class="draft-meal">{draft.meal_type}</div>
								{/if}
							</div>
							<div class="draft-inline-metrics">
								<span>{Math.round(draft.calories || 0)} cal</span>
								<span>{Math.round(draft.protein || 0)}p</span>
								<span>{Math.round(draft.carbs || 0)}c</span>
								<span>{Math.round(draft.fat || 0)}f</span>
								<span>{Math.round(draft.sugar || 0)} sugar</span>
								<span>{Math.round(draft.fiber || 0)} fiber</span>
							</div>
							<div class="draft-card-actions">
								<button class="draft-apply subtle" onclick={beginRevision} disabled={sending}>
									Edit
								</button>
								<button class="draft-apply subtle" onclick={retryCurrentGuess} disabled={sending}>
									Try again
								</button>
								<button class="draft-apply" onclick={() => void sendMessage('', 'apply')} disabled={sending}>
									{sending ? 'Adding...' : 'Add it'}
								</button>
							</div>
						</div>
					</div>
				{/if}

				<div class="thread-end" bind:this={threadEndEl} aria-hidden="true"></div>

			</section>
		</div>

		{#if error}
			<div class="assistant-error">{error}</div>
		{/if}

		<footer class="assistant-compose">
			<div class="compose-shell">
				<input
					class="sr-only-file"
					type="file"
					accept="image/*"
					bind:this={fileInputEl}
					onchange={(event) => loadPhoto((event.currentTarget as HTMLInputElement).files?.[0] || null)}
				/>
				<div class="compose-tools">
					<button class="compose-tool" type="button" onclick={openPhotoPicker}>
						<ImagePlus size={14} strokeWidth={1.9} />
						<span>Photo</span>
					</button>
				</div>
				{#if photoPreview}
					<div class="photo-staged">
						<img src={photoPreview} alt="Selected food" />
						<div class="photo-staged-copy">
							<div class="photo-staged-title">{photoName || 'Food photo'}</div>
							<div class="photo-staged-note">I’ll draft the entry from this photo.</div>
						</div>
						<button class="photo-staged-clear" type="button" onclick={clearPhoto}>Remove</button>
					</div>
				{:else if attachedPhoto}
					<div class="photo-staged attached">
						<img src={attachedPhoto} alt="Attached food context" />
						<div class="photo-staged-copy">
							<div class="photo-staged-title">{attachedPhotoName || 'Attached photo'}</div>
							<div class="photo-staged-note">Still attached for follow-up corrections.</div>
						</div>
						<button class="photo-staged-clear" type="button" onclick={() => { attachedPhoto = null; attachedPhotoName = ''; }}>
							Clear
						</button>
					</div>
				{/if}
				<input
					class="compose-input"
					bind:value={input}
					bind:this={composerEl}
					placeholder="Add 2 boiled eggs for breakfast..."
					onkeydown={handleKeydown}
					type="text"
				/>
				<button
					class="compose-send"
					onclick={handleSubmit}
					disabled={sending || (!input.trim() && !photoPreview)}
					aria-label="Send message"
				>
					<SendHorizonal size={16} strokeWidth={2} />
				</button>
			</div>
		</footer>
	</div>
{/if}

<style>
	.assistant-backdrop {
		position: fixed;
		inset: 0;
		border: none;
		background: linear-gradient(180deg, rgba(239, 231, 220, 0.52), rgba(225, 215, 202, 0.34));
		backdrop-filter: blur(6px) saturate(1.02);
		z-index: 88;
		overscroll-behavior: none;
		touch-action: none;
	}

	:global(body.assistant-open) {
		overflow: hidden;
		overscroll-behavior: none;
	}

	.assistant-drawer {
		position: fixed;
		top: 18px;
		right: 18px;
		bottom: 18px;
		width: min(430px, calc(100vw - 24px));
		border-radius: 30px;
		border: 1px solid rgba(36, 26, 18, 0.1);
		background:
			radial-gradient(circle at top right, rgba(54, 108, 86, 0.08), transparent 24%),
			linear-gradient(180deg, rgba(252, 248, 242, 0.96), rgba(244, 236, 227, 0.94));
		box-shadow: 0 28px 80px rgba(24, 16, 11, 0.2);
		display: grid;
		grid-template-rows: auto minmax(0, 1fr) auto auto;
		overflow: hidden;
		z-index: 89;
	}

	.assistant-head {
		display: flex;
		align-items: center;
		justify-content: space-between;
		gap: 12px;
		padding: 18px 18px 14px;
		border-bottom: 1px solid rgba(36, 26, 18, 0.08);
	}

	.assistant-title-wrap {
		display: flex;
		align-items: center;
		gap: 12px;
	}

	.assistant-mark {
		width: 38px;
		height: 38px;
		border-radius: 14px;
		display: grid;
		place-items: center;
		background: linear-gradient(135deg, #215043, #3f7862);
		color: #fff9f2;
	}

	.assistant-kicker {
		font-size: 0.68rem;
		font-weight: 700;
		letter-spacing: 0.12em;
		text-transform: uppercase;
		color: #857362;
	}

	.assistant-title {
		font-size: 1.05rem;
		font-weight: 700;
		letter-spacing: -0.03em;
		color: #1f1710;
	}

	.assistant-head-actions {
		display: flex;
		align-items: center;
		gap: 8px;
	}

	.assistant-ghost,
	.assistant-close {
		border: 1px solid rgba(36, 26, 18, 0.08);
		background: rgba(255, 255, 255, 0.62);
		color: #65584c;
	}

	.assistant-ghost {
		height: 34px;
		padding: 0 12px;
		border-radius: 999px;
		font-size: 0.84rem;
		font-weight: 600;
		font-family: inherit;
		cursor: pointer;
	}

	.assistant-close {
		width: 34px;
		height: 34px;
		border-radius: 999px;
		display: grid;
		place-items: center;
		cursor: pointer;
	}

	.assistant-body {
		min-height: 0;
		overflow: hidden;
	}

	.thread {
		min-height: 0;
		overflow: auto;
		overscroll-behavior: contain;
		-webkit-overflow-scrolling: touch;
		touch-action: pan-y;
		padding: 14px 18px 10px;
		display: flex;
		flex-direction: column;
		gap: 10px;
	}

	.bundle-card {
		gap: 12px;
	}

	.bundle-list {
		display: flex;
		flex-direction: column;
		gap: 8px;
	}

	.bundle-row {
		display: flex;
		align-items: center;
		justify-content: space-between;
		gap: 12px;
		padding: 10px 12px;
		border-radius: 14px;
		background: rgba(255, 255, 255, 0.56);
		border: 1px solid rgba(36, 26, 18, 0.08);
	}

	.bundle-name {
		font-size: 0.92rem;
		font-weight: 600;
		color: #201811;
	}

	.bundle-calories {
		font-size: 0.82rem;
		font-weight: 700;
		color: #6d5b4f;
		white-space: nowrap;
	}

	.thread-spacer {
		display: none;
	}

	.thread-end {
		width: 100%;
		height: 1px;
	}

	.bubble-row {
		display: flex;
		align-items: flex-end;
		gap: 8px;
	}

	.bubble-row.user {
		justify-content: flex-end;
	}

	.bubble-stack {
		display: grid;
		gap: 8px;
		max-width: 84%;
	}

	.bubble-stack.user {
		justify-items: end;
	}

	.bubble-icon {
		width: 28px;
		height: 28px;
		border-radius: 999px;
		display: grid;
		place-items: center;
		background: rgba(47, 106, 82, 0.08);
		color: #2f6a52;
		flex-shrink: 0;
	}

	.bubble {
		padding: 11px 13px;
		border-radius: 18px 18px 18px 8px;
		background: rgba(255, 255, 255, 0.78);
		border: 1px solid rgba(36, 26, 18, 0.08);
		color: #241c14;
		font-size: 0.95rem;
		line-height: 1.45;
	}

	.bubble.user {
		border-radius: 18px 18px 8px 18px;
		background: linear-gradient(180deg, #2d6450, #245242);
		border-color: transparent;
		color: #f8f4ee;
	}

	.image-bubble {
		width: min(240px, 100%);
		padding: 8px;
		border-radius: 20px 20px 20px 10px;
		background: rgba(255, 255, 255, 0.82);
		border: 1px solid rgba(36, 26, 18, 0.08);
		display: grid;
		gap: 8px;
	}

	.image-bubble.user {
		border-radius: 20px 20px 10px 20px;
		background: linear-gradient(180deg, rgba(53, 102, 83, 0.16), rgba(39, 83, 66, 0.12)), rgba(255, 251, 246, 0.96);
	}

	.image-bubble img {
		width: 100%;
		aspect-ratio: 1 / 1;
		object-fit: cover;
		border-radius: 14px;
		display: block;
	}

	.image-bubble-name {
		padding: 0 4px 2px;
		font-size: 0.74rem;
		font-weight: 700;
		color: #6b5c4f;
		line-height: 1.25;
		word-break: break-word;
	}

	.draft-meal {
		padding: 6px 10px;
		border-radius: 999px;
		background: rgba(47, 106, 82, 0.1);
		color: #2f6a52;
		font-size: 0.76rem;
		font-weight: 700;
		text-transform: capitalize;
	}

	.draft-apply {
		height: 40px;
		padding: 0 14px;
		border: none;
		border-radius: 14px;
		background: linear-gradient(180deg, #366d57, #245141);
		color: #f8f4ee;
		font-size: 0.88rem;
		font-weight: 700;
		font-family: inherit;
		cursor: pointer;
	}

	.draft-apply.subtle {
		background: rgba(255, 255, 255, 0.82);
		color: #2c2016;
		border: 1px solid rgba(36, 26, 18, 0.08);
	}

	.draft-apply:disabled {
		opacity: 0.45;
		cursor: default;
	}

	.draft-card {
		max-width: 100%;
		padding: 14px;
		border-radius: 20px 20px 20px 10px;
		border: 1px solid rgba(36, 26, 18, 0.08);
		background:
			radial-gradient(circle at top right, rgba(199, 137, 50, 0.08), transparent 30%),
			rgba(255, 251, 246, 0.88);
		display: grid;
		gap: 12px;
	}

	.draft-card-top {
		display: flex;
		align-items: start;
		justify-content: space-between;
		gap: 12px;
	}

	.draft-card-kicker {
		font-size: 0.68rem;
		font-weight: 700;
		letter-spacing: 0.12em;
		text-transform: uppercase;
		color: #8c7a68;
	}

	.draft-card-title {
		margin-top: 4px;
		font-size: 1rem;
		font-weight: 700;
		letter-spacing: -0.03em;
		color: #201811;
	}

	.draft-inline-metrics {
		display: flex;
		flex-wrap: wrap;
		gap: 8px;
	}

	.draft-inline-metrics span {
		padding: 6px 9px;
		border-radius: 999px;
		background: rgba(255, 255, 255, 0.8);
		border: 1px solid rgba(36, 26, 18, 0.06);
		font-size: 0.75rem;
		font-weight: 600;
		color: #5e5246;
	}

	.draft-card-actions {
		display: flex;
		gap: 8px;
		justify-content: flex-end;
	}

	.draft-row {
		align-items: stretch;
	}

	.assistant-error {
		padding: 0 18px 10px;
		color: #9b4337;
		font-size: 0.86rem;
	}

	.assistant-compose {
		padding: 14px 18px 18px;
		border-top: 1px solid rgba(36, 26, 18, 0.08);
	}

	.compose-shell {
		padding: 10px;
		border-radius: 24px;
		border: 1px solid rgba(36, 26, 18, 0.08);
		background: rgba(255, 255, 255, 0.7);
		display: grid;
		gap: 10px;
	}

	.sr-only-file {
		position: absolute;
		width: 1px;
		height: 1px;
		opacity: 0;
		pointer-events: none;
	}

	.compose-tools {
		display: flex;
		gap: 8px;
	}

	.compose-tool {
		height: 30px;
		padding: 0 10px;
		border-radius: 999px;
		border: 1px solid rgba(36, 26, 18, 0.08);
		background: rgba(249, 245, 239, 0.78);
		color: #6f6254;
		display: inline-flex;
		align-items: center;
		gap: 6px;
		font-size: 0.76rem;
		font-weight: 600;
		font-family: inherit;
	}

	.compose-input {
		width: 100%;
		border: none;
		background: transparent;
		outline: none;
		color: #201810;
		font-size: 1rem;
		line-height: 1.5;
		font-family: inherit;
		height: 42px;
		padding: 0 2px;
	}

	.compose-input::placeholder {
		color: #8f8071;
	}

	.compose-send {
		justify-self: end;
		width: 42px;
		height: 42px;
		border-radius: 14px;
		border: none;
		background: #2e6851;
		color: #fff7f0;
		display: grid;
		place-items: center;
		cursor: pointer;
	}

	.compose-send:disabled {
		opacity: 0.45;
		cursor: default;
	}

	.photo-staged {
		display: grid;
		grid-template-columns: 54px minmax(0, 1fr) auto;
		align-items: center;
		gap: 10px;
		padding: 10px;
		border-radius: 18px;
		background: rgba(255, 251, 246, 0.86);
		border: 1px solid rgba(36, 26, 18, 0.08);
	}

	.photo-staged.attached {
		background: rgba(246, 240, 231, 0.8);
	}

	.photo-staged img {
		width: 54px;
		height: 54px;
		object-fit: cover;
		border-radius: 12px;
		display: block;
	}

	.photo-staged-title {
		font-size: 0.84rem;
		font-weight: 700;
		color: #221912;
	}

	.photo-staged-note {
		font-size: 0.74rem;
		color: #7a6858;
	}

	.photo-staged-clear {
		border: none;
		background: none;
		color: #7d4d3e;
		font-size: 0.76rem;
		font-weight: 700;
		font-family: inherit;
		cursor: pointer;
	}

	@media (max-width: 768px) {
		.assistant-drawer {
			top: auto;
			right: 0;
			left: 0;
			bottom: 0;
			width: auto;
			height: min(86dvh, 760px);
			max-height: min(86dvh, 760px);
			border-radius: 28px 28px 0 0;
			border-left: none;
			border-right: none;
			border-bottom: none;
			box-shadow: 0 -18px 42px rgba(24, 16, 11, 0.18);
		}

		.assistant-backdrop {
			backdrop-filter: blur(6px);
		}

		.assistant-head {
			padding-top: 14px;
		}

		.assistant-head::before {
			content: '';
			position: absolute;
			top: 8px;
			left: 50%;
			transform: translateX(-50%);
			width: 42px;
			height: 4px;
			border-radius: 999px;
			background: rgba(44, 31, 19, 0.16);
		}

		.bubble {
			max-width: 90%;
		}

		.bubble-stack {
			max-width: 90%;
		}

		.image-bubble {
			width: min(220px, 100%);
		}

		.thread {
			padding-top: 8px;
			padding-bottom: 16px;
			align-content: start;
		}

		.thread-spacer {
			display: block;
			height: clamp(120px, 22vh, 220px);
		}

		.assistant-compose {
			padding-bottom: calc(14px + env(safe-area-inset-bottom));
			background: linear-gradient(180deg, rgba(247, 240, 231, 0.88), rgba(243, 235, 225, 0.96));
		}

		.compose-shell {
			border-radius: 22px;
			gap: 8px;
		}

		.compose-input {
			font-size: 16px;
			height: 40px;
		}

		.draft-card-actions {
			flex-direction: column;
		}

		.draft-apply,
		.draft-apply.subtle {
			width: 100%;
		}
	}
</style>
