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

	let slug = $derived(page.params.slug ?? '');
	let summary = $state<MetricsSummary | null>(null);
	let loading = $state(true);
	let error = $state<string | null>(null);
	let windowHours = $state(24);

	const windows = [
		{ label: '24 h', hours: 24 },
		{ label: '7 d', hours: 24 * 7 },
		{ label: '30 d', hours: 24 * 30 }
	];

	async function load() {
		loading = true;
		error = null;
		try {
			summary = await api.metrics.summary(slug, windowHours);
		} catch (e) {
			error = e instanceof ApiError ? e.message : String(e);
		} finally {
			loading = false;
		}
	}

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

	function pct(x: number): string {
		return `${(x * 100).toFixed(1)}%`;
	}
	function ms(x: number): string {
		return `${Math.round(x)} ms`;
	}

	// Chart geometry. Bars are scaled to the busiest bucket; the error portion
	// is drawn on top of the success portion so the split reads at a glance.
	const chartW = 720;
	const chartH = 160;
	let maxTotal = $derived(Math.max(1, ...(summary?.series ?? []).map((b) => b.total)));
	let barStep = $derived(
		summary && summary.series.length > 0 ? chartW / summary.series.length : chartW
	);
	function barW(): number {
		return Math.max(1, barStep * 0.8);
	}
	function fmtBucket(iso: string): string {
		return new Date(iso).toLocaleString();
	}
</script>

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

<div class="panel">
	<header class="panel-head">
		<div>
			<h2>Metrics</h2>
			<p class="subtitle">Execution rollup over the selected window (from the request log).</p>
		</div>
		<div class="controls" role="group" aria-label="Time window">
			{#each windows as w (w.hours)}
				<button
					class:active={windowHours === w.hours}
					onclick={() => (windowHours = w.hours)}
					type="button">{w.label}</button
				>
			{/each}
		</div>
	</header>

	{#if error}
		<p class="error" role="alert">{error}</p>
	{:else if loading && !summary}
		<p class="muted">Loading…</p>
	{:else if summary}
		{#if summary.total === 0}
			<p class="muted">No executions recorded in this window.</p>
		{:else}
			<div class="cards">
				<div class="card">
					<span class="k">Executions</span>
					<span class="v">{summary.total}</span>
				</div>
				<div class="card" class:bad={summary.errors > 0}>
					<span class="k">Error rate</span>
					<span class="v">{pct(summary.error_rate)}</span>
					<span class="sub">{summary.errors} errors</span>
				</div>
				<div class="card">
					<span class="k">Avg latency</span>
					<span class="v">{ms(summary.avg_duration_ms)}</span>
				</div>
				<div class="card">
					<span class="k">p50</span>
					<span class="v">{ms(summary.p50_duration_ms)}</span>
				</div>
				<div class="card">
					<span class="k">p95</span>
					<span class="v">{ms(summary.p95_duration_ms)}</span>
				</div>
			</div>

			<h3>Executions per hour</h3>
			<div class="chart-wrap">
				<svg
					viewBox="0 0 {chartW} {chartH}"
					width="100%"
					height={chartH}
					role="img"
					aria-label="Executions per hour, error portion highlighted"
				>
					{#each summary.series as b, i (b.ts)}
						{@const total = (b.total / maxTotal) * (chartH - 4)}
						{@const errs = (b.errors / maxTotal) * (chartH - 4)}
						{@const x = i * barStep + (barStep - barW()) / 2}
						<rect
							{x}
							y={chartH - total}
							width={barW()}
							height={total - errs}
							class="bar-ok"
						>
							<title>{fmtBucket(b.ts)}: {b.total} ({b.errors} errors)</title>
						</rect>
						<rect {x} y={chartH - errs} width={barW()} height={errs} class="bar-err">
							<title>{fmtBucket(b.ts)}: {b.errors} errors</title>
						</rect>
					{/each}
				</svg>
			</div>

			<div class="breakdowns">
				<div>
					<h3>By status</h3>
					<table>
						<tbody>
							{#each summary.by_status as row (row.key)}
								<tr>
									<td>{row.key}</td>
									<td class="num">{row.count}</td>
								</tr>
							{/each}
						</tbody>
					</table>
				</div>
				<div>
					<h3>By source</h3>
					<table>
						<tbody>
							{#each summary.by_source as row (row.key)}
								<tr>
									<td>{row.key}</td>
									<td class="num">{row.count}</td>
								</tr>
							{/each}
						</tbody>
					</table>
				</div>
			</div>
		{/if}
	{/if}
</div>

<style>
	.panel-head {
		display: flex;
		justify-content: space-between;
		align-items: flex-start;
		gap: 1rem;
		flex-wrap: wrap;
	}
	.subtitle {
		color: var(--text-muted);
		margin: 0.25rem 0 0;
		font-size: 0.875rem;
	}
	.controls {
		display: inline-flex;
		gap: 0.25rem;
	}
	.controls button {
		padding: 0.35rem 0.7rem;
		border: 1px solid var(--border);
		background: transparent;
		color: var(--text-muted);
		border-radius: var(--radius-sm);
		font: inherit;
		font-size: 0.8rem;
		cursor: pointer;
	}
	.controls button.active {
		color: var(--color-link);
		border-color: var(--color-link);
	}
	.cards {
		display: flex;
		flex-wrap: wrap;
		gap: 0.75rem;
		margin: 1rem 0 1.5rem;
	}
	.card {
		display: flex;
		flex-direction: column;
		gap: 0.15rem;
		min-width: 8rem;
		padding: 0.75rem 1rem;
		border: 1px solid var(--border);
		border-radius: var(--radius-md);
	}
	.card.bad .v {
		color: var(--color-danger-fg);
	}
	.card .k {
		font-size: 0.72rem;
		text-transform: uppercase;
		letter-spacing: 0.04em;
		color: var(--text-muted);
	}
	.card .v {
		font-size: 1.4rem;
		font-weight: 600;
	}
	.card .sub {
		font-size: 0.72rem;
		color: var(--text-muted);
	}
	.chart-wrap {
		overflow-x: auto;
		border: 1px solid var(--border);
		border-radius: var(--radius-md);
		padding: 0.5rem;
	}
	.bar-ok {
		fill: var(--color-link);
	}
	.bar-err {
		fill: var(--color-danger-fg);
	}
	.breakdowns {
		display: flex;
		flex-wrap: wrap;
		gap: 2rem;
		margin-top: 1.5rem;
	}
	table {
		border-collapse: collapse;
	}
	td {
		padding: 0.25rem 0.75rem 0.25rem 0;
		font-size: 0.875rem;
	}
	td.num {
		text-align: right;
		font-variant-numeric: tabular-nums;
	}
	.muted {
		color: var(--text-muted);
	}
	.error {
		color: var(--color-danger-fg);
	}
	h3 {
		margin: 1rem 0 0.5rem;
		font-size: 0.95rem;
	}
</style>
