import { useEffect, useRef, useState } from "react";
import { formatTime } from "@/lib/format";
import type { StatsTimeseries } from "@/lib/types";
import { isEmptyTimeseries, layoutTimeseries, type BarLayout } from "./chartLayout";

// Series colors validated for CVD separation and 3:1 surface contrast in both
// modes (Tailwind red-500 / blue-500 / emerald-600; same hex light and dark).
const SERIES = [
	{ key: "blocked", label: "Blocked", color: "#ef4444" },
	{ key: "cached", label: "Cached", color: "#059669" },
	{ key: "other", label: "Other", color: "#3b82f6" },
] as const;

const CHART_HEIGHT = 240;
const FALLBACK_WIDTH = 640;

function useContainerWidth(): [React.RefObject<HTMLDivElement | null>, number] {
	const ref = useRef<HTMLDivElement>(null);
	const [width, setWidth] = useState(0);
	useEffect(() => {
		const el = ref.current;
		if (el === null) return;
		setWidth(el.clientWidth);
		if (typeof ResizeObserver === "undefined") return;
		const observer = new ResizeObserver(() => setWidth(el.clientWidth));
		observer.observe(el);
		return () => observer.disconnect();
	}, []);
	return [ref, width];
}

const compact = new Intl.NumberFormat(undefined, { notation: "compact" });

function formatTick(ts: number, bucketSeconds: number): string {
	const date = new Date(ts * 1000);
	if (bucketSeconds >= 86_400) {
		return new Intl.DateTimeFormat(undefined, { month: "short", day: "numeric" }).format(date);
	}
	return new Intl.DateTimeFormat(undefined, { hour: "numeric", minute: "2-digit" }).format(date);
}

function barSummary(bar: BarLayout): string {
	return `${formatTime(bar.bucket.ts)}: ${bar.bucket.queries} queries, ${bar.bucket.blocked} blocked, ${bar.bucket.cached} cached`;
}

function Tooltip({ bar, chartWidth }: { bar: BarLayout; chartWidth: number }) {
	const centerX = bar.slot.x + bar.slot.width / 2;
	const leftHalf = centerX < chartWidth / 2;
	return (
		<div
			className="pointer-events-none absolute top-2 z-10 rounded border border-zinc-200 bg-white px-3 py-2 text-xs shadow-sm dark:border-zinc-700 dark:bg-zinc-900"
			style={leftHalf ? { left: Math.min(centerX + 8, chartWidth - 160) } : { right: chartWidth - centerX + 8 }}
		>
			<div className="font-medium">{formatTime(bar.bucket.ts)}</div>
			<dl className="mt-1 space-y-0.5">
				<div className="flex justify-between gap-4">
					<dt className="text-zinc-500">Queries</dt>
					<dd className="tabular-nums">{bar.bucket.queries}</dd>
				</div>
				{SERIES.map((series) => (
					<div key={series.key} className="flex items-center justify-between gap-4">
						<dt className="flex items-center gap-1.5 text-zinc-500">
							<span
								aria-hidden="true"
								className="inline-block size-2 rounded-xs"
								style={{ backgroundColor: series.color }}
							/>
							{series.label}
						</dt>
						<dd className="tabular-nums">{series.key === "other" ? bar.other : bar.bucket[series.key]}</dd>
					</div>
				))}
			</dl>
		</div>
	);
}

export default function TimeseriesChart({ data }: { data: StatsTimeseries }) {
	const [containerRef, measuredWidth] = useContainerWidth();
	const [hovered, setHovered] = useState<number | null>(null);
	const width = measuredWidth > 0 ? measuredWidth : FALLBACK_WIDTH;

	if (data.buckets.length === 0 || isEmptyTimeseries(data.buckets)) {
		return (
			<div
				ref={containerRef}
				className="flex items-center justify-center rounded border border-dashed border-zinc-300 text-sm text-zinc-500 dark:border-zinc-700"
				style={{ height: CHART_HEIGHT }}
			>
				No queries in this period.
			</div>
		);
	}

	const layout = layoutTimeseries(data.buckets, width, CHART_HEIGHT);
	const baseline = layout.plot.y + layout.plot.height;
	const hoveredBar = hovered !== null ? layout.bars[hovered] : undefined;

	return (
		<div ref={containerRef} className="relative">
			<svg
				role="img"
				aria-label={`Queries over time, ${data.buckets.length} buckets: blocked, cached and other queries per bucket`}
				width="100%"
				height={CHART_HEIGHT}
				viewBox={`0 0 ${width} ${CHART_HEIGHT}`}
				onMouseLeave={() => setHovered(null)}
			>
				{layout.yTicks.map((tick) => (
					<g key={tick.value}>
						<line
							x1={layout.plot.x}
							x2={layout.plot.x + layout.plot.width}
							y1={tick.y}
							y2={tick.y}
							className="stroke-zinc-200 dark:stroke-zinc-800"
						/>
						<text
							x={layout.plot.x - 6}
							y={tick.y}
							textAnchor="end"
							dominantBaseline="middle"
							className="fill-zinc-500 text-[10px] tabular-nums"
						>
							{compact.format(tick.value)}
						</text>
					</g>
				))}
				<line
					x1={layout.plot.x}
					x2={layout.plot.x + layout.plot.width}
					y1={baseline}
					y2={baseline}
					className="stroke-zinc-300 dark:stroke-zinc-700"
				/>
				{layout.xTicks.map((tick) => (
					<text
						key={tick.ts}
						x={tick.x}
						y={baseline + 14}
						textAnchor="middle"
						className="fill-zinc-500 text-[10px]"
					>
						{formatTick(tick.ts, data.bucket_seconds)}
					</text>
				))}
				{layout.bars.map((bar, i) => (
					<g key={bar.bucket.ts} opacity={hovered === null || hovered === i ? 1 : 0.55}>
						{SERIES.map((series) => {
							const rect = bar.segments[series.key];
							if (rect.height <= 0) return null;
							return (
								<rect
									key={series.key}
									x={rect.x}
									y={rect.y}
									width={rect.width}
									height={rect.height}
									fill={series.color}
									className="stroke-zinc-50 dark:stroke-zinc-950"
									strokeWidth={rect.width > 3 ? 1 : 0}
								/>
							);
						})}
					</g>
				))}
				{layout.bars.map((bar, i) => (
					<rect
						key={bar.bucket.ts}
						x={bar.slot.x}
						y={bar.slot.y}
						width={bar.slot.width}
						height={bar.slot.height}
						fill="transparent"
						onMouseEnter={() => setHovered(i)}
					>
						<title>{barSummary(bar)}</title>
					</rect>
				))}
			</svg>
			{hoveredBar !== undefined && <Tooltip bar={hoveredBar} chartWidth={width} />}
			<ul className="mt-2 flex flex-wrap gap-x-4 gap-y-1 text-xs text-zinc-600 dark:text-zinc-400">
				{SERIES.map((series) => (
					<li key={series.key} className="flex items-center gap-1.5">
						<span
							aria-hidden="true"
							className="inline-block size-2.5 rounded-xs"
							style={{ backgroundColor: series.color }}
						/>
						{series.label}
					</li>
				))}
			</ul>
			<table className="sr-only">
				<caption>Queries per time bucket</caption>
				<thead>
					<tr>
						<th scope="col">Time</th>
						<th scope="col">Queries</th>
						<th scope="col">Blocked</th>
						<th scope="col">Cached</th>
						<th scope="col">Other</th>
					</tr>
				</thead>
				<tbody>
					{layout.bars.map((bar) => (
						<tr key={bar.bucket.ts}>
							<th scope="row">{formatTime(bar.bucket.ts)}</th>
							<td>{bar.bucket.queries}</td>
							<td>{bar.bucket.blocked}</td>
							<td>{bar.bucket.cached}</td>
							<td>{bar.other}</td>
						</tr>
					))}
				</tbody>
			</table>
		</div>
	);
}
