<!--
  WebhookDeliveryLog

  Recent inbound webhook activity panel. Mounted on the Trigger detail
  page (the project + site detail pages it once served were dropped in
  the hard legacy cutover). Polls the audit table every 30s so users can
  debug "why didn't my deploy fire?" without grepping daemon logs.
-->
<script lang="ts">
	import { onMount, onDestroy } from 'svelte';
	import type { WebhookDelivery } from '$lib/api';
	import { t } from '$lib/i18n';
	import { fmt } from '$lib/format/datetime';
	import { IconRefresh, IconLoader } from '$lib/components/icons';

	interface Props {
		fetchDeliveries: (signal?: AbortSignal) => Promise<WebhookDelivery[]>;
	}

	const { fetchDeliveries }: Props = $props();

	let deliveries = $state<WebhookDelivery[]>([]);
	let loading = $state(true);
	let refreshing = $state(false);
	let error = $state('');

	let controller = new AbortController();
	let interval: ReturnType<typeof setInterval> | null = null;

	async function load(refresh = false) {
		controller.abort();
		controller = new AbortController();
		if (refresh) {
			refreshing = true;
		}
		try {
			deliveries = (await fetchDeliveries(controller.signal)) ?? [];
			error = '';
		} catch (e) {
			if (e instanceof DOMException && e.name === 'AbortError') return;
			error = e instanceof Error ? e.message : $t('webhookLog.loadFailed');
		} finally {
			loading = false;
			refreshing = false;
		}
	}

	function outcomeBadge(outcome: string): string {
		switch (outcome) {
			case 'deploy':
				return 'badge-success';
			case 'rejected':
			case 'error':
				return 'badge-danger';
			case 'bad_request':
			case 'not_found':
				return 'badge-warning';
			default:
				return 'badge-neutral';
		}
	}

	function signatureBadge(state: string): string {
		switch (state) {
			case 'valid':
				return 'badge-success';
			case 'invalid':
				return 'badge-danger';
			case 'missing':
				return 'badge-warning';
			default:
				return 'badge-neutral';
		}
	}

	/** Backend returns naive (no-offset) timestamps — treat as UTC. */
	function toUtcIso(s: string): string {
		if (!s) return '';
		return /Z|[+-]\d{2}:?\d{2}$/.test(s) ? s : s.replace(' ', 'T') + 'Z';
	}

	onMount(() => {
		load();
		interval = setInterval(() => load(), 30_000);
	});

	onDestroy(() => {
		controller.abort();
		if (interval) clearInterval(interval);
	});
</script>

<div class="rounded-xl border border-[var(--border-primary)] bg-[var(--surface-card)] p-6 shadow-[var(--shadow-sm)]">
	<div class="mb-4 flex items-center justify-between gap-3">
		<div>
			<h2 class="text-lg font-semibold text-[var(--text-primary)]">{$t('webhookLog.title')}</h2>
			<p class="mt-1 text-sm text-[var(--text-secondary)]">{$t('webhookLog.description')}</p>
		</div>
		<button
			type="button"
			onclick={() => load(true)}
			disabled={refreshing || loading}
			class="inline-flex items-center gap-1.5 rounded-lg border border-[var(--border-primary)] px-3 py-1.5 text-sm text-[var(--text-secondary)] hover:bg-[var(--surface-card-hover)] transition-colors disabled:opacity-50"
		>
			{#if refreshing}<IconLoader size={14} />{:else}<IconRefresh size={14} />{/if}
			<span>{$t('webhookLog.refresh')}</span>
		</button>
	</div>

	{#if loading}
		<div class="space-y-2">
			<div class="h-10 rounded-lg bg-[var(--surface-card-hover)]"></div>
			<div class="h-10 rounded-lg bg-[var(--surface-card-hover)]"></div>
			<div class="h-10 rounded-lg bg-[var(--surface-card-hover)]"></div>
		</div>
	{:else if error}
		<p class="text-sm text-[var(--color-danger)]">{error}</p>
	{:else if deliveries.length === 0}
		<p class="text-sm italic text-[var(--text-tertiary)]">{$t('webhookLog.empty')}</p>
	{:else}
		<div class="overflow-x-auto">
			<table class="w-full text-sm">
				<thead>
					<tr class="border-b border-[var(--border-primary)] bg-[var(--surface-card-hover)] text-xs uppercase tracking-wide text-[var(--text-secondary)]">
						<th class="px-3 py-2 text-left">{$t('webhookLog.colTime')}</th>
						<th class="px-3 py-2 text-left">{$t('webhookLog.colStatus')}</th>
						<th class="px-3 py-2 text-left">{$t('webhookLog.colOutcome')}</th>
						<th class="px-3 py-2 text-left">{$t('webhookLog.colSignature')}</th>
						<th class="px-3 py-2 text-left">{$t('webhookLog.colDetail')}</th>
						<th class="px-3 py-2 text-left">{$t('webhookLog.colSource')}</th>
					</tr>
				</thead>
				<tbody>
					{#each deliveries as d (d.id)}
						<tr class="border-b border-[var(--border-primary)] last:border-b-0 hover:bg-[var(--surface-card-hover)] transition-colors">
							<td class="px-3 py-2 whitespace-nowrap text-[var(--text-secondary)]" title={$fmt.dateTime(toUtcIso(d.received_at))}>
								{$fmt.relative(toUtcIso(d.received_at))}
							</td>
							<td class="px-3 py-2 tabular-nums">
								<span class="font-mono {d.status_code >= 500 ? 'text-[var(--color-danger)]' : d.status_code >= 400 ? 'text-amber-600 dark:text-amber-400' : 'text-emerald-600 dark:text-emerald-400'}">
									{d.status_code}
								</span>
							</td>
							<td class="px-3 py-2">
								<span class="inline-flex items-center rounded-full px-2 py-0.5 text-xs font-medium {outcomeBadge(d.outcome)}">
									{$t(`webhookLog.outcome.${d.outcome}`) || d.outcome}
								</span>
							</td>
							<td class="px-3 py-2">
								<span class="inline-flex items-center rounded-full px-2 py-0.5 text-xs font-medium {signatureBadge(d.signature_state)}">
									{$t(`webhookLog.sig.${d.signature_state}`) || d.signature_state}
								</span>
							</td>
							<td class="px-3 py-2 text-[var(--text-secondary)]">
								<span class="truncate" title={d.detail}>{d.detail}</span>
							</td>
							<td class="px-3 py-2 font-mono text-xs text-[var(--text-tertiary)]">
								{d.source_ip}
							</td>
						</tr>
					{/each}
				</tbody>
			</table>
		</div>
	{/if}
</div>
