<script lang="ts">
	import { page } from '$app/stores';
	import { connectionState, nodeStore, edgeStore, nodeVisibility } from '$lib/realtime';
	import type { Node } from '$lib/realtime';
	import { createNode, casUrl } from '$lib/api';
	import ChatInput from '$lib/components/ChatInput.svelte';
	import AudioPlayer from '$lib/components/AudioPlayer.svelte';
	import LocationMap from '$lib/components/LocationMap.svelte';
	import { tick } from 'svelte';

	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 communicationId = $derived($page.params.id ?? '');

	/** The communication node itself */
	const communicationNode = $derived(connected ? nodeStore.get(communicationId) : undefined);

	/**
	 * Messages in this chat: all nodes with a belongs_to edge pointing
	 * to this communication node, sorted by created_at ascending.
	 *
	 * Edge direction: message (source) --belongs_to--> communication (target)
	 * So we look at edges targeting the communication node with type belongs_to.
	 */
	const messages = $derived.by(() => {
		if (!connected || !communicationId) return [];

		const seen = new Set<string>();
		const nodes: Node[] = [];

		// Text messages: belongs_to edges pointing to this communication node
		// Edge direction: message (source) --belongs_to--> communication (target)
		for (const edge of edgeStore.byTarget(communicationId)) {
			if (edge.edgeType !== 'belongs_to') continue;
			const node = nodeStore.get(edge.sourceId);
			if (node && nodeVisibility(node, nodeId) !== 'hidden' && !seen.has(node.id)) {
				seen.add(node.id);
				nodes.push(node);
			}
		}

		// Media nodes: has_media edges from this communication node
		// Edge direction: communication (source) --has_media--> media (target)
		for (const edge of edgeStore.bySource(communicationId)) {
			if (edge.edgeType !== 'has_media') continue;
			const node = nodeStore.get(edge.targetId);
			if (node && nodeVisibility(node, nodeId) !== 'hidden' && !seen.has(node.id)) {
				seen.add(node.id);
				nodes.push(node);
			}
		}

		// Sort by created_at ascending (oldest first, like a chat)
		nodes.sort((a, b) => {
			const ta = a.createdAt ?? 0;
			const tb = b.createdAt ?? 0;
			return ta > tb ? 1 : ta < tb ? -1 : 0;
		});

		return nodes;
	});

	/** Participants: nodes with owner/member_of edges to this communication node */
	const participants = $derived.by(() => {
		if (!connected || !communicationId) return [];

		const edges = edgeStore.byTarget(communicationId)
			.filter(e => e.edgeType === 'owner' || e.edgeType === 'member_of');

		const nodes: Node[] = [];
		for (const edge of edges) {
			const node = nodeStore.get(edge.sourceId);
			if (node) nodes.push(node);
		}
		return nodes;
	});

	/** For 1:1 chats, show the other person's name instead of the generic title */
	const chatTitle = $derived.by(() => {
		if (participants.length === 2 && nodeId) {
			const other = participants.find(p => p.id !== nodeId);
			if (other?.title) return other.title;
		}
		return communicationNode?.title || 'Samtale';
	});

	// Auto-scroll to bottom when new messages arrive
	let messagesContainer: HTMLDivElement | undefined = $state();
	let prevMessageCount = $state(0);

	$effect(() => {
		const count = messages.length;
		if (count > prevMessageCount && messagesContainer) {
			tick().then(() => {
				messagesContainer?.scrollTo({ top: messagesContainer.scrollHeight, behavior: 'smooth' });
			});
		}
		prevMessageCount = count;
	});

	/** Send a message in this communication node */
	async function handleSendMessage(content: string) {
		if (!accessToken) throw new Error('Ikke innlogget');

		await createNode(accessToken, {
			node_kind: 'content',
			content,
			visibility: 'hidden',
			context_id: communicationId,
		});
	}

	/** Format timestamp for display */
	function formatTime(node: Node): string {
		if (!node.createdAt) return '';
		const ms = Math.floor(node.createdAt / 1000);
		const date = new Date(ms);
		const now = new Date();
		const isToday = date.toDateString() === now.toDateString();
		if (isToday) {
			return date.toLocaleTimeString('nb-NO', { hour: '2-digit', minute: '2-digit' });
		}
		return date.toLocaleDateString('nb-NO', {
			day: 'numeric', month: 'short', hour: '2-digit', minute: '2-digit'
		});
	}

	/** Get display name for message sender */
	function senderName(node: Node): string {
		if (!node.createdBy) return 'Ukjent';
		const sender = nodeStore.get(node.createdBy);
		return sender?.title || sender?.nodeKind || 'Ukjent';
	}

	/** Check if message sender is an agent (bot) */
	function isAgentMessage(node: Node): boolean {
		if (!node.createdBy) return false;
		const sender = nodeStore.get(node.createdBy);
		return sender?.nodeKind === 'agent';
	}

	/** Check if this message is from the current user */
	function isOwnMessage(node: Node): boolean {
		return !!nodeId && node.createdBy === nodeId;
	}

	/** Check if this node is an audio media node */
	function isAudioNode(node: Node): boolean {
		if (node.nodeKind !== 'media') return false;
		try {
			const meta = JSON.parse(node.metadata ?? '{}');
			return typeof meta.mime === 'string' && meta.mime.startsWith('audio/');
		} catch { return false; }
	}

	/** Check if this node is an image media node */
	function isImageNode(node: Node): boolean {
		if (node.nodeKind !== 'media') return false;
		try {
			const meta = JSON.parse(node.metadata ?? '{}');
			return typeof meta.mime === 'string' && meta.mime.startsWith('image/');
		} catch { return false; }
	}

	/** Get CAS image URL for a media node */
	function imageSrc(node: Node): string {
		try {
			const meta = JSON.parse(node.metadata ?? '{}');
			return casUrl(meta.cas_hash);
		} catch { return ''; }
	}

	/** Get AI-generated description for an image node */
	function imageDescription(node: Node): string | undefined {
		try {
			const meta = JSON.parse(node.metadata ?? '{}');
			return meta.description;
		} catch { return undefined; }
	}

	/** Get CAS audio URL for a media node */
	function audioSrc(node: Node): string {
		try {
			const meta = JSON.parse(node.metadata ?? '{}');
			return casUrl(meta.cas_hash);
		} catch { return ''; }
	}

	/** Get transcription duration from metadata */
	function audioDuration(node: Node): number | undefined {
		try {
			const meta = JSON.parse(node.metadata ?? '{}');
			const dms = meta.transcription?.duration_ms;
			return dms ? dms / 1000 : undefined;
		} catch { return undefined; }
	}

	/** Check if an audio node has transcription segments */
	function hasSegments(node: Node): boolean {
		try {
			const meta = JSON.parse(node.metadata ?? '{}');
			return (meta.transcription?.segment_count ?? 0) > 0;
		} catch { return false; }
	}

	/** Check if this node is a location node */
	function isLocationNode(node: Node): boolean {
		try {
			const meta = JSON.parse(node.metadata ?? '{}');
			return meta.location && typeof meta.location.lat === 'number' && typeof meta.location.lon === 'number';
		} catch { return false; }
	}

	/** Get location data from a node */
	function getLocation(node: Node): { lat: number; lon: number; address?: string } | undefined {
		try {
			const meta = JSON.parse(node.metadata ?? '{}');
			if (meta.location?.lat != null && meta.location?.lon != null) {
				return meta.location;
			}
		} catch {}
		return undefined;
	}
</script>

<div class="flex h-screen flex-col bg-gray-50">
	<!-- Header -->
	<header class="shrink-0 border-b border-gray-200 bg-white">
		<div class="mx-auto flex max-w-3xl items-center gap-3 px-4 py-3">
			<a
				href="/"
				class="rounded p-1 text-gray-400 hover:bg-gray-100 hover:text-gray-600"
				aria-label="Tilbake"
			>
				<svg class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
					<path stroke-linecap="round" stroke-linejoin="round" d="M15 19l-7-7 7-7" />
				</svg>
			</a>
			<div class="min-w-0 flex-1">
				<h1 class="truncate text-lg font-semibold text-gray-900">
					{chatTitle}
				</h1>
				{#if participants.length > 0}
					<p class="truncate text-xs text-gray-500">
						{participants.map(p => p.title || 'Ukjent').join(', ')}
					</p>
				{/if}
			</div>
			{#if connected}
				<span class="text-xs text-green-600">Tilkoblet</span>
			{:else}
				<span class="text-xs text-gray-400">{connectionState.current}</span>
			{/if}
		</div>
	</header>

	<!-- Messages -->
	<div
		bind:this={messagesContainer}
		class="flex-1 overflow-y-auto"
	>
		<div class="mx-auto max-w-3xl px-4 py-4">
			{#if !connected}
				<p class="text-center text-sm text-gray-400">Venter på tilkobling…</p>
			{:else if !communicationNode}
				<div class="rounded-lg border border-yellow-200 bg-yellow-50 p-4 text-sm text-yellow-800">
					<p class="font-medium">Samtale ikke funnet</p>
					<p class="mt-1">Kommunikasjonsnoden med ID {communicationId} finnes ikke eller er ikke tilgjengelig.</p>
					<a href="/" class="mt-2 inline-block text-blue-600 hover:underline">Tilbake til arbeidsflaten</a>
				</div>
			{:else if messages.length === 0}
				<p class="text-center text-sm text-gray-400">
					Ingen meldinger ennå. Skriv den første!
				</p>
			{:else}
				<div class="space-y-3">
					{#each messages as msg (msg.id)}
						{@const own = isOwnMessage(msg)}
						{@const audio = isAudioNode(msg)}
						{@const image = isImageNode(msg)}
						{@const location = isLocationNode(msg)}
						{@const bot = isAgentMessage(msg)}
						<div class="flex {own ? 'justify-end' : 'justify-start'}">
							<div class="max-w-[75%] {own ? (audio || image || location ? 'bg-blue-50 border border-blue-200 text-gray-900' : 'bg-blue-600 text-white') : bot ? 'bg-amber-50 border border-amber-200 text-gray-900' : 'bg-white border border-gray-200 text-gray-900'} rounded-2xl px-4 py-2 shadow-sm">
								{#if !own}
									<p class="mb-0.5 text-xs font-medium {bot ? 'text-amber-700' : 'text-blue-600'}">
										{#if bot}<span title="AI-agent">🤖 </span>{/if}{senderName(msg)}
									</p>
								{/if}
								{#if audio}
									<div class="flex items-center gap-1.5 mb-1">
										<svg class="h-3.5 w-3.5 text-blue-500 shrink-0" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
											<path stroke-linecap="round" stroke-linejoin="round" d="M12 1a3 3 0 00-3 3v8a3 3 0 006 0V4a3 3 0 00-3-3z" />
											<path stroke-linecap="round" stroke-linejoin="round" d="M19 10v2a7 7 0 01-14 0v-2" />
										</svg>
										<span class="text-[11px] font-medium text-blue-600">Talenotat</span>
									</div>
									<AudioPlayer
										src={audioSrc(msg)}
										duration={audioDuration(msg)}
										transcript={!hasSegments(msg) ? (msg.content || undefined) : undefined}
										nodeId={hasSegments(msg) ? msg.id : undefined}
										accessToken={hasSegments(msg) ? accessToken : undefined}
										compact
									/>
								{:else if image}
									<div class="my-1">
										<img
											src={imageSrc(msg)}
											alt={imageDescription(msg) || msg.title || 'Skjermklipp'}
											class="max-w-full rounded-lg"
											loading="lazy"
										/>
										{#if imageDescription(msg)}
											<p class="mt-1 text-xs text-gray-500 italic">{imageDescription(msg)}</p>
										{/if}
									</div>
								{:else if location}
									{@const loc = getLocation(msg)}
									{#if loc}
										<div class="flex items-center gap-1.5 mb-1">
											<svg class="h-3.5 w-3.5 text-green-500 shrink-0" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
												<path stroke-linecap="round" stroke-linejoin="round" d="M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z" />
												<path stroke-linecap="round" stroke-linejoin="round" d="M15 11a3 3 0 11-6 0 3 3 0 016 0z" />
											</svg>
											<span class="text-[11px] font-medium text-green-600">Posisjon</span>
										</div>
										<LocationMap lat={loc.lat} lon={loc.lon} address={loc.address} height={180} />
									{/if}
								{:else}
									<p class="text-sm whitespace-pre-wrap break-words">
										{msg.content || ''}
									</p>
								{/if}
								<p class="mt-1 text-right text-[10px] {own && !audio && !image && !location ? 'text-blue-200' : 'text-gray-400'}">
									{formatTime(msg)}
								</p>
							</div>
						</div>
					{/each}
				</div>
			{/if}
		</div>
	</div>

	<!-- Input -->
	{#if connected && accessToken && communicationNode}
		<div class="shrink-0 border-t border-gray-200 bg-white">
			<div class="mx-auto max-w-3xl px-4 py-3">
				<ChatInput onsubmit={handleSendMessage} disabled={!connected} {accessToken} contextId={communicationId} />
			</div>
		</div>
	{/if}
</div>
