<!--
  Container log viewer with tail line limit and auto-scroll.

  Workload-scoped only after the hard cutover. The legacy `instance` and
  `site` source variants targeted /api/projects/.../logs and
  /api/sites/.../logs respectively — both gone with their routes.
-->
<script lang="ts">
	import { onDestroy } from 'svelte';
	import { fetchWorkloadContainerLogs } from '$lib/api';
	import { getAuthToken } from '$lib/auth';
	import { t } from '$lib/i18n';
	import { IconLoader, IconX } from '$lib/components/icons';

	export type LogSource = {
		kind: 'workload';
		workloadId: string;
		containerRowId: string;
	};

	interface Props {
		source: LogSource;
		onclose: () => void;
	}

	const { source, onclose }: Props = $props();

	let lines = $state<string[]>([]);
	let loading = $state(true);
	let error = $state('');
	let tailCount = $state(200);
	let following = $state(false);
	let logContainer: HTMLDivElement | undefined = $state();
	let eventSource: EventSource | null = null;

	// Batch incoming SSE log lines to avoid per-line re-renders.
	let pendingLines: string[] = [];
	let flushTimer: ReturnType<typeof setTimeout> | null = null;

	function flushPendingLines() {
		flushTimer = null;
		if (pendingLines.length === 0) return;
		let updated = [...lines, ...pendingLines];
		pendingLines = [];
		if (updated.length > tailCount * 2) {
			updated = updated.slice(-tailCount);
		}
		lines = updated;
		scrollToBottom();
	}

	function enqueueLine(line: string) {
		pendingLines.push(line);
		if (!flushTimer) {
			flushTimer = setTimeout(flushPendingLines, 150);
		}
	}

	function buildFollowUrl(token: string | null): string {
		const tokenParam = token ? `&token=${token}` : '';
		return `/api/workloads/${source.workloadId}/containers/${source.containerRowId}/logs?follow=true&tail=0${tokenParam}`;
	}

	async function fetchLogs(tail: number): Promise<string[]> {
		return fetchWorkloadContainerLogs(source.workloadId, source.containerRowId, tail);
	}

	async function loadLogs() {
		loading = true;
		error = '';
		try {
			lines = await fetchLogs(tailCount);
		} catch (err) {
			error = err instanceof Error ? err.message : 'Failed to load logs';
		} finally {
			loading = false;
			scrollToBottom();
		}
	}

	function startFollowing() {
		if (eventSource) return;
		following = true;
		const token = getAuthToken();
		eventSource = new EventSource(buildFollowUrl(token));

		eventSource.onmessage = (e) => {
			try {
				const data = JSON.parse(e.data);
				if (data.line) {
					enqueueLine(data.line);
				}
			} catch { /* ignore parse errors */ }
		};

		eventSource.onerror = () => {
			stopFollowing();
		};
	}

	function stopFollowing() {
		if (eventSource) {
			eventSource.close();
			eventSource = null;
		}
		if (flushTimer) { clearTimeout(flushTimer); flushTimer = null; }
		flushPendingLines();
		following = false;
	}

	function scrollToBottom() {
		requestAnimationFrame(() => {
			if (logContainer) {
				logContainer.scrollTop = logContainer.scrollHeight;
			}
		});
	}

	function handleTailChange(e: Event) {
		const value = (e.target as HTMLSelectElement).value;
		tailCount = parseInt(value, 10);
		stopFollowing();
		loadLogs();
	}

	$effect(() => { loadLogs(); });

	onDestroy(() => {
		stopFollowing();
		if (flushTimer) { clearTimeout(flushTimer); flushTimer = null; }
	});
</script>

<div class="rounded-xl border border-[var(--border-primary)] bg-[var(--surface-page)] shadow-lg">
	<!-- Header -->
	<div class="flex items-center justify-between border-b border-[var(--border-primary)] px-4 py-2.5">
		<div class="flex items-center gap-3">
			<h3 class="text-sm font-semibold text-[var(--text-primary)]">{$t('logs.title')}</h3>
			<select
				value={String(tailCount)}
				onchange={handleTailChange}
				class="rounded border border-[var(--border-input)] bg-[var(--surface-input)] px-2 py-0.5 text-xs text-[var(--text-secondary)] focus:outline-none"
			>
				<option value="50">50 {$t('logs.lines')}</option>
				<option value="200">200 {$t('logs.lines')}</option>
				<option value="500">500 {$t('logs.lines')}</option>
				<option value="1000">1000 {$t('logs.lines')}</option>
			</select>
			<button
				type="button"
				class="rounded px-2 py-0.5 text-xs font-medium transition-colors {following
					? 'bg-emerald-600 text-white'
					: 'border border-[var(--border-input)] text-[var(--text-secondary)] hover:bg-[var(--surface-card-hover)]'}"
				onclick={() => following ? stopFollowing() : startFollowing()}
			>
				{following ? $t('logs.following') : $t('logs.follow')}
			</button>
		</div>
		<button
			type="button"
			onclick={onclose}
			class="rounded p-1 text-[var(--text-tertiary)] hover:text-[var(--text-primary)] transition-colors"
		>
			<IconX size={16} />
		</button>
	</div>

	<!-- Log content -->
	<div
		bind:this={logContainer}
		class="h-80 overflow-auto bg-gray-950 p-3 font-mono text-xs leading-relaxed text-gray-300"
	>
		{#if loading}
			<div class="flex items-center gap-2 text-gray-500">
				<IconLoader size={14} />
				{$t('logs.loading')}
			</div>
		{:else if error}
			<p class="text-red-400">{error}</p>
		{:else if lines.length === 0}
			<p class="text-gray-500">{$t('logs.noLogs')}</p>
		{:else}
			{#each lines as line}
				<div class="hover:bg-gray-900/50 px-1 -mx-1 rounded whitespace-pre-wrap break-all">{line}</div>
			{/each}
		{/if}
	</div>
</div>
