<script lang="ts">
	import { page } from '$app/state';
	import { api, ApiError, type DeadLetterRow } from '$lib/api';

	let slug = $derived(page.params.slug ?? '');
	let rows = $state<DeadLetterRow[]>([]);
	let unresolved = $state<number>(0);
	let loading = $state(true);
	let error = $state<string | null>(null);
	let unresolvedOnly = $state(true);
	let expandedId = $state<string | null>(null);

	// The parent layout (apps/[slug]/+layout.svelte) already fetches the
	// app + drives the historical-slug redirect. This page only needs the
	// DL count and the row list — no duplicate api.apps.get().
	async function load() {
		loading = true;
		error = null;
		try {
			const c = await api.deadLetters.count(slug);
			unresolved = c.unresolved;
			const r = await api.deadLetters.list(slug, { unresolved: unresolvedOnly, limit: 100 });
			rows = r.dead_letters;
		} catch (e) {
			error = e instanceof ApiError ? e.message : String(e);
		} finally {
			loading = false;
		}
	}

	$effect(() => {
		// Re-load whenever the slug or filter changes.
		void slug;
		void unresolvedOnly;
		void load();
	});

	async function replay(dlId: string) {
		try {
			await api.deadLetters.replay(slug, dlId);
			await load();
		} catch (e) {
			error = e instanceof ApiError ? e.message : String(e);
		}
	}

	async function markIgnored(dlId: string) {
		try {
			await api.deadLetters.resolve(slug, dlId, 'ignored');
			await load();
		} catch (e) {
			error = e instanceof ApiError ? e.message : String(e);
		}
	}

	function toggleExpanded(id: string) {
		expandedId = expandedId === id ? null : id;
	}

	function fmtTime(iso: string): string {
		return new Date(iso).toLocaleString();
	}

	function truncate(s: string, n: number): string {
		if (s.length <= n) return s;
		return s.slice(0, n) + '…';
	}
</script>

<svelte:head>
	<title>Dead letters · {slug} · PiCloud</title>
</svelte:head>

<div class="panel">
	<header class="panel-head">
		<div>
			<h2>Dead letters</h2>
			<p class="subtitle">
				{#if unresolved > 0}
					<strong class="badge">{unresolved}</strong> unresolved
				{:else}
					No unresolved dead letters
				{/if}
			</p>
		</div>
		<div class="controls">
			<label>
				<input type="checkbox" bind:checked={unresolvedOnly} />
				Show unresolved only
			</label>
			<button class="secondary" onclick={load} disabled={loading}>Refresh</button>
		</div>
	</header>

	{#if error}
		<div class="error">{error}</div>
	{/if}

	{#if loading}
		<p>Loading…</p>
	{:else if rows.length === 0}
		<p class="empty">
			{#if unresolvedOnly}
				No unresolved dead letters for this app. 🎉
			{:else}
				No dead letters recorded yet.
			{/if}
		</p>
	{:else}
		<table>
			<thead>
				<tr>
					<th>Created</th>
					<th>Source</th>
					<th>Op</th>
					<th>Script</th>
					<th>Attempts</th>
					<th>First / Last attempt</th>
					<th>Last error</th>
					<th>Actions</th>
				</tr>
			</thead>
			<tbody>
				{#each rows as row (row.id)}
					<tr class:resolved={row.resolved_at !== null}>
						<td>{fmtTime(row.created_at)}</td>
						<td><code>{row.source}</code></td>
						<td><code>{row.op}</code></td>
						<td>{row.script_id ? row.script_id.slice(0, 8) : '—'}</td>
						<td>{row.attempt_count}</td>
						<td class="times">
							<div>{fmtTime(row.first_attempt_at)}</div>
							<div>{fmtTime(row.last_attempt_at)}</div>
						</td>
						<td class="err">
							<button class="link" onclick={() => toggleExpanded(row.id)}>
								{truncate(row.last_error, 60)}
							</button>
						</td>
						<td class="actions">
							{#if row.resolved_at === null}
								<button onclick={() => replay(row.id)}>Replay</button>
								<button class="secondary" onclick={() => markIgnored(row.id)}>
									Mark resolved
								</button>
							{:else}
								<span class="resolution">{row.resolution ?? 'resolved'}</span>
							{/if}
						</td>
					</tr>
					{#if expandedId === row.id}
						<tr class="detail">
							<td colspan="8">
								<div class="detail-grid">
									<section>
										<h3>Payload</h3>
										<pre>{JSON.stringify(row.payload, null, 2)}</pre>
									</section>
									<section>
										<h3>Last error</h3>
										<pre>{row.last_error}</pre>
									</section>
								</div>
							</td>
						</tr>
					{/if}
				{/each}
			</tbody>
		</table>
	{/if}
</div>

<style>
	.panel-head {
		display: flex;
		justify-content: space-between;
		align-items: flex-start;
		margin-bottom: 1rem;
		gap: 1rem;
	}
	h2 {
		margin: 0.25rem 0;
		font-size: 1.125rem;
	}
	.subtitle {
		color: var(--text-muted);
		margin: 0;
	}
	.badge {
		display: inline-block;
		min-width: 1.5rem;
		padding: 0.1rem 0.4rem;
		background: var(--color-danger-bg);
		color: var(--color-danger-fg);
		border-radius: var(--radius-pill);
		text-align: center;
		font-weight: 600;
	}
	.controls {
		display: flex;
		gap: 0.75rem;
		align-items: center;
	}
	.controls label {
		display: inline-flex;
		gap: 0.4rem;
		align-items: center;
		font-size: 0.85rem;
		color: var(--text-muted);
	}
	.error {
		background: var(--color-danger-bg);
		border: 1px solid var(--color-danger-border);
		color: var(--color-danger-fg);
		padding: 0.75rem 1rem;
		border-radius: var(--radius-md);
		margin-bottom: 1rem;
	}
	.empty {
		color: var(--text-muted);
		text-align: center;
		padding: 2rem;
	}
	table {
		width: 100%;
		border-collapse: collapse;
		font-size: 0.9rem;
	}
	th,
	td {
		text-align: left;
		padding: 0.5rem 0.75rem;
		border-bottom: 1px solid var(--border);
		vertical-align: top;
	}
	th {
		background: var(--bg-secondary);
		font-weight: 600;
	}
	tr.resolved {
		opacity: 0.6;
	}
	.times div {
		font-size: 0.8rem;
		white-space: nowrap;
	}
	.err button.link {
		background: none;
		border: none;
		color: var(--color-link);
		text-decoration: underline;
		cursor: pointer;
		padding: 0;
		font-family: monospace;
		font-size: 0.85rem;
		text-align: left;
	}
	.actions {
		white-space: nowrap;
		display: flex;
		gap: 0.4rem;
	}
	button.secondary {
		background: transparent;
		color: var(--text-muted);
		border: 1px solid var(--border);
		padding: 0.35rem 0.75rem;
		border-radius: var(--radius-md);
		cursor: pointer;
		font-size: 0.85rem;
	}
	button.secondary:hover:not(:disabled) {
		background: var(--bg-elevated);
		color: var(--text-primary);
	}
	button.secondary:disabled {
		opacity: 0.5;
		cursor: not-allowed;
	}
	.resolution {
		font-style: italic;
		color: var(--text-muted);
		font-size: 0.85rem;
	}
	tr.detail td {
		background: var(--bg-secondary);
		padding: 0;
	}
	.detail-grid {
		display: grid;
		grid-template-columns: 2fr 1fr;
		gap: 1rem;
		padding: 1rem;
	}
	.detail-grid section h3 {
		margin: 0 0 0.5rem 0;
		font-size: 0.85rem;
		text-transform: uppercase;
		color: var(--text-muted);
	}
	.detail-grid pre {
		background: var(--bg-elevated);
		color: var(--text-primary);
		border: 1px solid var(--border);
		padding: 0.75rem;
		border-radius: var(--radius-md);
		font-size: 0.8rem;
		overflow: auto;
		max-height: 300px;
		margin: 0;
	}
	code {
		font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
		font-size: 0.85rem;
	}
</style>
