<script lang="ts">
	import type { Node } from '$lib/realtime';
	import { edgeStore, nodeStore, nodeVisibility } from '$lib/realtime';
	import { createNode, casUrl } from '$lib/api';
	import { setDragPayload, checkChatCompat, type DragPayload } from '$lib/transfer';
	import type { BlockReceiver, PlacementIntent } from '$lib/components/blockshell/types';
	import ChatInput from '$lib/components/ChatInput.svelte';
	import AudioPlayer from '$lib/components/AudioPlayer.svelte';
	import { tick } from 'svelte';

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

	/**
	 * BlockReceiver implementation for Chat.
	 * Accepts any node — wraps as message with mentions.
	 * Ref: docs/retninger/arbeidsflaten.md § Kompatibilitetsmatrise
	 */
	export const receiver: BlockReceiver = {
		canReceive(payload: DragPayload) {
			return checkChatCompat(payload);
		},
		receive(payload: DragPayload) {
			const intent: PlacementIntent = {
				mode: 'lettvekts-triage',
				contextId: activeChannelId ?? collection?.id ?? '',
				contextType: 'chat',
			};
			onReceiveDrop?.(payload, intent);
			return intent;
		}
	};

	// =========================================================================
	// Channel list — communication nodes linked to this collection
	// =========================================================================

	const chatNodes = $derived.by(() => {
		const nodes: Node[] = [];
		if (!collection) return nodes;
		const seen = new Set<string>();
		for (const edge of edgeStore.byTarget(collection.id)) {
			if (edge.edgeType !== 'belongs_to') continue;
			const node = nodeStore.get(edge.sourceId);
			if (node && node.nodeKind === 'communication' && !seen.has(node.id)) {
				seen.add(node.id);
				nodes.push(node);
			}
		}
		for (const edge of edgeStore.bySource(collection.id)) {
			if (edge.edgeType !== 'has_channel') continue;
			const node = nodeStore.get(edge.targetId);
			if (node && node.nodeKind === 'communication' && !seen.has(node.id)) {
				seen.add(node.id);
				nodes.push(node);
			}
		}
		return nodes;
	});

	// =========================================================================
	// Active channel — auto-select single channel, otherwise user picks
	// =========================================================================

	let selectedChannelId = $state<string | null>(null);

	/** Auto-select when there's exactly one channel */
	const activeChannelId = $derived(
		chatNodes.length === 1
			? chatNodes[0].id
			: selectedChannelId
	);

	const activeChannel = $derived(
		activeChannelId ? nodeStore.get(activeChannelId) : undefined
	);

	function selectChannel(id: string) {
		selectedChannelId = id;
	}

	function backToList() {
		selectedChannelId = null;
	}

	// =========================================================================
	// Messages for the active channel
	// =========================================================================

	const messages = $derived.by(() => {
		if (!activeChannelId) return [];

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

		// Text messages: belongs_to edges pointing to this communication node
		for (const edge of edgeStore.byTarget(activeChannelId)) {
			if (edge.edgeType !== 'belongs_to') continue;
			const node = nodeStore.get(edge.sourceId);
			if (node && nodeVisibility(node, userId) !== 'hidden' && !seen.has(node.id)) {
				seen.add(node.id);
				nodes.push(node);
			}
		}

		// Media nodes: has_media edges
		for (const edge of edgeStore.bySource(activeChannelId)) {
			if (edge.edgeType !== 'has_media') continue;
			const node = nodeStore.get(edge.targetId);
			if (node && nodeVisibility(node, userId) !== 'hidden' && !seen.has(node.id)) {
				seen.add(node.id);
				nodes.push(node);
			}
		}

		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 (for channel title)
	// =========================================================================

	const participants = $derived.by(() => {
		if (!activeChannelId) return [];
		const edges = edgeStore.byTarget(activeChannelId)
			.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;
	});

	const chatTitle = $derived.by(() => {
		if (participants.length === 2 && userId) {
			const other = participants.find(p => p.id !== userId);
			if (other?.title) return other.title;
		}
		return activeChannel?.title || 'Samtale';
	});

	// =========================================================================
	// Auto-scroll
	// =========================================================================

	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 message
	// =========================================================================

	async function handleSendMessage(content: string) {
		if (!accessToken || !activeChannelId) throw new Error('Ikke innlogget');
		await createNode(accessToken, {
			node_kind: 'content',
			content,
			visibility: 'hidden',
			context_id: activeChannelId,
		});
	}

	// =========================================================================
	// Helpers
	// =========================================================================

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

	function senderName(node: Node): string {
		if (!node.createdBy) return 'Ukjent';
		const sender = nodeStore.get(node.createdBy);
		return sender?.title || sender?.nodeKind || 'Ukjent';
	}

	function isAgentMessage(node: Node): boolean {
		if (!node.createdBy) return false;
		const sender = nodeStore.get(node.createdBy);
		return sender?.nodeKind === 'agent';
	}

	function isOwnMessage(node: Node): boolean {
		return !!userId && node.createdBy === userId;
	}

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

	function audioSrc(node: Node): string {
		try {
			const meta = JSON.parse(node.metadata ?? '{}');
			return casUrl(meta.cas_hash);
		} catch { return ''; }
	}

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

	function hasSegments(node: Node): boolean {
		try {
			const meta = JSON.parse(node.metadata ?? '{}');
			return (meta.transcription?.segment_count ?? 0) > 0;
		} catch { return false; }
	}

	function handleDragStart(e: DragEvent, node: Node) {
		if (!e.dataTransfer) return;
		setDragPayload(e.dataTransfer, {
			nodeId: node.id,
			nodeKind: node.nodeKind,
			sourcePanel: 'chat'
		});
	}
</script>

<!--
	ChatTrait — fullverdig BlockShell-panel for chat.
	Viser kanalliste eller inline chat avhengig av kontekst.
	Forelder (collection page) wrapper dette i BlockShell.
-->

<div class="chat-trait">
	{#if chatNodes.length === 0}
		<!-- No channels -->
		<div class="chat-empty">
			<span class="chat-empty-icon">💬</span>
			<p>Ingen samtaler knyttet til denne samlingen.</p>
		</div>
	{:else if !activeChannelId}
		<!-- Channel list (multiple channels, none selected) -->
		<div class="chat-channel-list">
			<div class="chat-channel-header">Velg samtale</div>
			{#each chatNodes as node (node.id)}
				<button
					class="chat-channel-item"
					onclick={() => selectChannel(node.id)}
					draggable="true"
					ondragstart={(e) => handleDragStart(e, node)}
				>
					<span class="chat-channel-icon">💬</span>
					<span class="chat-channel-name">{node.title || 'Samtale'}</span>
				</button>
			{/each}
		</div>
	{:else}
		<!-- Inline chat view -->
		<div class="chat-view">
			<!-- Channel sub-header (only if multiple channels, to allow going back) -->
			{#if chatNodes.length > 1}
				<div class="chat-subheader">
					<button class="chat-back-btn" onclick={backToList} aria-label="Tilbake til kanalliste">
						<svg class="chat-back-icon" 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>
					</button>
					<span class="chat-subheader-title">{chatTitle}</span>
					{#if participants.length > 0}
						<span class="chat-subheader-participants">
							{participants.map(p => p.title || 'Ukjent').join(', ')}
						</span>
					{/if}
				</div>
			{/if}

			<!-- Messages -->
			<div class="chat-messages" bind:this={messagesContainer}>
				{#if messages.length === 0}
					<p class="chat-messages-empty">Ingen meldinger ennå. Skriv den første!</p>
				{:else}
					{#each messages as msg (msg.id)}
						{@const own = isOwnMessage(msg)}
						{@const audio = isAudioNode(msg)}
						{@const bot = isAgentMessage(msg)}
						<div class="chat-msg {own ? 'chat-msg-own' : 'chat-msg-other'}">
							<div
								class="chat-bubble {own ? (audio ? 'chat-bubble-own-audio' : 'chat-bubble-own') : bot ? 'chat-bubble-bot' : 'chat-bubble-other'}"
								draggable="true"
								ondragstart={(e) => handleDragStart(e, msg)}
							>
								{#if !own}
									<p class="chat-sender {bot ? 'chat-sender-bot' : ''}">
										{#if bot}<span title="AI-agent">🤖 </span>{/if}{senderName(msg)}
									</p>
								{/if}
								{#if audio}
									<div class="chat-audio-badge">
										<svg class="chat-audio-icon" 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>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}
									<p class="chat-text">{msg.content || ''}</p>
								{/if}
								<p class="chat-time {own && !audio ? 'chat-time-own' : ''}">{formatTime(msg)}</p>
							</div>
						</div>
					{/each}
				{/if}
			</div>

			<!-- Input -->
			{#if accessToken && activeChannelId}
				<div class="chat-input-area">
					<ChatInput onsubmit={handleSendMessage} {accessToken} contextId={activeChannelId} />
				</div>
			{/if}
		</div>
	{/if}
</div>

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

	/* ================================================================= */
	/* Empty state                                                        */
	/* ================================================================= */
	.chat-empty {
		display: flex;
		flex-direction: column;
		align-items: center;
		justify-content: center;
		flex: 1;
		padding: 24px;
		color: var(--color-text-dim);
		font-size: 13px;
		text-align: center;
		gap: 8px;
	}

	.chat-empty-icon {
		font-size: 28px;
	}

	/* ================================================================= */
	/* Channel list (multiple channels)                                   */
	/* ================================================================= */
	.chat-channel-list {
		display: flex;
		flex-direction: column;
		flex: 1;
		overflow-y: auto;
	}

	.chat-channel-header {
		padding: 8px 12px;
		font-size: 11px;
		font-weight: 600;
		text-transform: uppercase;
		letter-spacing: 0.05em;
		color: var(--color-text-dim);
	}

	.chat-channel-item {
		display: flex;
		align-items: center;
		gap: 8px;
		padding: 10px 12px;
		border: none;
		background: transparent;
		cursor: pointer;
		font-size: 13px;
		color: var(--color-text-muted);
		text-align: left;
		transition: background 0.1s;
	}

	.chat-channel-item:hover {
		background: var(--color-surface-hover);
	}

	.chat-channel-icon {
		font-size: 16px;
		flex-shrink: 0;
	}

	.chat-channel-name {
		min-width: 0;
		overflow: hidden;
		text-overflow: ellipsis;
		white-space: nowrap;
	}

	/* ================================================================= */
	/* Chat view (messages + input)                                       */
	/* ================================================================= */
	.chat-view {
		display: flex;
		flex-direction: column;
		flex: 1;
		min-height: 0;
		overflow: hidden;
	}

	/* Sub-header (back button + channel name) */
	.chat-subheader {
		display: flex;
		align-items: center;
		gap: 6px;
		padding: 6px 8px;
		border-bottom: 1px solid var(--color-border);
		flex-shrink: 0;
		min-height: 32px;
	}

	.chat-back-btn {
		display: flex;
		align-items: center;
		justify-content: center;
		width: 24px;
		height: 24px;
		border: none;
		background: transparent;
		border-radius: 4px;
		cursor: pointer;
		color: var(--color-text-muted);
		flex-shrink: 0;
	}

	.chat-back-btn:hover {
		background: var(--color-surface-hover);
		color: var(--color-text-muted);
	}

	.chat-back-icon {
		width: 14px;
		height: 14px;
	}

	.chat-subheader-title {
		font-size: 13px;
		font-weight: 600;
		color: var(--color-text-muted);
		min-width: 0;
		overflow: hidden;
		text-overflow: ellipsis;
		white-space: nowrap;
	}

	.chat-subheader-participants {
		font-size: 11px;
		color: var(--color-text-dim);
		margin-left: auto;
		white-space: nowrap;
		overflow: hidden;
		text-overflow: ellipsis;
		flex-shrink: 1;
		min-width: 0;
	}

	/* Messages area */
	.chat-messages {
		flex: 1;
		overflow-y: auto;
		padding: 8px;
		display: flex;
		flex-direction: column;
		gap: 4px;
		min-height: 0;
	}

	.chat-messages-empty {
		text-align: center;
		font-size: 12px;
		color: var(--color-text-dim);
		padding: 24px 8px;
		margin: auto 0;
	}

	/* Message row */
	.chat-msg {
		display: flex;
	}

	.chat-msg-own {
		justify-content: flex-end;
	}

	.chat-msg-other {
		justify-content: flex-start;
	}

	/* Bubbles */
	.chat-bubble {
		max-width: 85%;
		border-radius: 12px;
		padding: 6px 10px;
		cursor: grab;
	}

	.chat-bubble:active {
		cursor: grabbing;
	}

	.chat-bubble-own {
		background: var(--color-accent);
		color: white;
	}

	.chat-bubble-own-audio {
		background: var(--color-accent-glow);
		border: 1px solid var(--color-accent);
		color: var(--color-text);
	}

	.chat-bubble-other {
		background: var(--color-surface);
		border: 1px solid var(--color-border);
		color: var(--color-text);
	}

	.chat-bubble-bot {
		background: var(--color-surface);
		border: 1px solid var(--color-border);
		color: var(--color-text);
	}

	.chat-sender {
		font-size: 11px;
		font-weight: 600;
		color: var(--color-accent);
		margin-bottom: 1px;
	}

	.chat-sender-bot {
		color: var(--color-warning);
	}

	.chat-text {
		font-size: 13px;
		white-space: pre-wrap;
		word-break: break-word;
		line-height: 1.4;
		margin: 0;
	}

	.chat-time {
		font-size: 10px;
		color: var(--color-text-dim);
		text-align: right;
		margin-top: 2px;
	}

	.chat-time-own {
		color: rgba(255, 255, 255, 0.6);
	}

	/* Audio badge */
	.chat-audio-badge {
		display: flex;
		align-items: center;
		gap: 4px;
		margin-bottom: 4px;
		font-size: 11px;
		font-weight: 500;
		color: var(--color-accent);
	}

	.chat-audio-icon {
		width: 12px;
		height: 12px;
		flex-shrink: 0;
	}

	/* Input area */
	.chat-input-area {
		flex-shrink: 0;
		padding: 6px 8px;
		border-top: 1px solid var(--color-border);
		background: var(--color-bg);
	}

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

	/* Small panels: tighter spacing */
	@container (max-width: 320px) {
		.chat-bubble {
			max-width: 92%;
			padding: 5px 8px;
		}

		.chat-text {
			font-size: 12px;
		}

		.chat-input-area {
			padding: 4px 6px;
		}
	}

	/* Mobile viewport fallback */
	@media (max-width: 480px) {
		.chat-bubble {
			max-width: 90%;
		}

		.chat-messages {
			padding: 6px;
		}

		.chat-input-area {
			padding: 6px;
		}
	}
</style>
