<script lang="ts">
	import { page } from '$app/stores';
	import { connectionState, nodeStore, edgeStore, nodeVisibility } from '$lib/spacetime';
	import type { Node, Edge } from '$lib/spacetime';
	import { createNode, createEdge, updateEdge } 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');

	// =========================================================================
	// Calendar state
	// =========================================================================

	const today = new Date();
	let viewYear = $state(today.getFullYear());
	let viewMonth = $state(today.getMonth()); // 0-indexed

	const monthNames = [
		'Januar', 'Februar', 'Mars', 'April', 'Mai', 'Juni',
		'Juli', 'August', 'September', 'Oktober', 'November', 'Desember'
	];
	const dayNames = ['Man', 'Tir', 'Ons', 'Tor', 'Fre', 'Lør', 'Søn'];

	function prevMonth() {
		if (viewMonth === 0) {
			viewMonth = 11;
			viewYear--;
		} else {
			viewMonth--;
		}
	}

	function nextMonth() {
		if (viewMonth === 11) {
			viewMonth = 0;
			viewYear++;
		} else {
			viewMonth++;
		}
	}

	function goToday() {
		viewYear = today.getFullYear();
		viewMonth = today.getMonth();
	}

	// =========================================================================
	// Calendar grid computation
	// =========================================================================

	interface CalendarDay {
		date: Date;
		dayOfMonth: number;
		isCurrentMonth: boolean;
		isToday: boolean;
		dateKey: string; // YYYY-MM-DD for grouping
	}

	/** Build the 6-row grid of days for the current month view */
	const calendarDays = $derived.by((): CalendarDay[] => {
		const firstOfMonth = new Date(viewYear, viewMonth, 1);
		// Monday=0 ... Sunday=6 (ISO week)
		let startDow = firstOfMonth.getDay() - 1;
		if (startDow < 0) startDow = 6;

		const days: CalendarDay[] = [];
		// Start from the Monday before (or on) the 1st
		const startDate = new Date(viewYear, viewMonth, 1 - startDow);

		const todayKey = formatDateKey(today);

		for (let i = 0; i < 42; i++) {
			const d = new Date(startDate);
			d.setDate(startDate.getDate() + i);
			const dateKey = formatDateKey(d);
			days.push({
				date: d,
				dayOfMonth: d.getDate(),
				isCurrentMonth: d.getMonth() === viewMonth && d.getFullYear() === viewYear,
				isToday: dateKey === todayKey,
				dateKey
			});
		}
		return days;
	});

	function formatDateKey(d: Date): string {
		const y = d.getFullYear();
		const m = String(d.getMonth() + 1).padStart(2, '0');
		const day = String(d.getDate()).padStart(2, '0');
		return `${y}-${m}-${day}`;
	}

	// =========================================================================
	// Scheduled events from SpacetimeDB
	// =========================================================================

	interface ScheduledEvent {
		node: Node;
		edge: Edge;
		scheduledAt: Date;
		dateKey: string;
		timeStr: string; // HH:MM or "Heldag"
	}

	/** All scheduled events visible to the user */
	const scheduledEvents = $derived.by((): ScheduledEvent[] => {
		if (!connected || !nodeId) return [];

		const events: ScheduledEvent[] = [];

		for (const edge of edgeStore.byType('scheduled')) {
			const node = nodeStore.get(edge.sourceId);
			if (!node || nodeVisibility(node, nodeId) === 'hidden') continue;

			let at: string | undefined;
			try {
				const meta = JSON.parse(edge.metadata ?? '{}');
				at = meta.at;
			} catch { /* ignore */ }

			if (!at) continue;

			const scheduledAt = new Date(at);
			if (isNaN(scheduledAt.getTime())) continue;

			const dateKey = formatDateKey(scheduledAt);
			const hours = scheduledAt.getHours();
			const minutes = scheduledAt.getMinutes();
			// If time is 12:00, it's likely an all-day event (convention from kalender.md)
			const isAllDay = hours === 12 && minutes === 0;
			const timeStr = isAllDay
				? ''
				: `${String(hours).padStart(2, '0')}:${String(minutes).padStart(2, '0')}`;

			events.push({ node, edge, scheduledAt, dateKey, timeStr });
		}

		// Sort by time
		events.sort((a, b) => a.scheduledAt.getTime() - b.scheduledAt.getTime());
		return events;
	});

	/** Events grouped by dateKey for fast lookup */
	const eventsByDate = $derived.by((): Map<string, ScheduledEvent[]> => {
		const map = new Map<string, ScheduledEvent[]>();
		for (const ev of scheduledEvents) {
			const list = map.get(ev.dateKey);
			if (list) {
				list.push(ev);
			} else {
				map.set(ev.dateKey, [ev]);
			}
		}
		return map;
	});

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

	let draggedEvent = $state<ScheduledEvent | null>(null);
	let dragOverDate = $state<string | null>(null);

	function handleDragStart(e: DragEvent, event: ScheduledEvent) {
		draggedEvent = event;
		if (e.dataTransfer) {
			e.dataTransfer.effectAllowed = 'move';
			e.dataTransfer.setData('text/plain', event.node.id);
		}
	}

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

	function handleDragLeave() {
		dragOverDate = null;
	}

	async function handleDrop(e: DragEvent, dateKey: string) {
		e.preventDefault();
		dragOverDate = null;

		if (!draggedEvent || !accessToken) return;
		if (draggedEvent.dateKey === dateKey) {
			draggedEvent = null;
			return;
		}

		const event = draggedEvent;
		draggedEvent = null;

		// Parse the target date and preserve the original time
		const [y, m, d] = dateKey.split('-').map(Number);
		const newDate = new Date(y, m - 1, d,
			event.scheduledAt.getHours(),
			event.scheduledAt.getMinutes()
		);

		try {
			await updateEdge(accessToken, {
				edge_id: event.edge.id,
				metadata: { at: newDate.toISOString() }
			});
		} catch (err) {
			console.error('Feil ved flytting av hendelse:', err);
		}
	}

	function handleDragEnd() {
		draggedEvent = null;
		dragOverDate = null;
	}

	// =========================================================================
	// Create new scheduled event
	// =========================================================================

	let addingToDate = $state<string | null>(null);
	let newEventTitle = $state('');
	let newEventTime = $state('');
	let isCreating = $state(false);

	function startAddEvent(dateKey: string) {
		addingToDate = dateKey;
		newEventTitle = '';
		newEventTime = '';
	}

	async function handleCreateEvent() {
		if (!accessToken || !nodeId || !addingToDate || !newEventTitle.trim() || isCreating) return;

		isCreating = true;
		try {
			const [y, m, d] = addingToDate.split('-').map(Number);
			let scheduledDate: Date;
			if (newEventTime) {
				const [h, min] = newEventTime.split(':').map(Number);
				scheduledDate = new Date(y, m - 1, d, h, min);
			} else {
				// All-day: use T12:00:00 convention
				scheduledDate = new Date(y, m - 1, d, 12, 0);
			}

			// Create node
			const { node_id } = await createNode(accessToken, {
				node_kind: 'content',
				title: newEventTitle.trim(),
				visibility: 'hidden'
			});

			// Create owner edge
			await createEdge(accessToken, {
				source_id: nodeId,
				target_id: node_id,
				edge_type: 'owner'
			});

			// Create scheduled edge
			await createEdge(accessToken, {
				source_id: node_id,
				target_id: node_id,
				edge_type: 'scheduled',
				metadata: { at: scheduledDate.toISOString() }
			});

			newEventTitle = '';
			newEventTime = '';
			addingToDate = null;
		} catch (err) {
			console.error('Feil ved oppretting av hendelse:', err);
		} finally {
			isCreating = false;
		}
	}

	function handleEventKeydown(e: KeyboardEvent) {
		if (e.key === 'Enter') {
			e.preventDefault();
			handleCreateEvent();
		} else if (e.key === 'Escape') {
			addingToDate = null;
			newEventTitle = '';
			newEventTime = '';
		}
	}

	// =========================================================================
	// Event colors based on node_kind
	// =========================================================================

	function eventColor(node: Node): string {
		switch (node.nodeKind) {
			case 'communication': return 'bg-blue-100 border-blue-300 text-blue-800';
			case 'media': return 'bg-purple-100 border-purple-300 text-purple-800';
			case 'collection': return 'bg-green-100 border-green-300 text-green-800';
			default: return 'bg-amber-100 border-amber-300 text-amber-800';
		}
	}
</script>

<div class="min-h-screen bg-gray-50">
	<!-- Header -->
	<header class="border-b border-gray-200 bg-white">
		<div class="flex items-center justify-between px-4 py-3">
			<div class="flex items-center gap-3">
				<a href="/" class="text-sm text-gray-400 hover:text-gray-600">&larr; Mottak</a>
				<h1 class="text-lg font-semibold text-gray-900">Kalender</h1>
			</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">{scheduledEvents.length} hendelser</span>
			</div>
		</div>
	</header>

	<main class="p-4">
		{#if !connected}
			<p class="text-sm text-gray-400">Venter på tilkobling…</p>
		{:else}
			<!-- Month navigation -->
			<div class="mb-4 flex items-center justify-between">
				<button
					onclick={prevMonth}
					class="rounded-lg bg-white px-3 py-1.5 text-sm font-medium text-gray-700 shadow-sm border border-gray-200 hover:bg-gray-50"
				>
					&larr;
				</button>
				<div class="flex items-center gap-3">
					<h2 class="text-xl font-semibold text-gray-800">
						{monthNames[viewMonth]} {viewYear}
					</h2>
					<button
						onclick={goToday}
						class="rounded bg-gray-100 px-2 py-1 text-xs text-gray-600 hover:bg-gray-200"
					>
						I dag
					</button>
				</div>
				<button
					onclick={nextMonth}
					class="rounded-lg bg-white px-3 py-1.5 text-sm font-medium text-gray-700 shadow-sm border border-gray-200 hover:bg-gray-50"
				>
					&rarr;
				</button>
			</div>

			<!-- Calendar grid -->
			<div class="rounded-lg border border-gray-200 bg-white shadow-sm overflow-hidden">
				<!-- Day headers -->
				<div class="grid grid-cols-7 border-b border-gray-200 bg-gray-50">
					{#each dayNames as day}
						<div class="px-2 py-2 text-center text-xs font-medium text-gray-500">
							{day}
						</div>
					{/each}
				</div>

				<!-- Day cells -->
				<div class="grid grid-cols-7">
					{#each calendarDays as day, i (day.dateKey)}
						{@const dayEvents = eventsByDate.get(day.dateKey) ?? []}
						{@const isDropTarget = dragOverDate === day.dateKey}
						<div
							class="min-h-24 border-b border-r border-gray-100 p-1
								{day.isCurrentMonth ? '' : 'bg-gray-50'}
								{day.isToday ? 'bg-blue-50' : ''}
								{isDropTarget ? 'ring-2 ring-inset ring-blue-400 bg-blue-50' : ''}"
							ondragover={(e: DragEvent) => handleDragOver(e, day.dateKey)}
							ondragleave={handleDragLeave}
							ondrop={(e: DragEvent) => handleDrop(e, day.dateKey)}
							role="gridcell"
						>
							<!-- Date number -->
							<div class="flex items-center justify-between px-1">
								<span class="text-xs font-medium {day.isCurrentMonth ? 'text-gray-700' : 'text-gray-300'} {day.isToday ? 'rounded-full bg-blue-600 text-white px-1.5 py-0.5' : ''}">
									{day.dayOfMonth}
								</span>
								{#if day.isCurrentMonth && accessToken}
									<button
										onclick={() => startAddEvent(day.dateKey)}
										class="text-gray-300 hover:text-blue-500 text-xs leading-none"
										title="Legg til hendelse"
									>
										+
									</button>
								{/if}
							</div>

							<!-- Events -->
							<div class="mt-0.5 space-y-0.5">
								{#each dayEvents as event (event.node.id)}
									<div
										class="cursor-grab rounded border px-1 py-0.5 text-xs truncate {eventColor(event.node)} {draggedEvent?.node.id === event.node.id ? 'opacity-50' : ''}"
										draggable="true"
										ondragstart={(e: DragEvent) => handleDragStart(e, event)}
										ondragend={handleDragEnd}
										title="{event.timeStr ? event.timeStr + ' ' : ''}{event.node.title || 'Uten tittel'}"
									>
										{#if event.timeStr}
											<span class="font-medium">{event.timeStr}</span>
										{/if}
										{event.node.title || 'Uten tittel'}
									</div>
								{/each}
							</div>

							<!-- Inline add form -->
							{#if addingToDate === day.dateKey}
								<div class="mt-1 rounded border border-blue-300 bg-white p-1">
									<input
										type="text"
										bind:value={newEventTitle}
										onkeydown={handleEventKeydown}
										placeholder="Tittel…"
										class="w-full rounded border border-gray-200 px-1 py-0.5 text-xs focus:border-blue-400 focus:outline-none"
										disabled={isCreating}
									/>
									<input
										type="time"
										bind:value={newEventTime}
										class="mt-0.5 w-full rounded border border-gray-200 px-1 py-0.5 text-xs focus:border-blue-400 focus:outline-none"
									/>
									<div class="mt-1 flex gap-1">
										<button
											onclick={handleCreateEvent}
											disabled={isCreating || !newEventTitle.trim()}
											class="rounded bg-blue-600 px-1.5 py-0.5 text-xs font-medium text-white hover:bg-blue-700 disabled:opacity-50"
										>
											{isCreating ? '…' : 'Legg til'}
										</button>
										<button
											onclick={() => { addingToDate = null; newEventTitle = ''; newEventTime = ''; }}
											class="rounded px-1.5 py-0.5 text-xs text-gray-500 hover:bg-gray-100"
										>
											Avbryt
										</button>
									</div>
								</div>
							{/if}
						</div>
					{/each}
				</div>
			</div>

			<!-- Event list for current month -->
			{#if scheduledEvents.length > 0}
				{@const monthEvents = scheduledEvents.filter(e => {
					const d = e.scheduledAt;
					return d.getMonth() === viewMonth && d.getFullYear() === viewYear;
				})}
				{#if monthEvents.length > 0}
					<div class="mt-4">
						<h3 class="mb-2 text-sm font-medium text-gray-600">Hendelser denne måneden</h3>
						<ul class="space-y-1">
							{#each monthEvents as event (event.node.id)}
								<li class="flex items-center gap-2 rounded-lg border border-gray-200 bg-white px-3 py-2 text-sm shadow-sm">
									<span class="shrink-0 text-xs text-gray-400">
										{event.scheduledAt.getDate()}. {monthNames[event.scheduledAt.getMonth()].slice(0, 3).toLowerCase()}
									</span>
									{#if event.timeStr}
										<span class="shrink-0 text-xs font-medium text-gray-600">{event.timeStr}</span>
									{/if}
									<span class="truncate text-gray-900">{event.node.title || 'Uten tittel'}</span>
									<span class="shrink-0 rounded-full bg-gray-100 px-2 py-0.5 text-xs text-gray-500">
										{event.node.nodeKind}
									</span>
								</li>
							{/each}
						</ul>
					</div>
				{/if}
			{/if}
		{/if}
	</main>
</div>
