<script lang="ts">
	import { page } from '$app/stores';
	import { connectionState, nodeStore, edgeStore, nodeVisibility } from '$lib/realtime';
	import type { Node, Edge } from '$lib/realtime';
	import { setSlot } from '$lib/api';

	const session = $derived($page.data.session as Record<string, unknown> | undefined);
	const nodeId = $derived(session?.nodeId as string | undefined);
	const accessToken = $derived(session?.accessToken as string | undefined);
	const connected = $derived(connectionState.current === 'connected');
	const collectionId = $derived($page.params.id ?? '');

	const collectionNode = $derived(connected ? nodeStore.get(collectionId) : undefined);

	/** Parse collection metadata */
	const parsedMetadata = $derived.by((): Record<string, unknown> => {
		if (!collectionNode) return {};
		try {
			return JSON.parse(collectionNode.metadata ?? '{}') as Record<string, unknown>;
		} catch { return {}; }
	});

	/** Publishing config from traits */
	const pubConfig = $derived.by((): Record<string, unknown> => {
		const meta = parsedMetadata;
		if (meta.traits && typeof meta.traits === 'object') {
			const traits = meta.traits as Record<string, unknown>;
			if (traits.publishing && typeof traits.publishing === 'object') {
				return traits.publishing as Record<string, unknown>;
			}
		}
		return {};
	});

	const slug = $derived((pubConfig.slug as string) ?? '');
	const theme = $derived((pubConfig.theme as string) ?? 'default');
	const featuredMax = $derived((pubConfig.featured_max as number) ?? 4);

	// =========================================================================
	// Article data: nodes with belongs_to edge to this collection
	// =========================================================================

	interface ArticleItem {
		node: Node;
		edge: Edge;
		slot: string | null;
		slotOrder: number | null;
		pinned: boolean;
		publishAt: string | null;
	}

	function parseEdgeMeta(edge: Edge): { slot: string | null; slotOrder: number | null; pinned: boolean; publishAt: string | null } {
		try {
			const meta = JSON.parse(edge.metadata ?? '{}');
			return {
				slot: meta.slot ?? null,
				slotOrder: typeof meta.slot_order === 'number' ? meta.slot_order : null,
				pinned: meta.pinned === true,
				publishAt: meta.publish_at ?? null,
			};
		} catch {
			return { slot: null, slotOrder: null, pinned: false, publishAt: null };
		}
	}

	const articles = $derived.by((): ArticleItem[] => {
		if (!connected || !collectionId) return [];
		const result: ArticleItem[] = [];

		for (const edge of edgeStore.byTarget(collectionId)) {
			if (edge.edgeType !== 'belongs_to') continue;
			const node = nodeStore.get(edge.sourceId);
			if (!node || node.nodeKind === 'collection') continue;
			if (nodeVisibility(node, nodeId) === 'hidden') continue;

			const { slot, slotOrder, pinned, publishAt } = parseEdgeMeta(edge);
			result.push({ node, edge, slot, slotOrder, pinned, publishAt });
		}

		return result;
	});

	/** Hero article (max 1) */
	const heroArticle = $derived(articles.find(a => a.slot === 'hero') ?? null);

	/** Featured articles, sorted by slot_order */
	const featuredArticles = $derived.by(() => {
		return articles
			.filter(a => a.slot === 'featured')
			.sort((a, b) => (a.slotOrder ?? 999) - (b.slotOrder ?? 999));
	});

	/** Stream articles (no slot), sorted by publish_at or created_at desc */
	const streamArticles = $derived.by(() => {
		return articles
			.filter(a => !a.slot)
			.sort((a, b) => {
				const dateA = a.publishAt ?? a.node.createdAt?.toString() ?? '';
				const dateB = b.publishAt ?? b.node.createdAt?.toString() ?? '';
				return dateB.localeCompare(dateA);
			});
	});

	// =========================================================================
	// Drag and drop
	// =========================================================================

	let draggedArticle = $state<ArticleItem | null>(null);
	let dragOverZone = $state<string | null>(null);
	let isUpdating = $state(false);
	let lastError = $state<string | null>(null);

	function handleDragStart(e: DragEvent, article: ArticleItem) {
		draggedArticle = article;
		if (e.dataTransfer) {
			e.dataTransfer.effectAllowed = 'move';
			e.dataTransfer.setData('text/plain', article.edge.id);
		}
	}

	function handleDragOver(e: DragEvent, zone: string) {
		e.preventDefault();
		if (e.dataTransfer) e.dataTransfer.dropEffect = 'move';
		dragOverZone = zone;
	}

	function handleDragLeave() {
		dragOverZone = null;
	}

	async function handleDrop(e: DragEvent, targetSlot: string | null) {
		e.preventDefault();
		dragOverZone = null;

		if (!draggedArticle || !accessToken || isUpdating) return;

		// No-op if same slot
		const currentSlot = draggedArticle.slot ?? '';
		const target = targetSlot ?? '';
		if (currentSlot === target) {
			draggedArticle = null;
			return;
		}

		const article = draggedArticle;
		draggedArticle = null;
		lastError = null;
		isUpdating = true;

		try {
			const slotOrder = targetSlot === 'featured' ? featuredArticles.length : undefined;
			await setSlot(accessToken, {
				edge_id: article.edge.id,
				slot: targetSlot,
				slot_order: slotOrder,
			});
		} catch (err) {
			console.error('Feil ved slotendring:', err);
			lastError = err instanceof Error ? err.message : 'Ukjent feil';
		} finally {
			isUpdating = false;
		}
	}

	function handleDragEnd() {
		draggedArticle = null;
		dragOverZone = null;
	}

	// =========================================================================
	// Actions: pin, move to slot
	// =========================================================================

	async function togglePin(article: ArticleItem) {
		if (!accessToken || isUpdating) return;
		isUpdating = true;
		lastError = null;
		try {
			await setSlot(accessToken, {
				edge_id: article.edge.id,
				slot: article.slot,
				slot_order: article.slotOrder ?? undefined,
				pinned: !article.pinned,
			});
		} catch (err) {
			console.error('Feil ved pin-endring:', err);
			lastError = err instanceof Error ? err.message : 'Ukjent feil';
		} finally {
			isUpdating = false;
		}
	}

	async function moveToSlot(article: ArticleItem, targetSlot: string | null) {
		if (!accessToken || isUpdating) return;
		isUpdating = true;
		lastError = null;
		try {
			const slotOrder = targetSlot === 'featured' ? featuredArticles.length : undefined;
			await setSlot(accessToken, {
				edge_id: article.edge.id,
				slot: targetSlot,
				slot_order: slotOrder,
			});
		} catch (err) {
			console.error('Feil ved slotflytting:', err);
			lastError = err instanceof Error ? err.message : 'Ukjent feil';
		} finally {
			isUpdating = false;
		}
	}

	// =========================================================================
	// Preview
	// =========================================================================

	let showPreview = $state(false);
	const previewUrl = $derived(slug ? `/api/pub/${slug}` : '');

	function formatDate(dateStr: string | null | undefined): string {
		if (!dateStr) return '';
		try {
			const d = new Date(dateStr);
			return d.toLocaleDateString('nb-NO', { day: 'numeric', month: 'short', year: 'numeric' });
		} catch {
			return '';
		}
	}

	function articleTitle(node: Node): string {
		return node.title || 'Uten tittel';
	}

	function articleExcerpt(node: Node): string {
		const content = node.content ?? '';
		if (content.length <= 120) return content;
		return content.slice(0, 120) + '…';
	}
</script>

<div class="min-h-screen bg-gray-50">
	<!-- Header -->
	<header class="border-b border-gray-200 bg-white">
		<div class="mx-auto flex max-w-5xl items-center justify-between px-4 py-3">
			<div class="flex items-center gap-3">
				<a href="/collection/{collectionId}" class="text-sm text-gray-400 hover:text-gray-600">&larr; Samling</a>
				<h1 class="text-lg font-semibold text-gray-900">
					Forside-admin
				</h1>
				{#if collectionNode}
					<span class="text-sm text-gray-500">{collectionNode.title}</span>
				{/if}
			</div>
			<div class="flex items-center gap-3">
				{#if connected}
					<span class="text-xs text-green-600">Tilkoblet</span>
				{:else}
					<span class="text-xs text-gray-400">{connectionState.current}</span>
				{/if}
				<span class="text-xs text-gray-400">{articles.length} artikler</span>
				<span class="text-xs text-gray-400">Tema: {theme}</span>
				{#if previewUrl}
					<button
						onclick={() => { showPreview = !showPreview; }}
						class="rounded-lg px-2 py-1 text-xs font-medium transition-colors {showPreview ? 'bg-emerald-600 text-white' : 'bg-gray-100 text-gray-600 hover:bg-gray-200'}"
					>
						{showPreview ? 'Skjul forhåndsvisning' : 'Forhåndsvisning'}
					</button>
				{/if}
			</div>
		</div>
	</header>

	<main class="mx-auto max-w-5xl px-4 py-6">
		{#if lastError}
			<div class="mb-4 rounded-lg border border-red-200 bg-red-50 p-3 text-sm text-red-700">
				{lastError}
				<button onclick={() => { lastError = null; }} class="ml-2 underline">Lukk</button>
			</div>
		{/if}

		{#if isUpdating}
			<div class="mb-4 rounded-lg border border-blue-200 bg-blue-50 p-2 text-sm text-blue-700">
				Oppdaterer…
			</div>
		{/if}

		{#if !connected}
			<p class="text-sm text-gray-400">Venter på tilkobling…</p>
		{:else if !collectionNode}
			<div class="rounded-lg border border-yellow-200 bg-yellow-50 p-4 text-sm text-yellow-800">
				Samling ikke funnet.
				<a href="/" class="text-blue-600 hover:underline">Tilbake</a>
			</div>
		{:else if !slug}
			<div class="rounded-lg border border-yellow-200 bg-yellow-50 p-4 text-sm text-yellow-800">
				Denne samlingen har ikke publishing-trait med slug konfigurert.
			</div>
		{:else}
			<div class="space-y-6">
				<!-- HERO ZONE -->
				<section>
					<h2 class="mb-2 text-sm font-semibold text-gray-700">Hero (maks 1)</h2>
					<div
						class="min-h-[120px] rounded-lg border-2 border-dashed transition-colors {dragOverZone === 'hero' ? 'border-amber-400 bg-amber-50' : 'border-gray-300 bg-white'}"
						ondragover={(e: DragEvent) => handleDragOver(e, 'hero')}
						ondragleave={handleDragLeave}
						ondrop={(e: DragEvent) => handleDrop(e, 'hero')}
						role="region"
						aria-label="Hero-sone"
					>
						{#if heroArticle}
							<div
								class="group relative m-2 cursor-grab rounded-lg border border-amber-200 bg-amber-50 p-4 shadow-sm transition-shadow hover:shadow-md active:cursor-grabbing {draggedArticle?.edge.id === heroArticle.edge.id ? 'opacity-50' : ''}"
								draggable="true"
								ondragstart={(e: DragEvent) => handleDragStart(e, heroArticle)}
								ondragend={handleDragEnd}
							>
								<div class="flex items-start justify-between gap-3">
									<div class="min-w-0 flex-1">
										<h3 class="text-base font-semibold text-gray-900">{articleTitle(heroArticle.node)}</h3>
										{#if articleExcerpt(heroArticle.node)}
											<p class="mt-1 text-sm text-gray-600">{articleExcerpt(heroArticle.node)}</p>
										{/if}
										{#if heroArticle.publishAt}
											<p class="mt-1 text-xs text-gray-400">{formatDate(heroArticle.publishAt)}</p>
										{/if}
									</div>
									<div class="flex shrink-0 items-center gap-1">
										<button
											onclick={() => togglePin(heroArticle)}
											class="rounded p-1 text-sm transition-colors {heroArticle.pinned ? 'bg-amber-200 text-amber-800' : 'text-gray-400 hover:bg-gray-100 hover:text-gray-600'}"
											title={heroArticle.pinned ? 'Fjern pin' : 'Pin (forhindrer automatisk fjerning)'}
										>
											{heroArticle.pinned ? '&#128204;' : '&#128392;'}
										</button>
										<button
											onclick={() => moveToSlot(heroArticle, null)}
											class="rounded p-1 text-xs text-gray-400 hover:bg-gray-100 hover:text-gray-600"
											title="Flytt til strøm"
										>
											&#8595;
										</button>
									</div>
								</div>
							</div>
						{:else}
							<div class="flex items-center justify-center p-8 text-sm text-gray-400">
								Dra en artikkel hit for å sette som hero
							</div>
						{/if}
					</div>
				</section>

				<!-- FEATURED ZONE -->
				<section>
					<h2 class="mb-2 text-sm font-semibold text-gray-700">
						Fremhevet ({featuredArticles.length}/{featuredMax})
					</h2>
					<div
						class="min-h-[80px] rounded-lg border-2 border-dashed transition-colors {dragOverZone === 'featured' ? 'border-blue-400 bg-blue-50' : 'border-gray-300 bg-white'}"
						ondragover={(e: DragEvent) => handleDragOver(e, 'featured')}
						ondragleave={handleDragLeave}
						ondrop={(e: DragEvent) => handleDrop(e, 'featured')}
						role="region"
						aria-label="Fremhevet-sone"
					>
						{#if featuredArticles.length > 0}
							<div class="grid grid-cols-1 gap-2 p-2 sm:grid-cols-2 lg:grid-cols-4">
								{#each featuredArticles as article (article.edge.id)}
									<div
										class="group relative cursor-grab rounded-lg border border-blue-200 bg-blue-50 p-3 shadow-sm transition-shadow hover:shadow-md active:cursor-grabbing {draggedArticle?.edge.id === article.edge.id ? 'opacity-50' : ''}"
										draggable="true"
										ondragstart={(e: DragEvent) => handleDragStart(e, article)}
										ondragend={handleDragEnd}
									>
										<div class="flex items-start justify-between gap-2">
											<div class="min-w-0 flex-1">
												<h3 class="text-sm font-semibold text-gray-900">{articleTitle(article.node)}</h3>
												{#if article.publishAt}
													<p class="mt-1 text-xs text-gray-400">{formatDate(article.publishAt)}</p>
												{/if}
											</div>
											<div class="flex shrink-0 items-center gap-1">
												<button
													onclick={() => togglePin(article)}
													class="rounded p-1 text-xs transition-colors {article.pinned ? 'bg-blue-200 text-blue-800' : 'text-gray-400 hover:bg-gray-100 hover:text-gray-600'}"
													title={article.pinned ? 'Fjern pin' : 'Pin'}
												>
													{article.pinned ? '&#128204;' : '&#128392;'}
												</button>
												<button
													onclick={() => moveToSlot(article, 'hero')}
													class="rounded p-1 text-xs text-gray-400 hover:bg-gray-100 hover:text-gray-600"
													title="Flytt til hero"
												>
													&#8593;
												</button>
												<button
													onclick={() => moveToSlot(article, null)}
													class="rounded p-1 text-xs text-gray-400 hover:bg-gray-100 hover:text-gray-600"
													title="Flytt til strøm"
												>
													&#8595;
												</button>
											</div>
										</div>
									</div>
								{/each}
							</div>
						{:else}
							<div class="flex items-center justify-center p-6 text-sm text-gray-400">
								Dra artikler hit for fremheving
							</div>
						{/if}
					</div>
				</section>

				<!-- STREAM ZONE -->
				<section>
					<h2 class="mb-2 text-sm font-semibold text-gray-700">
						Strøm ({streamArticles.length})
					</h2>
					<div
						class="min-h-[80px] rounded-lg border-2 border-dashed transition-colors {dragOverZone === 'stream' ? 'border-gray-500 bg-gray-100' : 'border-gray-300 bg-white'}"
						ondragover={(e: DragEvent) => handleDragOver(e, 'stream')}
						ondragleave={handleDragLeave}
						ondrop={(e: DragEvent) => handleDrop(e, 'stream')}
						role="region"
						aria-label="Strøm-sone"
					>
						{#if streamArticles.length > 0}
							<div class="divide-y divide-gray-100">
								{#each streamArticles as article (article.edge.id)}
									<div
										class="group flex cursor-grab items-center gap-3 px-4 py-3 transition-colors hover:bg-gray-50 active:cursor-grabbing {draggedArticle?.edge.id === article.edge.id ? 'opacity-50' : ''}"
										draggable="true"
										ondragstart={(e: DragEvent) => handleDragStart(e, article)}
										ondragend={handleDragEnd}
									>
										<div class="min-w-0 flex-1">
											<h3 class="text-sm font-medium text-gray-900">{articleTitle(article.node)}</h3>
											{#if articleExcerpt(article.node)}
												<p class="mt-0.5 text-xs text-gray-500 line-clamp-1">{articleExcerpt(article.node)}</p>
											{/if}
										</div>
										<div class="flex shrink-0 items-center gap-2">
											{#if article.publishAt}
												<span class="text-xs text-gray-400">{formatDate(article.publishAt)}</span>
											{/if}
											<button
												onclick={() => togglePin(article)}
												class="rounded p-1 text-xs transition-colors {article.pinned ? 'bg-gray-200 text-gray-800' : 'text-gray-400 opacity-0 group-hover:opacity-100 hover:bg-gray-100 hover:text-gray-600'}"
												title={article.pinned ? 'Fjern pin' : 'Pin'}
											>
												{article.pinned ? '&#128204;' : '&#128392;'}
											</button>
											<button
												onclick={() => moveToSlot(article, 'hero')}
												class="rounded p-1 text-xs text-gray-400 opacity-0 transition-opacity group-hover:opacity-100 hover:bg-gray-100 hover:text-gray-600"
												title="Sett som hero"
											>
												&#9733;
											</button>
											<button
												onclick={() => moveToSlot(article, 'featured')}
												class="rounded p-1 text-xs text-gray-400 opacity-0 transition-opacity group-hover:opacity-100 hover:bg-gray-100 hover:text-gray-600"
												title="Fremhev"
											>
												&#8593;
											</button>
										</div>
									</div>
								{/each}
							</div>
						{:else}
							<div class="flex items-center justify-center p-6 text-sm text-gray-400">
								Ingen artikler i strømmen
							</div>
						{/if}
					</div>
				</section>
			</div>

			<!-- Preview panel -->
			{#if showPreview && previewUrl}
				<div class="mt-6">
					<div class="overflow-hidden rounded-lg border border-gray-200 bg-white shadow-sm">
						<div class="flex items-center justify-between border-b border-gray-100 px-4 py-2">
							<span class="text-xs font-medium text-gray-500">Forhåndsvisning</span>
							<a
								href={previewUrl}
								target="_blank"
								rel="noopener"
								class="text-xs text-blue-600 hover:underline"
							>
								Åpne i ny fane
							</a>
						</div>
						<iframe
							src={previewUrl}
							title="Forhåndsvisning av forside"
							class="h-[600px] w-full border-0"
							sandbox="allow-same-origin"
						></iframe>
					</div>
				</div>
			{/if}
		{/if}
	</main>
</div>

<style>
	.line-clamp-1 {
		display: -webkit-box;
		-webkit-line-clamp: 1;
		-webkit-box-orient: vertical;
		overflow: hidden;
	}
</style>
