<!--
  ChartToolbar.svelte — Chart action bar with download and display controls

  Provides a toolbar row with:
  - Download full meteogram as PNG button
  - Slot for additional custom controls (e.g. legend toggle)

  When multiple charts are provided, they are stitched into a single
  combined image on download.

  Usage:
    <ChartToolbar
      charts={chartComponents}
      fileName="model-comparison"
    >
      {#snippet controls()}
        <Switch bind:checked={showLegend} />
      {/snippet}
    </ChartToolbar>
-->
<script module lang="ts">
	/** Anything that can export itself as a PNG data URL (e.g. a CanvasChart). */
	export interface DownloadableChart {
		getPngDataUrl(): string | null;
	}
</script>

<script lang="ts">
	import type { Snippet } from 'svelte';

	// ─── Props ──────────────────────────────────────────────────────────────────

	interface Props {
		/** Chart components available for download (undefined entries are skipped) */
		charts?: Array<DownloadableChart | undefined | null>;
		/** Base file name for downloaded images (without extension) */
		fileName?: string;
		/** Optional CSS class for the outer container */
		class?: string;
		/** Slot for additional controls (switches, checkboxes, etc.) */
		controls?: Snippet;
	}

	let {
		charts = [],
		fileName = 'drizzli-chart',
		class: className = '',
		controls
	}: Props = $props();

	// ─── State ──────────────────────────────────────────────────────────────────

	let downloading = $state(false);

	// ─── Computed ───────────────────────────────────────────────────────────────

	let hasCharts = $derived(charts.some((chart) => chart != null));

	// ─── Download ───────────────────────────────────────────────────────────────

	function loadImage(src: string): Promise<HTMLImageElement> {
		return new Promise((resolve) => {
			const img = new Image();
			img.onload = () => resolve(img);
			img.onerror = () => resolve(img);
			img.src = src;
		});
	}

	/** Resolves the page background so exports match the current theme. */
	function exportBackground(): string {
		const bg = getComputedStyle(document.documentElement).getPropertyValue('--background').trim();
		return bg || '#ffffff';
	}

	function triggerDownload(url: string, name: string): void {
		const link = document.createElement('a');
		link.href = url;
		link.download = name;
		link.style.display = 'none';
		document.body.appendChild(link);
		link.click();
		requestAnimationFrame(() => {
			document.body.removeChild(link);
		});
	}

	async function handleDownload(): Promise<void> {
		if (!hasCharts || downloading) return;

		downloading = true;

		try {
			const dataUrls = charts
				.filter((chart): chart is DownloadableChart => chart != null)
				.map((chart) => chart.getPngDataUrl())
				.filter((url): url is string => url !== null);
			if (dataUrls.length === 0) return;

			const images = (await Promise.all(dataUrls.map(loadImage))).filter(
				(img) => img.naturalWidth > 0
			);
			if (images.length === 0) return;

			const maxWidth = Math.max(...images.map((img) => img.naturalWidth));
			const totalHeight = images.reduce((sum, img) => sum + img.naturalHeight, 0);

			const canvas = document.createElement('canvas');
			canvas.width = maxWidth;
			canvas.height = totalHeight;

			const ctx = canvas.getContext('2d');
			if (!ctx) return;

			ctx.fillStyle = exportBackground();
			ctx.fillRect(0, 0, maxWidth, totalHeight);

			let y = 0;
			for (const img of images) {
				ctx.drawImage(img, 0, y);
				y += img.naturalHeight;
			}

			triggerDownload(canvas.toDataURL('image/png'), `${fileName}.png`);
		} finally {
			setTimeout(() => {
				downloading = false;
			}, 500);
		}
	}
</script>

<div
	class="chart-toolbar flex flex-col items-center gap-4 md:flex-row md:justify-between {className}"
>
	<!-- Left side: Custom controls slot -->
	<div class="flex flex-wrap items-center gap-4 md:gap-6">
		{#if controls}
			{@render controls()}
		{/if}
	</div>

	<!-- Right side: Download button -->
	<div class="flex flex-wrap items-center gap-2">
		<button
			type="button"
			class="toolbar-btn"
			disabled={!hasCharts || downloading}
			onclick={handleDownload}
			title="Download meteogram as PNG image"
		>
			{#if downloading}
				<svg
					class="h-4 w-4 animate-spin"
					xmlns="http://www.w3.org/2000/svg"
					viewBox="0 0 24 24"
					fill="none"
					stroke="currentColor"
					stroke-width="2"
					aria-hidden="true"
				>
					<path d="M21 12a9 9 0 1 1-6.219-8.56" />
				</svg>
			{:else}
				<svg
					class="h-4 w-4"
					xmlns="http://www.w3.org/2000/svg"
					viewBox="0 0 24 24"
					fill="none"
					stroke="currentColor"
					stroke-width="2"
					stroke-linecap="round"
					stroke-linejoin="round"
					aria-hidden="true"
				>
					<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" />
					<polyline points="7 10 12 15 17 10" />
					<line x1="12" y1="15" x2="12" y2="3" />
				</svg>
			{/if}
			<span>PNG</span>
		</button>
	</div>
</div>

<style>
	.toolbar-btn {
		display: inline-flex;
		align-items: center;
		gap: 0.375rem;
		padding: 0.375rem 0.75rem;
		font-size: 0.8125rem;
		font-weight: 500;
		line-height: 1.25rem;
		color: hsl(var(--muted-foreground));
		background: hsl(var(--muted) / 0.5);
		border: 1px solid hsl(var(--border));
		border-radius: var(--radius, 0.375rem);
		cursor: pointer;
		transition:
			color 150ms ease,
			background-color 150ms ease,
			border-color 150ms ease;
		white-space: nowrap;
		user-select: none;
	}

	.toolbar-btn:hover:not(:disabled) {
		color: hsl(var(--foreground));
		background: hsl(var(--muted));
		border-color: hsl(var(--foreground) / 0.2);
	}

	.toolbar-btn:active:not(:disabled) {
		background: hsl(var(--muted) / 0.8);
		transform: translateY(0.5px);
	}

	.toolbar-btn:disabled {
		opacity: 0.5;
		cursor: not-allowed;
	}

	.toolbar-btn:focus-visible {
		outline: 2px solid hsl(var(--ring));
		outline-offset: 2px;
	}
</style>
