/**
 * The seven columns of the Activity table: Time, Domain, Client, Type, Result,
 * Route and Duration.
 *
 * The labels here are the compact forms a scanned table needs. The detail page
 * keeps `provenanceCopy`'s long forms, which spell out the same facts with room
 * for the rcode number and the "answered by" phrasing.
 *
 * The cells render both a stored row and a streamed event, so they know nothing
 * about routes: the caller renders the Domain cell's contents and decides what,
 * if anything, a row opens. A streamed row has no id — the frame precedes its
 * own insert — and only the caller knows whether it has a surface for one.
 */

import type { ReactNode } from "react";
import * as stylex from "@stylexjs/stylex";
import { formatMicros, formatTime } from "@/lib/format";
import type { RouteKind } from "@/lib/types";
import { ClientName, type ClientNames } from "@/features/clients/clientNames";
import { rcodeShortName } from "@/features/provenance/provenanceCopy";
import { qtypeName } from "@/features/provenance/qtype";
import type { QuerySummary } from "@/features/provenance/querySummary";
import { styles as shared } from "@/ui/styles";
import { colors } from "@/ui/tokens.stylex";

const DARK = "@media (prefers-color-scheme: dark)";

const styles = stylex.create({
	head: {
		backgroundColor: { default: "oklch(98.5% 0 none)", [DARK]: "oklch(21% 0.006 285.885)" },
		textAlign: "left",
	},
	th: {
		paddingInline: "0.75rem",
		paddingBlock: "0.5rem",
		fontWeight: 500,
		color: colors.textSecondary,
	},
	cell: {
		paddingInline: "0.75rem",
		paddingBlock: "0.5rem",
	},
	nowrap: {
		whiteSpace: "nowrap",
	},
	breakAll: {
		wordBreak: "break-all",
	},
	small: {
		fontSize: "0.75rem",
		lineHeight: "1rem",
	},
	muted: {
		color: colors.textMuted,
	},
	domainLink: {
		color: colors.primaryOnSurface,
		textDecorationLine: "none",
	},
	/**
	 * The badge shape and its weight are the signal; the tint only says which
	 * kind of unhappy answer this was. A monochrome or colour-blind reading of
	 * the table still separates a blocked or failed row from a plain NOERROR
	 * one, which a hue alone would not.
	 */
	badge: {
		display: "inline-block",
		borderRadius: "0.25rem",
		paddingInline: "0.375rem",
		paddingBlock: "0.125rem",
		fontSize: "0.75rem",
		lineHeight: "1rem",
		fontWeight: 600,
	},
	blockedBadge: {
		backgroundColor: { default: "oklch(93.6% 0.032 17.717)", [DARK]: "oklch(39.6% 0.141 25.723)" },
		color: { default: "oklch(44.4% 0.177 26.899)", [DARK]: "oklch(88.5% 0.062 18.334)" },
	},
	faultBadge: {
		backgroundColor: { default: "oklch(96.2% 0.059 95.617)", [DARK]: "oklch(41.4% 0.112 45.904)" },
		color: { default: "oklch(47.3% 0.137 46.201)", [DARK]: "oklch(90.1% 0.076 70.697)" },
	},
});

const ROUTE_LABELS: Record<RouteKind, string> = {
	blocked: "Blocked",
	local: "Local",
	forward_zone: "Forward zone",
	upstream: "Upstream",
	cache: "Cache",
	rejected: "Rejected",
};

/** The compact Route label. `Record` over the union, so a new kind fails `tsc`. */
export function routeLabel(kind: RouteKind): string {
	return ROUTE_LABELS[kind];
}

/**
 * The compact Result label. A block is the answer the operator asked nxdns for,
 * so it wins over the rcode it was delivered as — a blocked name answered with
 * NOERROR and a zero address is still "Blocked". Everything else reads as the
 * code the client saw.
 */
export function resultLabel(row: Pick<QuerySummary, "blocked" | "rcode">): string {
	return row.blocked ? "Blocked" : rcodeShortName(row.rcode);
}

/**
 * What a row's domain is wrapped in. The caller owns the routes, so it builds
 * the element; the look stays here, as `activityDomainLink`, which the caller
 * spreads onto the control itself — a link's own colour beats one inherited
 * from a wrapper. `id` is null for a streamed row, which history has no surface
 * for and live opens from memory.
 */
export type DomainRenderer = (id: number | null, children: ReactNode) => ReactNode;

export const activityDomainLink = styles.domainLink;

export function ResultCellContent({ row }: { row: Pick<QuerySummary, "blocked" | "rcode"> }) {
	const label = resultLabel(row);
	if (row.blocked) return <span {...stylex.props(styles.badge, styles.blockedBadge)}>{label}</span>;
	if (row.rcode !== 0) return <span {...stylex.props(styles.badge, styles.faultBadge)}>{label}</span>;
	return <span {...stylex.props(styles.small, styles.muted)}>{label}</span>;
}

export function ActivityCells({
	row,
	clientNames,
	renderDomain,
}: {
	row: QuerySummary;
	clientNames: ClientNames;
	renderDomain: DomainRenderer;
}) {
	return (
		<>
			<td {...stylex.props(styles.cell, styles.nowrap, styles.muted)}>{formatTime(row.ts)}</td>
			<td {...stylex.props(styles.cell, styles.small, styles.breakAll, shared.mono)}>
				{renderDomain(row.id, row.domain)}
			</td>
			<td {...stylex.props(styles.cell, styles.small, styles.nowrap)}>
				<ClientName ip={row.client_ip} names={clientNames} />
			</td>
			<td {...stylex.props(styles.cell, styles.nowrap)}>{qtypeName(row.qtype)}</td>
			<td {...stylex.props(styles.cell, styles.nowrap)}>
				<ResultCellContent row={row} />
			</td>
			<td {...stylex.props(styles.cell, styles.nowrap)}>{routeLabel(row.route_kind)}</td>
			<td {...stylex.props(styles.cell, styles.nowrap, shared.tabularNums)}>
				{row.response_time_us === null ? "—" : formatMicros(row.response_time_us)}
			</td>
		</>
	);
}

export const ACTIVITY_COLUMNS = ["Time", "Domain", "Client", "Type", "Result", "Route", "Duration"] as const;

export function ActivityTableHead() {
	return (
		<thead {...stylex.props(styles.head)}>
			<tr>
				{ACTIVITY_COLUMNS.map((column) => (
					<th key={column} {...stylex.props(styles.th)}>
						{column}
					</th>
				))}
			</tr>
		</thead>
	);
}
