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

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

	import {
		buildAverageSeries,
		buildCurrentTimeSeries,
		buildDaylightSeries,
		buildSpreadSeries,
		composeChartOption,
		getThemeColors
	} from '$lib/utils/echarts';

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

	import {
		type EnsembleForecastResult,
		type MarkArea,
		fetchEnsembleForecast
	} from '$lib/services/weather';

	import { defaultParameters } from '../options';

	import type * as echarts from 'echarts';

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

	let showLegend = $state(false);

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

	let chartComponents: EChart[] = $state([]);
	let chartInstances: echarts.ECharts[] = $state([]);
	let chartOptions: Array<Record<string, unknown>> = $state([]);
	let mounted = $state(false);
	let loading = $state(true);

	const location = get(storedLocation);

	let params = $state({
		latitude: [52.52],
		longitude: [13.41],
		...defaultParameters,
		hourly: ['temperature_2m'],
		models: ['gfs_seamless']
	});

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

	interface FetchedData {
		ensembleResult: EnsembleForecastResult;
		timestamps: number[];
		utc_offset_seconds: number;
		markAreas: MarkArea[];
	}

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

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

	onMount(() => {
		mounted = true;
	});

	onDestroy(() => {
		chartInstances = [];
		chartOptions = [];
		chartComponents = [];
	});

	// ─── Chart Instance Tracking ────────────────────────────────────────────────

	function handleChartReady(chart: echarts.ECharts): void {
		chartInstances = [...chartInstances, chart];
	}

	// ─── 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 loadData = async () => {
			loading = true;
			chartInstances = [];
			chartComponents = [];

			const result: EnsembleForecastResult = await fetchEnsembleForecast({
				latitude: location.latitude!,
				longitude: location.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'
			});

			fetchedData = {
				ensembleResult: result,
				timestamps: result.timestamps,
				utc_offset_seconds: result.utcOffsetSeconds,
				markAreas: result.markAreas
			};

			loading = false;
		};

		loadData();
	});

	// ─── Chart Option Building (runs when fetchedData OR display toggles change) ─

	$effect(() => {
		if (!fetchedData) return;

		const { ensembleResult, timestamps, utc_offset_seconds, markAreas } = fetchedData;
		const _showLegend = showLegend;

		const colors = getThemeColors();
		const variableCount = params.hourly?.length || 0;
		const newOptions: Array<Record<string, unknown>> = [];

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

			const unit = varData.unit;
			const { average, min: minValues, max: maxValues } = varData;

			const series: Array<Record<string, unknown>> = [];

			const spreadData: Array<[number, number, number]> = minValues.map(
				(minVal, index) =>
					[timestamps[index], minVal ?? 0, maxValues[index] ?? 0] as [number, number, number]
			);

			series.push(...buildSpreadSeries({ variable, spreadData }));

			const averageData = average.map((val, idx) => [timestamps[idx], val] as [number, number]);
			series.push(buildAverageSeries({ variable, data: averageData, unit }));

			series.push(buildCurrentTimeSeries({ utcOffsetSeconds: utc_offset_seconds }));

			const daylightSeries = buildDaylightSeries({ markAreas });
			if (daylightSeries) {
				series.push(daylightSeries);
			}

			const isFirst = vi === 0;
			const isLast = vi === variableCount - 1;

			const option = composeChartOption({
				title: isFirst
					? {
							text: 'Model Spread',
							subtext: `Compare ${params.hourly?.join(', ') || ''} in models: ${params.models?.join(', ') || ''}`
						}
					: null,
				tooltip: { unit },
				legend: {
					show: _showLegend,
					data: [variable + '_average']
				},
				grid: {
					hasTitle: isFirst,
					hasSubtitle: isFirst,
					showLegend: _showLegend
				},
				yAxis: { unit },
				series,
				toolbox: false,
				showCredit: isLast,
				colors
			});

			newOptions.push(option);
		}

		chartOptions = newOptions;
	});
</script>

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

<ChartContainer
	{loading}
	chartCount={params.hourly?.length || 0}
	chartHeight={showLegend ? 400 : 300}
>
	{#each chartOptions as option, i (i)}
		<EChart
			{option}
			height={showLegend ? '400px' : '300px'}
			onChartReady={handleChartReady}
			bind:this={chartComponents[i]}
		/>
	{/each}
</ChartContainer>

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

<div class="mt-6 md:mt-10">
	<ChartToolbar charts={chartInstances} 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>
