<script lang="ts">
	import { onMount } from 'svelte';
	import { get } from 'svelte/store';

	import { storedEnsembleModel, storedLocation } from '$lib/stores/settings';

	import { ChartContainer, ChartToolbar } from '$lib/components/charts';
	import { Label } from '$lib/components/ui/label';
	import { Switch } from '$lib/components/ui/switch';

	import { CHART_COLORS, CanvasChart, type ChartSeries, isColumnUnit } from '$lib/charts';
	import {
		type DaylightBand,
		type EnsembleForecastResult,
		fetchEnsembleForecast
	} from '$lib/services/weather';

	import { defaultParameters, ensembleModelGroups } from '../../options';
	import ModelSelector from '../../week/[location]/ModelSelector.svelte';

	import type { PageData } from './$types';

	const CHART_GROUP = '14-day-ensemble';

	// ─── Display State (does NOT trigger data re-fetch) ─────────────────────────

	let showLegend = $state(true);

	// ─── Data Fetch State ───────────────────────────────────────────────────────

	let chartComponents: CanvasChart[] = $state([]);
	let mounted = $state(false);
	let loading = $state(true);
	let loadError = $state<string | null>(null);
	let requestVersion = 0;

	let { data }: { data: PageData } = $props();

	// the URL is the source of truth: location comes from the load function,
	// which is also correct on hydrated prerendered pages. The persisted store
	// only mirrors it so the header and bare /weather/* redirects follow along.
	let location = $derived(data.location);
	$effect(() => {
		storedLocation.set(data.location);
	});

	let params = $state({
		...defaultParameters,
		hourly: ['temperature_2m'],
		models: ['ncep_gefs_seamless']
	});

	// ─── Cached API Response ────────────────────────────────────────────────────

	interface FetchedData {
		ensembleResult: EnsembleForecastResult;
		timestamps: number[];
		timezone: string;
		daylightBands: DaylightBand[];
	}

	let fetchedData: FetchedData | null = $state(null);

	// ─── Lifecycle ──────────────────────────────────────────────────────────────

	onMount(() => {
		// preselect the persisted ensemble model (client-only, keeps SSR stable)
		params.models = [get(storedEnsembleModel)];
		mounted = true;
	});

	// components persist across refetches; entries are null while unmounted
	let liveCharts = $derived(chartComponents.filter((chart) => chart != null));

	// ─── Data Fetching (only when params.hourly or params.models change) ───────

	$effect(() => {
		const hourlyVars = params.hourly;
		const modelList = params.models;

		if (!mounted || !hourlyVars?.length || !modelList?.length) return;

		const loc = location;

		// versioned so a slow stale response can never overwrite a newer one
		const version = ++requestVersion;
		loading = true;
		loadError = null;

		fetchEnsembleForecast({
			latitude: loc.latitude!,
			longitude: loc.longitude!,
			hourlyVariables: hourlyVars,
			models: modelList,
			forecast_days: 14,
			temperature_unit: params.temperature_unit as 'celsius' | 'fahrenheit',
			wind_speed_unit: params.wind_speed_unit as 'kmh' | 'ms' | 'mph' | 'kn',
			precipitation_unit: params.precipitation_unit as 'mm' | 'inch',
			timezone: loc.timezone
		})
			.then((result: EnsembleForecastResult) => {
				if (version !== requestVersion) return;

				fetchedData = {
					ensembleResult: result,
					timestamps: result.timestamps,
					timezone: result.timezone,
					daylightBands: result.daylightBands
				};

				loading = false;
			})
			.catch((err: unknown) => {
				if (version !== requestVersion) return;
				loadError = err instanceof Error ? err.message : String(err);
				loading = false;
			});
	});

	// ─── Chart Building (runs when fetchedData or the variable list changes) ────

	// Timestamps from the service are in milliseconds; CanvasChart uses seconds.
	let timestampsSec = $derived.by(() =>
		fetchedData ? fetchedData.timestamps.map((t) => t / 1000) : []
	);

	interface ChartDef {
		title?: string;
		subtitle?: string;
		unit: string;
		showCredit: boolean;
		series: ChartSeries[];
	}

	const BAND_COLOR = 'rgba(115, 192, 222, 0.9)';

	let chartDefs = $derived.by((): ChartDef[] => {
		if (!fetchedData) return [];

		const { ensembleResult } = fetchedData;
		const variables = params.hourly || [];
		const defs: ChartDef[] = [];

		for (let vi = 0; vi < variables.length; vi++) {
			const variable = variables[vi];
			const varData = ensembleResult.variables[variable];
			if (!varData) continue;

			const unit = varData.unit;
			const isColumn = isColumnUnit(unit);
			const memberCount = varData.members.length;

			// Min/max spread band + mean, instead of every individual member
			const series: ChartSeries[] = [
				{
					name: 'Max',
					type: 'line',
					color: BAND_COLOR,
					data: varData.max,
					width: 1,
					fill: true,
					fillOpacity: 0.25,
					bandTo: varData.min,
					format: (v) => `${v.toFixed(1)} ${unit}`
				},
				{
					name: 'Min',
					type: 'line',
					color: BAND_COLOR,
					data: varData.min,
					width: 1,
					format: (v) => `${v.toFixed(1)} ${unit}`
				},
				{
					name: 'Mean',
					type: isColumn ? 'bar' : 'line',
					color: CHART_COLORS.average,
					data: varData.average,
					width: 3,
					dashed: !isColumn,
					format: (v) => `${v.toFixed(1)} ${unit}`
				}
			];

			const isFirst = vi === 0;
			const isLast = vi === variables.length - 1;

			defs.push({
				title: isFirst ? 'Ensemble Spread' : undefined,
				subtitle: isFirst
					? `${variable} min/mean/max across ${memberCount} members (${params.models?.[0] ?? ''})`
					: undefined,
				unit,
				showCredit: isLast,
				series
			});
		}

		return defs;
	});
</script>

<!-- ─── Page hero: location + ensemble model selection ─────────────────────── -->

<div class="mb-5 flex flex-wrap items-center justify-between gap-x-6 gap-y-3">
	<div class="flex min-w-0 items-center gap-3">
		<img
			class="h-10 w-10 shrink-0 rounded-full shadow-sm ring-2 ring-border"
			src="/images/country-flags/{(location.country_code || 'united_nations').toLowerCase()}.svg"
			alt={location.country ?? ''}
		/>
		<div class="min-w-0">
			<h1 class="truncate text-2xl leading-tight font-bold tracking-tight md:text-3xl">
				{location.name}
			</h1>
			<p class="truncate text-sm text-muted-foreground">
				{#if location.admin1}{location.admin1},
				{/if}{location.country ?? ''}
				<span class="mx-1 opacity-50">·</span>
				14-day ensemble forecast
			</p>
		</div>
	</div>

	<ModelSelector
		selectedModel={params.models?.[0] ?? 'ncep_gefs_seamless'}
		groups={ensembleModelGroups}
		label="Ensemble model"
		onModelChange={(model) => {
			params.models = [model];
			storedEnsembleModel.set(model);
		}}
	/>
</div>

<!-- ─── Chart Area ─────────────────────────────────────────────────────────── -->

{#if loadError}
	<div
		class="mb-4 rounded-md border border-destructive/50 bg-destructive/10 px-4 py-3 text-sm text-destructive"
	>
		Failed to load weather data: {loadError}
	</div>
{/if}

<ChartContainer {loading} chartCount={params.hourly?.length || 1} chartHeight={300}>
	{#if fetchedData}
		{#each chartDefs as def, i (i)}
			<CanvasChart
				bind:this={chartComponents[i]}
				timestamps={timestampsSec}
				timezone={fetchedData.timezone}
				series={def.series}
				bands={fetchedData.daylightBands}
				unit={def.unit}
				title={def.title}
				subtitle={def.subtitle}
				showCredit={def.showCredit}
				{showLegend}
				height={300}
				group={CHART_GROUP}
			/>
		{/each}
	{/if}
</ChartContainer>

<!-- ─── Toolbar: Controls + Download ───────────────────────────────────────── -->

<div class="mt-6 md:mt-10">
	<ChartToolbar charts={liveCharts} fileName="14-day-forecast">
		{#snippet controls()}
			<div class="flex gap-2">
				<Switch id="show_legend" name="Show legend" bind:checked={showLegend} />
				<Label for="show_legend" class="mb-0.5 cursor-pointer text-lg">Show legend</Label>
			</div>
		{/snippet}
	</ChartToolbar>
</div>
