<script lang="ts">
	import type { Node, Edge } from '$lib/realtime';
	import { edgeStore, nodeStore, nodeVisibility, connectionState } from '$lib/realtime';
	import { createNode, createEdge, updateEdge } from '$lib/api';
	import { setDragPayload, checkCalendarCompat, type DragPayload } from '$lib/transfer';
	import type { BlockReceiver, PlacementIntent } from '$lib/components/blockshell/types';

	interface Props {
		collection?: Node;
		config: Record<string, unknown>;
		userId?: string;
		accessToken?: string;
		/** Called when a drop is received on this panel */
		onReceiveDrop?: (payload: DragPayload, intent: PlacementIntent) => void;
	}

	let { collection, config, userId, accessToken, onReceiveDrop }: Props = $props();

	const connected = $derived(connectionState.current === 'connected');

	/**
	 * BlockReceiver implementation for Calendar.
	 * Accepts communication and content nodes as new events/scheduled items.
	 * Kanban cards get a scheduled edge.
	 * Ref: docs/retninger/arbeidsflaten.md § Kompatibilitetsmatrise
	 */
	export const receiver: BlockReceiver = {
		canReceive(payload: DragPayload) {
			return checkCalendarCompat(payload);
		},
		receive(payload: DragPayload) {
			const today = new Date().toISOString().slice(0, 10);
			const mode = payload.sourcePanel === 'kanban' ? 'lettvekts-triage' as const : 'innholdstransfer' as const;
			const intent: PlacementIntent = {
				mode,
				contextId: collection?.id ?? '',
				contextType: 'calendar',
				position: { date: today, all_day: true },
			};
			onReceiveDrop?.(payload, intent);
			return intent;
		}
	};

	// =========================================================================
	// 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 = ['Ma', 'Ti', 'On', 'To', 'Fr', 'Lø', 'Sø'];

	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;
	}

	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}`;
	}

	const calendarDays = $derived.by((): CalendarDay[] => {
		const firstOfMonth = new Date(viewYear, viewMonth, 1);
		let startDow = firstOfMonth.getDay() - 1;
		if (startDow < 0) startDow = 6;

		const days: CalendarDay[] = [];
		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;
	});

	// =========================================================================
	// Scheduled events from WebSocket store
	// =========================================================================

	interface ScheduledEvent {
		node: Node;
		edge: Edge;
		scheduledAt: Date;
		dateKey: string;
		timeStr: string;
	}

	const scheduledEvents = $derived.by((): ScheduledEvent[] => {
		if (!connected || !userId) return [];

		const events: ScheduledEvent[] = [];

		for (const edge of edgeStore.byType('scheduled')) {
			const node = nodeStore.get(edge.sourceId);
			if (!node || nodeVisibility(node, userId) === '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();
			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 });
		}

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

	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;
	});

	// =========================================================================
	// Internal drag-and-drop (move events between dates)
	// =========================================================================

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

	function handleDragStart(e: DragEvent, event: ScheduledEvent) {
		draggedEvent = event;
		if (!e.dataTransfer) return;
		e.dataTransfer.effectAllowed = 'move';
		// Set cross-panel payload for dragging events to other panels
		setDragPayload(e.dataTransfer, {
			nodeId: event.node.id,
			nodeKind: event.node.nodeKind,
			sourcePanel: 'calendar',
		});
	}

	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;

		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 || !userId || !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 {
				scheduledDate = new Date(y, m - 1, d, 12, 0);
			}

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

			await createEdge(accessToken, {
				source_id: userId,
				target_id: node_id,
				edge_type: 'owner'
			});

			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 eventColorClass(node: Node): string {
		switch (node.nodeKind) {
			case 'communication': return 'cal-event-comm';
			case 'media': return 'cal-event-media';
			case 'collection': return 'cal-event-coll';
			default: return 'cal-event-default';
		}
	}
</script>

<!--
	CalendarTrait — fullverdig BlockShell-panel for kalender.
	Viser månedsgrid med hendelser, drag-and-drop mellom datoer,
	inline opprettelse, og støtter mottak av noder fra andre paneler.
	Forelder (collection page) wrapper dette i BlockShell.
-->

<div class="cal-trait">
	{#if !connected}
		<div class="cal-empty">
			<p>Venter på tilkobling…</p>
		</div>
	{:else}
		<!-- Month navigation -->
		<div class="cal-nav">
			<button onclick={prevMonth} class="cal-nav-btn" aria-label="Forrige måned">&larr;</button>
			<div class="cal-nav-center">
				<span class="cal-nav-title">{monthNames[viewMonth]} {viewYear}</span>
				<button onclick={goToday} class="cal-today-btn">I dag</button>
			</div>
			<button onclick={nextMonth} class="cal-nav-btn" aria-label="Neste måned">&rarr;</button>
		</div>

		<!-- Calendar grid -->
		<div class="cal-grid-wrapper">
			<!-- Day headers -->
			<div class="cal-day-headers">
				{#each dayNames as day}
					<div class="cal-day-header">{day}</div>
				{/each}
			</div>

			<!-- Day cells -->
			<div class="cal-grid">
				{#each calendarDays as day (day.dateKey)}
					{@const dayEvents = eventsByDate.get(day.dateKey) ?? []}
					{@const isDropTarget = dragOverDate === day.dateKey}
					<div
						class="cal-day
							{day.isCurrentMonth ? '' : 'cal-day-other'}
							{day.isToday ? 'cal-day-today' : ''}
							{isDropTarget ? 'cal-day-drop' : ''}"
						ondragover={(e: DragEvent) => handleDragOver(e, day.dateKey)}
						ondragleave={handleDragLeave}
						ondrop={(e: DragEvent) => handleDrop(e, day.dateKey)}
						role="gridcell"
					>
						<!-- Date number + add button -->
						<div class="cal-day-num-row">
							<span class="cal-day-num {day.isToday ? 'cal-day-num-today' : ''}">
								{day.dayOfMonth}
							</span>
							{#if day.isCurrentMonth && accessToken}
								<button
									onclick={() => startAddEvent(day.dateKey)}
									class="cal-add-btn"
									title="Legg til hendelse"
								>+</button>
							{/if}
						</div>

						<!-- Events -->
						{#each dayEvents as event (event.node.id)}
							<div
								class="cal-event {eventColorClass(event.node)} {draggedEvent?.node.id === event.node.id ? 'cal-event-dragging' : ''}"
								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="cal-event-time">{event.timeStr}</span>
								{/if}
								<span class="cal-event-title">{event.node.title || 'Uten tittel'}</span>
							</div>
						{/each}

						<!-- Inline add form -->
						{#if addingToDate === day.dateKey}
							<div class="cal-add-form">
								<input
									type="text"
									bind:value={newEventTitle}
									onkeydown={handleEventKeydown}
									placeholder="Tittel…"
									class="cal-add-input"
									disabled={isCreating}
								/>
								<input
									type="time"
									bind:value={newEventTime}
									class="cal-add-input cal-add-time"
								/>
								<div class="cal-add-actions">
									<button
										onclick={handleCreateEvent}
										disabled={isCreating || !newEventTitle.trim()}
										class="cal-add-submit"
									>
										{isCreating ? '…' : 'Legg til'}
									</button>
									<button
										onclick={() => { addingToDate = null; newEventTitle = ''; newEventTime = ''; }}
										class="cal-add-cancel"
									>
										Avbryt
									</button>
								</div>
							</div>
						{/if}
					</div>
				{/each}
			</div>
		</div>

		<!-- Event list for current month -->
		{@const monthEvents = scheduledEvents.filter(e => {
			const d = e.scheduledAt;
			return d.getMonth() === viewMonth && d.getFullYear() === viewYear;
		})}
		{#if monthEvents.length > 0}
			<div class="cal-list">
				<div class="cal-list-header">Hendelser denne måneden</div>
				{#each monthEvents as event (event.node.id)}
					<div
						class="cal-list-item"
						draggable="true"
						ondragstart={(e: DragEvent) => handleDragStart(e, event)}
						ondragend={handleDragEnd}
					>
						<span class="cal-list-date">
							{event.scheduledAt.getDate()}. {monthNames[event.scheduledAt.getMonth()].slice(0, 3).toLowerCase()}
						</span>
						{#if event.timeStr}
							<span class="cal-list-time">{event.timeStr}</span>
						{/if}
						<span class="cal-list-title">{event.node.title || 'Uten tittel'}</span>
					</div>
				{/each}
			</div>
		{/if}
	{/if}
</div>

<style>
	/* ================================================================= */
	/* Root — fills BlockShell content area                               */
	/* ================================================================= */
	.cal-trait {
		display: flex;
		flex-direction: column;
		height: 100%;
		min-height: 0;
		overflow: hidden;
	}

	/* ================================================================= */
	/* Empty / loading state                                              */
	/* ================================================================= */
	.cal-empty {
		display: flex;
		align-items: center;
		justify-content: center;
		flex: 1;
		color: #9ca3af;
		font-size: 13px;
	}

	/* ================================================================= */
	/* Month navigation                                                   */
	/* ================================================================= */
	.cal-nav {
		display: flex;
		align-items: center;
		justify-content: space-between;
		padding: 6px 8px;
		flex-shrink: 0;
		border-bottom: 1px solid #f3f4f6;
	}

	.cal-nav-btn {
		display: flex;
		align-items: center;
		justify-content: center;
		width: 28px;
		height: 28px;
		border: 1px solid #e5e7eb;
		background: white;
		border-radius: 6px;
		cursor: pointer;
		font-size: 12px;
		color: #6b7280;
		flex-shrink: 0;
	}

	.cal-nav-btn:hover {
		background: #f9fafb;
		color: #374151;
	}

	.cal-nav-center {
		display: flex;
		align-items: center;
		gap: 6px;
		min-width: 0;
	}

	.cal-nav-title {
		font-size: 13px;
		font-weight: 600;
		color: #1f2937;
		white-space: nowrap;
	}

	.cal-today-btn {
		padding: 2px 6px;
		border: none;
		background: #f3f4f6;
		border-radius: 4px;
		font-size: 10px;
		color: #6b7280;
		cursor: pointer;
	}

	.cal-today-btn:hover {
		background: #e5e7eb;
	}

	/* ================================================================= */
	/* Calendar grid                                                      */
	/* ================================================================= */
	.cal-grid-wrapper {
		flex: 1;
		min-height: 0;
		overflow-y: auto;
		overflow-x: hidden;
	}

	.cal-day-headers {
		display: grid;
		grid-template-columns: repeat(7, 1fr);
		border-bottom: 1px solid #e5e7eb;
		background: #f9fafb;
		position: sticky;
		top: 0;
		z-index: 1;
	}

	.cal-day-header {
		text-align: center;
		font-size: 10px;
		font-weight: 600;
		color: #9ca3af;
		padding: 4px 0;
		text-transform: uppercase;
		letter-spacing: 0.03em;
	}

	.cal-grid {
		display: grid;
		grid-template-columns: repeat(7, 1fr);
	}

	/* ================================================================= */
	/* Day cell                                                           */
	/* ================================================================= */
	.cal-day {
		min-height: 56px;
		border-bottom: 1px solid #f3f4f6;
		border-right: 1px solid #f3f4f6;
		padding: 2px;
		overflow: hidden;
	}

	.cal-day-other {
		background: #fafbfc;
	}

	.cal-day-today {
		background: #eff6ff;
	}

	.cal-day-drop {
		box-shadow: inset 0 0 0 2px #60a5fa;
		background: #eff6ff;
	}

	.cal-day-num-row {
		display: flex;
		align-items: center;
		justify-content: space-between;
		padding: 0 2px;
	}

	.cal-day-num {
		font-size: 11px;
		font-weight: 500;
		color: #6b7280;
		line-height: 1;
	}

	.cal-day-other .cal-day-num {
		color: #d1d5db;
	}

	.cal-day-num-today {
		background: #2563eb;
		color: white;
		border-radius: 9999px;
		padding: 1px 4px;
	}

	.cal-add-btn {
		border: none;
		background: transparent;
		color: #d1d5db;
		font-size: 11px;
		cursor: pointer;
		line-height: 1;
		padding: 0 2px;
	}

	.cal-add-btn:hover {
		color: #2563eb;
	}

	/* ================================================================= */
	/* Event chip                                                         */
	/* ================================================================= */
	.cal-event {
		display: flex;
		align-items: center;
		gap: 2px;
		margin-top: 1px;
		padding: 1px 3px;
		border-radius: 3px;
		border: 1px solid;
		cursor: grab;
		overflow: hidden;
		font-size: 10px;
		line-height: 1.3;
		transition: opacity 0.15s;
	}

	.cal-event:active {
		cursor: grabbing;
	}

	.cal-event-dragging {
		opacity: 0.4;
	}

	.cal-event-default {
		background: #fef3c7;
		border-color: #fcd34d;
		color: #92400e;
	}

	.cal-event-comm {
		background: #dbeafe;
		border-color: #93c5fd;
		color: #1e40af;
	}

	.cal-event-media {
		background: #ede9fe;
		border-color: #c4b5fd;
		color: #5b21b6;
	}

	.cal-event-coll {
		background: #d1fae5;
		border-color: #6ee7b7;
		color: #065f46;
	}

	.cal-event-time {
		font-weight: 600;
		flex-shrink: 0;
	}

	.cal-event-title {
		overflow: hidden;
		text-overflow: ellipsis;
		white-space: nowrap;
	}

	/* ================================================================= */
	/* Inline add form                                                    */
	/* ================================================================= */
	.cal-add-form {
		margin-top: 2px;
		padding: 3px;
		background: white;
		border: 1px solid #bfdbfe;
		border-radius: 4px;
	}

	.cal-add-input {
		width: 100%;
		padding: 2px 4px;
		border: 1px solid #e5e7eb;
		border-radius: 3px;
		font-size: 10px;
		outline: none;
		box-sizing: border-box;
	}

	.cal-add-input:focus {
		border-color: #60a5fa;
	}

	.cal-add-time {
		margin-top: 2px;
	}

	.cal-add-actions {
		display: flex;
		gap: 3px;
		margin-top: 2px;
	}

	.cal-add-submit {
		padding: 1px 6px;
		border: none;
		border-radius: 3px;
		font-size: 10px;
		font-weight: 500;
		background: #2563eb;
		color: white;
		cursor: pointer;
	}

	.cal-add-submit:hover {
		background: #1d4ed8;
	}

	.cal-add-submit:disabled {
		opacity: 0.5;
		cursor: default;
	}

	.cal-add-cancel {
		padding: 1px 6px;
		border: none;
		border-radius: 3px;
		font-size: 10px;
		background: transparent;
		color: #6b7280;
		cursor: pointer;
	}

	.cal-add-cancel:hover {
		background: #f3f4f6;
	}

	/* ================================================================= */
	/* Event list (below grid)                                            */
	/* ================================================================= */
	.cal-list {
		flex-shrink: 0;
		border-top: 1px solid #e5e7eb;
		max-height: 140px;
		overflow-y: auto;
	}

	.cal-list-header {
		padding: 4px 8px;
		font-size: 10px;
		font-weight: 600;
		text-transform: uppercase;
		letter-spacing: 0.03em;
		color: #9ca3af;
		position: sticky;
		top: 0;
		background: white;
	}

	.cal-list-item {
		display: flex;
		align-items: center;
		gap: 6px;
		padding: 4px 8px;
		font-size: 12px;
		cursor: grab;
		transition: background 0.1s;
	}

	.cal-list-item:hover {
		background: #f9fafb;
	}

	.cal-list-item:active {
		cursor: grabbing;
	}

	.cal-list-date {
		font-size: 10px;
		color: #9ca3af;
		flex-shrink: 0;
	}

	.cal-list-time {
		font-size: 10px;
		font-weight: 600;
		color: #6b7280;
		flex-shrink: 0;
	}

	.cal-list-title {
		overflow: hidden;
		text-overflow: ellipsis;
		white-space: nowrap;
		color: #1f2937;
	}

	/* ================================================================= */
	/* Responsive within bounded container                                */
	/* ================================================================= */

	/* Small panels: compact everything */
	@container (max-width: 360px) {
		.cal-nav {
			padding: 4px 6px;
		}

		.cal-nav-title {
			font-size: 11px;
		}

		.cal-nav-btn {
			width: 24px;
			height: 24px;
			font-size: 10px;
		}

		.cal-day {
			min-height: 40px;
			padding: 1px;
		}

		.cal-day-num {
			font-size: 9px;
		}

		.cal-day-header {
			font-size: 8px;
			padding: 2px 0;
		}

		.cal-event {
			font-size: 8px;
			padding: 0 2px;
		}

		.cal-add-form {
			padding: 2px;
		}

		.cal-list {
			max-height: 100px;
		}

		.cal-list-item {
			font-size: 10px;
			padding: 3px 6px;
		}
	}

	/* Medium panels: slightly tighter */
	@container (max-width: 500px) {
		.cal-day {
			min-height: 48px;
		}

		.cal-event {
			font-size: 9px;
		}
	}

	/* Mobile viewport fallback */
	@media (max-width: 480px) {
		.cal-day {
			min-height: 40px;
			padding: 1px;
		}

		.cal-day-num {
			font-size: 9px;
		}

		.cal-day-header {
			font-size: 8px;
		}

		.cal-event {
			font-size: 8px;
		}

		.cal-list {
			max-height: 100px;
		}
	}
</style>
