import { useState } from "react";
import {
	CartesianGrid,
	Line,
	LineChart,
	ResponsiveContainer,
	Tooltip,
	XAxis,
	YAxis,
} from "recharts";
import {
	Select,
	SelectContent,
	SelectItem,
	SelectTrigger,
	SelectValue,
} from "@/components/ui/select";
import {
	DEFAULT_CHART_RANGES,
	type ChartRangeOption,
	type ChartRangeValue,
} from "./chartRanges";
import {
	formatScaled,
	metricScaleInfo,
	type MetricScale,
	type MetricUnit,
} from "../lib/metricFormat";

export interface SeriesPoint {
	t: number;
	v: number | null;
}

export interface ChartSeries {
	label: string;
	points: SeriesPoint[];
}

/** Merge multiple time-series into a single recharts-friendly array. */
function mergeSeries(series: ChartSeries[]): Record<string, unknown>[] {
	const map = new Map<number, Record<string, unknown>>();
	for (const seriesItem of series) {
		for (const point of seriesItem.points) {
			const existing = map.get(point.t) ?? { time: point.t };
			existing[seriesItem.label] = point.v;
			map.set(point.t, existing);
		}
	}
	return [...map.values()].sort(
		(a, b) => (a.time as number) - (b.time as number),
	);
}

function formatTime(ms: number): string {
	return new Date(ms).toLocaleTimeString([], {
		hour: "2-digit",
		minute: "2-digit",
	});
}

const CHART_COLORS = [
	"var(--color-chart-1)",
	"var(--color-chart-2)",
	"var(--color-chart-3)",
	"var(--color-chart-4)",
	"var(--color-chart-5)",
];

interface LineSeriesChartProps {
	series: ChartSeries[];
	height?: number;
	/** Display unit for the Y axis + tooltip (drives decimal-prefix scaling). */
	unit?: MetricUnit;
	/** "auto" picks a prefix from the data magnitude; k/m/g/t force one. */
	scale?: MetricScale;
	/** Available displayed time ranges. Defaults to the shared range choices. */
	rangeOptions?: readonly ChartRangeOption[];
	/** Whether to render the interactive range selector. */
	showRangeSelector?: boolean;
	/** Initial uncontrolled range. Defaults to the largest numeric option. */
	defaultRangeSeconds?: ChartRangeValue;
	/** Controlled range for consumers that refetch when the selection changes. */
	rangeSeconds?: ChartRangeValue;
	onRangeChange?: (range: ChartRangeValue) => void;
}

/** Shared range-aware line chart renderer for Prometheus and qBittorrent data. */
export function LineSeriesChart({
	series,
	height = 300,
	unit = "none",
	scale = "auto",
	rangeOptions = DEFAULT_CHART_RANGES,
	showRangeSelector = true,
	defaultRangeSeconds,
	rangeSeconds,
	onRangeChange,
}: LineSeriesChartProps) {
	const initialRange =
		defaultRangeSeconds ??
		[...rangeOptions].reverse().find((range) => typeof range.value === "number")
			?.value;
	const [localRangeSeconds, setLocalRangeSeconds] = useState<
		ChartRangeValue | undefined
	>(initialRange);
	const selectedRangeSeconds = rangeSeconds ?? localRangeSeconds;
	const latestTimestamp = series.reduce(
		(max, seriesItem) =>
			seriesItem.points.reduce(
				(seriesMax, point) => Math.max(seriesMax, point.t),
				max,
			),
		0,
	);
	const cutoff =
		typeof selectedRangeSeconds === "number"
			? latestTimestamp - selectedRangeSeconds * 1000
			: null;
	const visibleSeries =
		cutoff !== null && latestTimestamp > 0
			? series.map((seriesItem) => ({
					...seriesItem,
					points: seriesItem.points.filter((point) => point.t >= cutoff),
				}))
			: series;

	const maxAbs = visibleSeries.reduce((max, seriesItem) => {
		for (const point of seriesItem.points) {
			const value = point.v == null ? 0 : Math.abs(point.v);
			if (value > max) max = value;
		}
		return max;
	}, 0);
	const scaleInfo = metricScaleInfo(maxAbs, unit, scale);
	const formatValue = (value: number | null | undefined) =>
		formatScaled(value, scaleInfo, unit);

	function handleRangeChange(value: string) {
		const nextRange: ChartRangeValue = value === "all" ? "all" : Number(value);
		setLocalRangeSeconds(nextRange);
		onRangeChange?.(nextRange);
	}

	return (
		<div className="space-y-2">
			{showRangeSelector && rangeOptions.length > 0 && (
				<div className="flex justify-end">
					<Select
						value={
							selectedRangeSeconds === undefined
								? undefined
								: String(selectedRangeSeconds)
						}
						onValueChange={handleRangeChange}
					>
						<SelectTrigger
							className="w-[150px]"
							size="sm"
							aria-label="Chart range"
						>
							<SelectValue placeholder="Chart range" />
						</SelectTrigger>
						<SelectContent>
							{rangeOptions.map((range) => (
								<SelectItem key={range.value} value={String(range.value)}>
									{range.label}
								</SelectItem>
							))}
						</SelectContent>
					</Select>
				</div>
			)}
			<ResponsiveContainer width="100%" height={height}>
				<LineChart data={mergeSeries(visibleSeries)}>
					<CartesianGrid strokeDasharray="3 3" className="stroke-muted" />
					<XAxis
						dataKey="time"
						tickFormatter={formatTime}
						tick={{ fontSize: 11 }}
						className="fill-muted-foreground"
					/>
					<YAxis
						tickFormatter={formatValue}
						tick={{ fontSize: 11 }}
						width={56}
						className="fill-muted-foreground"
					/>
					<Tooltip
						labelFormatter={(label) => formatTime(Number(label))}
						formatter={(value) => formatValue(Number(value))}
						contentStyle={{
							backgroundColor: "var(--color-popover)",
							border: "1px solid var(--color-border)",
							borderRadius: "0.5rem",
							color: "var(--color-popover-foreground)",
						}}
					/>
					{visibleSeries.map((seriesItem, index) => (
						<Line
							key={seriesItem.label}
							type="monotone"
							dataKey={seriesItem.label}
							stroke={CHART_COLORS[index % CHART_COLORS.length]}
							dot={false}
							strokeWidth={2}
							connectNulls
						/>
					))}
				</LineChart>
			</ResponsiveContainer>
		</div>
	);
}
