/**
 * Overview: what the resolver did over a period the reader chooses, in the
 * layout Pi-hole's dashboard established — four totals, two full-width charts,
 * two breakdown donuts. Nothing on this page is a current-state readout; the
 * five health conditions live on Diagnostics, and protection lives in the
 * sidebar beside its control.
 *
 * The period is URL state, so a view is a link: `/overview?period=1h` opens
 * exactly what the sender was reading.
 *
 * One request feeds every panel (`overviewWindow.ts`), so the page has one
 * loading state and one error state rather than six: there is no longer a
 * partial answer to render, and nothing left for a panel to disagree about.
 */

import * as stylex from "@stylexjs/stylex";
import { useNavigate, useSearch } from "@tanstack/react-router";
import CoverageNotice from "@/lib/CoverageNotice";
import InlineError from "@/lib/InlineError";
import { qtypeName } from "@/features/provenance/qtype";
import type { Overview, OverviewRouteRow, OverviewTypeRow } from "@/lib/types";
import { colors } from "@/ui/tokens.stylex";
import ClientChart from "./ClientChart";
import Donut from "./Donut";
import StatTiles from "./StatTiles";
import TimeseriesChart from "./TimeseriesChart";
import type { DonutSlice } from "./Donut";
import { OverviewFrame, OverviewLoading } from "./OverviewFrame";
import { useOverviewWindow, type Panel } from "./overviewWindow";
import { DEFAULT_PERIOD } from "./period";
import { qtypeKey, routeKey, seriesColor } from "./seriesColors";

/**
 * Where the two donuts stop competing for width and sit side by side. `Donut`
 * carries the same query for the alignment it switches at that width; StyleX
 * requires the string to be a literal in the module that uses it, so the two
 * agree by inspection rather than by sharing a constant.
 */
const TWO_COLUMN = "@media (min-width: 1280px)";

const ROUTE_LABELS = {
	blocked: "Blocked",
	cache: "Cache",
	local: "Local",
	rejected: "Rejected",
	upstream: "Upstream",
	forward_zone: "Forward zone",
} as const;

const styles = stylex.create({
	panel: {
		borderRadius: "0.25rem",
		borderWidth: 1,
		borderStyle: "solid",
		borderColor: colors.border,
		backgroundColor: colors.surfaceRaised,
		paddingInline: "1rem",
		paddingBlock: "0.75rem",
	},
	panelHeading: {
		marginBottom: "0.75rem",
		fontSize: "0.875rem",
		lineHeight: "1.25rem",
		fontWeight: 600,
	},
	donutRow: {
		display: "grid",
		gap: "1rem",
		gridTemplateColumns: { default: "minmax(0, 1fr)", [TWO_COLUMN]: "repeat(2, minmax(0, 1fr))" },
	},
});

/**
 * The page's three states. The heading and the period picker stay put through
 * all three, so the reader can rescope or retry without waiting for anything.
 */
function PageBody({ panel, children }: { panel: Panel<Overview>; children: (data: Overview) => React.ReactNode }) {
	if (panel.status === "error") return <InlineError error={panel.error} onRetry={panel.retry} />;
	if (panel.status === "loading") return <OverviewLoading />;
	return <>{children(panel.data)}</>;
}

function typeSlices(types: OverviewTypeRow[]): DonutSlice[] {
	return types.map((row) => ({
		key: qtypeKey(row.qtype),
		label: row.qtype === null ? "Unknown" : qtypeName(row.qtype),
		value: row.count,
		color: seriesColor(qtypeKey(row.qtype)),
	}));
}

/**
 * Route slices. A row that names a resolver or a zone is labelled by that name
 * with the route kind as secondary text, because one name can legitimately
 * appear under two kinds and two rows can both be "Unknown". The four
 * source-less kinds are their own label and need no qualifier.
 */
function routeSlices(routes: OverviewRouteRow[]): DonutSlice[] {
	return routes.map((row) => {
		const named = row.route === "upstream" || row.route === "forward_zone";
		return {
			key: routeKey(row.route, row.source),
			label: named ? (row.source ?? "Unknown") : ROUTE_LABELS[row.route],
			...(named ? { secondary: ROUTE_LABELS[row.route] } : {}),
			value: row.count,
			color: seriesColor(routeKey(row.route, row.source)),
		};
	});
}

export default function OverviewPage() {
	const period = useSearch({ from: "/shell/overview" }).period ?? DEFAULT_PERIOD;
	const navigate = useNavigate({ from: "/overview" });
	const overview = useOverviewWindow(period);

	return (
		<OverviewFrame
			period={period}
			onChange={(next) => void navigate({ search: (prev) => ({ ...prev, period: next }) })}
		>
			<PageBody panel={overview}>
				{(data) => (
					<>
						<StatTiles stats={{ since: data.since, until: data.until, ...data.totals }} />

						{/* One notice for the page: every panel came out of this one
						    response, so a second copy would only repeat this sentence. */}
						<CoverageNotice coverage={data.coverage} />

						<section aria-labelledby="overview-queries" {...stylex.props(styles.panel)}>
							<h2 id="overview-queries" {...stylex.props(styles.panelHeading)}>
								Queries over time
							</h2>
							<TimeseriesChart data={data} />
						</section>

						<section aria-labelledby="overview-clients" {...stylex.props(styles.panel)}>
							<h2 id="overview-clients" {...stylex.props(styles.panelHeading)}>
								Client activity over time
							</h2>
							<ClientChart data={data} />
						</section>

						<div {...stylex.props(styles.donutRow)}>
							<section aria-labelledby="overview-types" {...stylex.props(styles.panel)}>
								<h2 id="overview-types" {...stylex.props(styles.panelHeading)}>
									Query types
								</h2>
								<Donut slices={typeSlices(data.types)} caption="Queries by DNS type" unit="Queries" />
							</section>
							<section aria-labelledby="overview-routes" {...stylex.props(styles.panel)}>
								<h2 id="overview-routes" {...stylex.props(styles.panelHeading)}>
									Upstream servers
								</h2>
								<Donut
									slices={routeSlices(data.routes)}
									caption="Queries by how they were answered"
									unit="Queries"
								/>
							</section>
						</div>
					</>
				)}
			</PageBody>
		</OverviewFrame>
	);
}
