<script lang="ts">
	import { page } from '$app/stores';
	import { signOut } from '@auth/sveltekit/client';
	import { connectionState, nodeStore, edgeStore, nodeAccessStore, nodeVisibility } from '$lib/spacetime';
	import type { Node } from '$lib/spacetime';
	import NodeEditor from '$lib/components/NodeEditor.svelte';
	import NewChatDialog from '$lib/components/NewChatDialog.svelte';
	import AudioPlayer from '$lib/components/AudioPlayer.svelte';
	import { createNode, createEdge, createCommunication, casUrl } 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');

	/**
	 * Find all nodes visible to the user, applying visibility filtering.
	 *
	 * A node is visible if:
	 * - User created it (created_by)
	 * - User has explicit access (node_access)
	 * - Node is 'readable' or 'open' (public)
	 * - Node is 'discoverable' (shown with limited info)
	 *
	 * Among visible nodes, the mottak shows:
	 * - Nodes connected via edges (either direction, non-system)
	 * - Readable/open/discoverable nodes
	 *
	 * Sorted by created_at descending (newest first).
	 */
	const mottaksnoder = $derived.by(() => {
		if (!nodeId || !connected) return [];

		// Collect node IDs connected to the user via edges
		const connectedNodeIds = new Set<string>();

		for (const edge of edgeStore.bySource(nodeId)) {
			if (!edge.system) connectedNodeIds.add(edge.targetId);
		}
		for (const edge of edgeStore.byTarget(nodeId)) {
			if (!edge.system) connectedNodeIds.add(edge.sourceId);
		}

		// Also include all nodes user has explicit access to
		const accessibleIds = nodeAccessStore.objectsForSubject(nodeId);
		for (const id of accessibleIds) {
			connectedNodeIds.add(id);
		}

		// Resolve to nodes, applying visibility filter
		const nodes: Node[] = [];
		for (const id of connectedNodeIds) {
			if (id === nodeId) continue;
			const node = nodeStore.get(id);
			if (node && nodeVisibility(node, nodeId) !== 'hidden') {
				nodes.push(node);
			}
		}

		// Also add public nodes (readable/open) that aren't connected
		for (const node of nodeStore.all) {
			if (node.id === nodeId) continue;
			if (connectedNodeIds.has(node.id)) continue;
			if (node.visibility === 'readable' || node.visibility === 'open') {
				nodes.push(node);
			}
		}

		// Sort by created_at descending
		nodes.sort((a, b) => {
			const ta = a.createdAt?.microsSinceUnixEpoch ?? 0n;
			const tb = b.createdAt?.microsSinceUnixEpoch ?? 0n;
			return tb > ta ? 1 : tb < ta ? -1 : 0;
		});

		return nodes;
	});

	/** Truncate content to a short excerpt */
	function excerpt(content: string, maxLen = 140): string {
		if (!content) return '';
		if (content.length <= maxLen) return content;
		return content.slice(0, maxLen).trimEnd() + '…';
	}

	/** Format node_kind as a readable label */
	function kindLabel(kind: string): string {
		const labels: Record<string, string> = {
			content: 'Innhold',
			person: 'Person',
			team: 'Team',
			collection: 'Samling',
			communication: 'Samtale',
			topic: 'Tema',
			media: 'Media'
		};
		return labels[kind] ?? kind;
	}

	/** Get edge types between the user and a node */
	function edgeTypes(targetNodeId: string): string[] {
		if (!nodeId) return [];
		const edges = edgeStore.between(nodeId, targetNodeId);
		return edges.filter((e) => !e.system).map((e) => e.edgeType);
	}

	/** Check if a node is an audio media node and extract its metadata */
	function audioMeta(node: Node): { src: string; duration?: number; hasSegments: boolean } | null {
		if (node.nodeKind !== 'media') return null;
		try {
			const meta = JSON.parse(node.metadata ?? '{}');
			if (typeof meta.mime === 'string' && meta.mime.startsWith('audio/') && meta.cas_hash) {
				return {
					src: casUrl(meta.cas_hash),
					duration: meta.transcription?.duration_ms ? meta.transcription.duration_ms / 1000 : undefined,
					hasSegments: (meta.transcription?.segment_count ?? 0) > 0,
				};
			}
		} catch { /* ignore */ }
		return null;
	}

	/** Check if a node is a kanban board (collection with board metadata, no traits) */
	function isBoard(node: Node): boolean {
		if (node.nodeKind !== 'collection') return false;
		try {
			const meta = JSON.parse(node.metadata ?? '{}');
			return meta.board === true && !meta.traits;
		} catch { return false; }
	}

	/** Check if a collection has traits */
	function hasTraits(node: Node): boolean {
		if (node.nodeKind !== 'collection') return false;
		try {
			const meta = JSON.parse(node.metadata ?? '{}');
			return meta.traits && typeof meta.traits === 'object' && Object.keys(meta.traits).length > 0;
		} catch { return false; }
	}

	/** Get trait names from a collection node */
	function traitNames(node: Node): string[] {
		try {
			const meta = JSON.parse(node.metadata ?? '{}');
			if (meta.traits && typeof meta.traits === 'object') return Object.keys(meta.traits);
		} catch { /* ignore */ }
		return [];
	}

	/** Count scheduled events for badge display */
	const scheduledCount = $derived.by(() => {
		if (!connected) return 0;
		return edgeStore.byType('scheduled').length;
	});

	/** Count private diary entries for badge display */
	const diaryCount = $derived.by(() => {
		if (!connected || !nodeId) return 0;
		let count = 0;
		for (const node of nodeStore.all) {
			if (node.createdBy !== nodeId) continue;
			if (node.nodeKind === 'communication' || node.nodeKind === 'agent' ||
				node.nodeKind === 'person' || node.nodeKind === 'team') continue;
			// Check for shared edges (same logic as diary page)
			let shared = false;
			for (const edge of edgeStore.bySource(node.id)) {
				if (edge.system || edge.targetId === node.id || edge.targetId === nodeId) continue;
				shared = true; break;
			}
			if (!shared) {
				for (const edge of edgeStore.byTarget(node.id)) {
					if (edge.system || edge.sourceId === node.id || edge.sourceId === nodeId) continue;
					shared = true; break;
				}
			}
			if (!shared) count++;
		}
		return count;
	});

	let isCreatingBoard = $state(false);

	/** Create a new kanban board */
	async function handleNewBoard() {
		if (!accessToken || !nodeId || isCreatingBoard) return;
		isCreatingBoard = true;
		try {
			const { node_id } = await createNode(accessToken, {
				node_kind: 'collection',
				title: 'Nytt brett',
				visibility: 'hidden',
				metadata: {
					board: true,
					columns: ['todo', 'in_progress', 'done']
				}
			});

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

			window.location.href = `/board/${node_id}`;
		} catch (e) {
			console.error('Feil ved oppretting av brett:', e);
		} finally {
			isCreatingBoard = false;
		}
	}

	let showNewChatDialog = $state(false);

	/** Open the new chat dialog to pick a participant */
	function handleNewChat() {
		showNewChatDialog = true;
	}

	/** Create a 1:1 communication with the selected person */
	async function handleStartChat(personId: string) {
		if (!accessToken || !nodeId) return;
		showNewChatDialog = false;

		try {
			// Check if there's already a communication with this person
			const existingId = findExistingChat(personId);
			if (existingId) {
				window.location.href = `/chat/${existingId}`;
				return;
			}

			// Get the other person's name for the chat title
			const person = nodeStore.get(personId);
			const myNode = nodeStore.get(nodeId);
			const title = [myNode?.title, person?.title].filter(Boolean).join(' & ') || 'Samtale';

			const { node_id } = await createCommunication(accessToken, {
				title,
				participants: [personId],
			});
			window.location.href = `/chat/${node_id}`;
		} catch (e) {
			console.error('Feil ved oppretting av samtale:', e);
		}
	}

	/** Find an existing 1:1 communication between current user and another person */
	function findExistingChat(personId: string): string | undefined {
		if (!nodeId) return undefined;

		// Collect communication nodes where current user is owner or member
		const userComms = new Set<string>();
		for (const edge of edgeStore.bySource(nodeId)) {
			if (edge.edgeType === 'owner' || edge.edgeType === 'member_of') {
				const target = nodeStore.get(edge.targetId);
				if (target?.nodeKind === 'communication') {
					userComms.add(edge.targetId);
				}
			}
		}

		// Check if the other person is also in any of these communications
		for (const edge of edgeStore.bySource(personId)) {
			if (edge.edgeType === 'owner' || edge.edgeType === 'member_of') {
				if (userComms.has(edge.targetId)) {
					return edge.targetId;
				}
			}
		}
		return undefined;
	}

	/** Submit a new node via maskinrommet */
	async function handleCreateNode(data: { title: string; content: string; html: string }) {
		if (!accessToken) throw new Error('Ikke innlogget');

		const metadata: Record<string, unknown> = {};
		if (data.html && data.html !== '<p></p>') {
			metadata.document = data.html;
		}

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

		// Create owner edge so the node appears in the user's mottak
		await createEdge(accessToken, {
			source_id: nodeId!,
			target_id: node_id,
			edge_type: 'owner'
		});
	}
</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-3xl items-center justify-between px-4 py-3">
			<h1 class="text-lg font-semibold text-gray-900">Synops</h1>
			<div class="flex items-center gap-3">
				{#if connected}
					<span class="text-xs text-green-600">Tilkoblet</span>
				{:else if connectionState.current === 'connecting'}
					<span class="text-xs text-yellow-600">Kobler til…</span>
				{:else}
					<span class="text-xs text-gray-400">{connectionState.current}</span>
				{/if}
				{#if $page.data.session?.user}
					<span class="text-sm text-gray-500">{$page.data.session.user.name}</span>
					<button
						onclick={() => signOut()}
						class="rounded bg-gray-100 px-3 py-1 text-xs text-gray-600 hover:bg-gray-200"
					>
						Logg ut
					</button>
				{/if}
			</div>
		</div>
	</header>

	<!-- Main content -->
	<main class="mx-auto max-w-3xl px-4 py-6">
		<div class="mb-4 flex items-center justify-between">
			<h2 class="text-xl font-semibold text-gray-800">Mottak</h2>
			{#if connected && accessToken}
				<div class="flex gap-2">
					<a
						href="/diary"
						class="rounded-lg bg-violet-600 px-3 py-1.5 text-sm font-medium text-white hover:bg-violet-700"
					>
						Dagbok{#if diaryCount > 0} ({diaryCount}){/if}
					</a>
					<a
						href="/calendar"
						class="rounded-lg bg-amber-600 px-3 py-1.5 text-sm font-medium text-white hover:bg-amber-700"
					>
						Kalender{#if scheduledCount > 0} ({scheduledCount}){/if}
					</a>
					<a
						href="/graph"
						class="rounded-lg bg-purple-600 px-3 py-1.5 text-sm font-medium text-white hover:bg-purple-700"
					>
						Graf
					</a>
					<button
						onclick={handleNewBoard}
						disabled={isCreatingBoard}
						class="rounded-lg bg-gray-600 px-3 py-1.5 text-sm font-medium text-white hover:bg-gray-700 disabled:opacity-50"
					>
						{isCreatingBoard ? 'Oppretter…' : 'Nytt brett'}
					</button>
					<button
						onclick={handleNewChat}
						class="rounded-lg bg-blue-600 px-3 py-1.5 text-sm font-medium text-white hover:bg-blue-700"
					>
						Ny samtale
					</button>
				</div>
			{/if}
		</div>

		{#if connected && accessToken}
			<div class="mb-6">
				<NodeEditor onsubmit={handleCreateNode} disabled={!connected} accessToken={accessToken} />
			</div>
		{/if}

		{#if !connected}
			<p class="text-sm text-gray-400">Venter på tilkobling…</p>
		{:else if !nodeId}
			<div class="rounded-lg border border-yellow-200 bg-yellow-50 p-4 text-sm text-yellow-800">
				<p class="font-medium">Bruker-ID ikke tilgjengelig</p>
				<p class="mt-1">
					Kunne ikke hente node-ID fra maskinrommet. Prøv å logge ut og inn igjen.
				</p>
			</div>
		{:else if mottaksnoder.length === 0}
			<p class="text-sm text-gray-400">Ingen noder å vise ennå.</p>
		{:else}
			<ul class="space-y-2">
				{#each mottaksnoder as node (node.id)}
					{@const vis = nodeVisibility(node, nodeId)}
					{#if hasTraits(node)}
						<li class="rounded-lg border border-gray-200 bg-white shadow-sm transition-colors hover:border-indigo-300">
							<a href="/collection/{node.id}" class="block p-4">
								<div class="flex items-start justify-between gap-2">
									<div class="min-w-0 flex-1">
										<h3 class="font-medium text-gray-900">
											{node.title || 'Uten tittel'}
											<span class="ml-1 text-xs text-indigo-500">→ Åpne samling</span>
										</h3>
										{#if vis === 'full' && node.content}
											<p class="mt-1 text-sm text-gray-500">
												{excerpt(node.content)}
											</p>
										{/if}
										<div class="mt-1.5 flex flex-wrap gap-1">
											{#each traitNames(node) as trait (trait)}
												<span class="rounded-full bg-indigo-50 px-2 py-0.5 text-[10px] font-medium text-indigo-600">
													{trait}
												</span>
											{/each}
										</div>
									</div>
									<div class="flex shrink-0 flex-col items-end gap-1">
										<span class="rounded-full bg-indigo-100 px-2 py-0.5 text-xs text-indigo-700">
											Samling
										</span>
									</div>
								</div>
							</a>
						</li>
					{:else if isBoard(node)}
						<li class="rounded-lg border border-gray-200 bg-white shadow-sm transition-colors hover:border-gray-400">
							<a href="/board/{node.id}" class="block p-4">
								<div class="flex items-start justify-between gap-2">
									<div class="min-w-0 flex-1">
										<h3 class="font-medium text-gray-900">
											{node.title || 'Uten tittel'}
											<span class="ml-1 text-xs text-gray-500">→ Åpne brett</span>
										</h3>
										{#if vis === 'full' && node.content}
											<p class="mt-1 text-sm text-gray-500">
												{excerpt(node.content)}
											</p>
										{/if}
									</div>
									<div class="flex shrink-0 flex-col items-end gap-1">
										<span class="rounded-full bg-gray-100 px-2 py-0.5 text-xs text-gray-500">
											Brett
										</span>
									</div>
								</div>
							</a>
						</li>
					{:else if node.nodeKind === 'communication'}
						<li class="rounded-lg border border-gray-200 bg-white shadow-sm transition-colors hover:border-blue-300">
							<a href="/chat/{node.id}" class="block p-4">
								<div class="flex items-start justify-between gap-2">
									<div class="min-w-0 flex-1">
										<h3 class="font-medium text-gray-900">
											{node.title || 'Uten tittel'}
											<span class="ml-1 text-xs text-blue-500">→ Åpne chat</span>
										</h3>
										{#if vis === 'full' && node.content}
											<p class="mt-1 text-sm text-gray-500">
												{excerpt(node.content)}
											</p>
										{/if}
									</div>
									<div class="flex shrink-0 flex-col items-end gap-1">
										<span class="rounded-full bg-gray-100 px-2 py-0.5 text-xs text-gray-500">
											{kindLabel(node.nodeKind)}
										</span>
										{#each edgeTypes(node.id) as et}
											<span class="rounded-full bg-blue-50 px-2 py-0.5 text-xs text-blue-600">
												{et}
											</span>
										{/each}
									</div>
								</div>
							</a>
						</li>
					{:else}
						{@const audio = audioMeta(node)}
						<li class="rounded-lg border border-gray-200 bg-white p-4 shadow-sm">
							<div class="flex items-start justify-between gap-2">
								<div class="min-w-0 flex-1">
									<h3 class="font-medium text-gray-900">
										{node.title || 'Uten tittel'}
									</h3>
									{#if audio}
										<div class="mt-2">
											<AudioPlayer
												src={audio.src}
												duration={audio.duration}
												transcript={vis === 'full' && !audio.hasSegments ? (node.content || undefined) : undefined}
												nodeId={audio.hasSegments ? node.id : undefined}
												accessToken={audio.hasSegments ? accessToken : undefined}
											/>
										</div>
									{:else if vis === 'full' && node.content}
										<p class="mt-1 text-sm text-gray-500">
											{excerpt(node.content)}
										</p>
									{:else if vis === 'discoverable'}
										<p class="mt-1 text-xs italic text-gray-400">
											Begrenset tilgang — be om tilgang for å se innholdet
										</p>
									{/if}
								</div>
								<div class="flex shrink-0 flex-col items-end gap-1">
									<span class="rounded-full bg-gray-100 px-2 py-0.5 text-xs text-gray-500">
										{kindLabel(node.nodeKind)}
									</span>
									{#each edgeTypes(node.id) as et}
										<span class="rounded-full bg-blue-50 px-2 py-0.5 text-xs text-blue-600">
											{et}
										</span>
									{/each}
								</div>
							</div>
						</li>
					{/if}
				{/each}
			</ul>
		{/if}

		<!-- Debug info (small, bottom) -->
		{#if connected}
			<p class="mt-8 text-xs text-gray-300">
				{nodeStore.count} noder · {edgeStore.count} edges · {nodeAccessStore.count} access
				{#if nodeId}· node: {nodeId.slice(0, 8)}…{/if}
			</p>
		{/if}
	</main>

	{#if showNewChatDialog && nodeId}
		<NewChatDialog
			currentUserId={nodeId}
			onselect={handleStartChat}
			onclose={() => showNewChatDialog = false}
		/>
	{/if}
</div>
