/**
 * Activity: one surface over the queries nxdns answered, in two modes.
 *
 * History reads the persisted log and Live reads the stream, but they are the
 * same seven columns over the same filters, and the reader moves between them
 * without losing the question they were asking. The mode lives in the URL with
 * the filters, so an investigation is one link — including which half of it the
 * recipient should be looking at.
 */

import { useCallback } from "react";
import { Link, useNavigate, useSearch } from "@tanstack/react-router";
import * as stylex from "@stylexjs/stylex";
import { Tab, TabList, TabPanel, Tabs } from "react-aria-components";
import { styles as shared } from "@/ui/styles";
import { colors } from "@/ui/tokens.stylex";
import ActivityFilters, { NO_FILTERS, type AppliedFilters } from "./ActivityFilters";
import HistoryActivity from "./HistoryActivity";
import LiveActivity from "./LiveActivity";
import type { ActivityMode } from "./search";

const MODES: ReadonlyArray<{ mode: ActivityMode; label: string }> = [
	{ mode: "history", label: "History" },
	{ mode: "live", label: "Live" },
];

const styles = stylex.create({
	header: {
		display: "flex",
		flexWrap: "wrap",
		alignItems: "center",
		gap: "0.75rem",
	},
	heading: {
		fontSize: "1.5rem",
		lineHeight: "2rem",
		fontWeight: 600,
	},
	switch: {
		display: "flex",
		gap: "0.25rem",
		borderRadius: "0.25rem",
		borderWidth: 1,
		borderStyle: "solid",
		borderColor: colors.border,
		padding: "0.125rem",
	},
	modeButton: {
		borderStyle: "none",
		borderRadius: "0.1875rem",
		paddingInline: "0.75rem",
		paddingBlock: "0.25rem",
		fontSize: "0.875rem",
		lineHeight: "1.25rem",
		fontWeight: 500,
		cursor: "pointer",
	},
	/** A Tab is a `div` with a roving tabindex, so RAC drives the ring, not `:focus-visible`. */
	modeFocusVisible: {
		outlineWidth: 2,
		outlineStyle: "solid",
		outlineColor: colors.focus,
		outlineOffset: 2,
	},
	modeIdle: {
		backgroundColor: { default: "transparent", ":hover": colors.surfaceHover },
		color: { default: colors.textSecondary, ":hover": colors.text },
	},
	/** The selected mode reads as a filled chip, the same weight the nav uses. */
	modeSelected: {
		backgroundColor: colors.primary,
		color: colors.primaryText,
	},
	/** The one way into the simulation from here, so it cannot sit behind a row. */
	simulationLink: {
		marginInlineStart: "auto",
		fontSize: "0.875rem",
		lineHeight: "1.25rem",
		color: colors.primaryOnSurface,
		textDecorationLine: "none",
	},
	liveNote: {
		marginTop: "0.5rem",
		fontSize: "0.875rem",
		lineHeight: "1.25rem",
		color: colors.textMuted,
	},
	panel: {
		outlineStyle: "none",
	},
	/**
	 * A panel with nothing tabbable in it — an empty or loading history — is given
	 * a tabindex by RAC so the reader can still reach its content, so it has to be
	 * able to show that it holds the focus.
	 */
	panelFocusVisible: {
		outlineWidth: 2,
		outlineStyle: "solid",
		outlineColor: colors.focus,
		outlineOffset: 2,
	},
	/** Explicit, so RAC's default `react-aria-Tabs` class does not land instead. */
	tabsRoot: {
		display: "block",
	},
});

function panelClass({ isFocusVisible }: { isFocusVisible: boolean }): string {
	return stylex.props(styles.panel, isFocusVisible && styles.panelFocusVisible).className ?? "";
}

export default function ActivityPage() {
	const search = useSearch({ from: "/shell/activity" });
	const navigate = useNavigate({ from: "/activity" });
	// The functional form, not a replacement object: the filters are retained
	// across a mode switch on purpose, and spelling out a new search here would
	// drop every one of them on the way to Live and back.
	function selectMode(mode: ActivityMode) {
		if (mode === search.mode) return;
		void navigate({ search: (prev) => ({ ...prev, mode }) });
	}

	// A patch, merged into whatever the URL already says: a segment click must not
	// spell out the four filters it is not about. `replace` is the debounced text
	// commits, so the back button steps between investigations, not keystrokes.
	const apply = useCallback(
		(patch: Partial<AppliedFilters>, replace = false) => {
			void navigate({ search: (prev) => ({ ...prev, ...patch }), replace });
		},
		[navigate],
	);

	const clear = useCallback(() => {
		void navigate({ search: (prev) => ({ mode: prev.mode, ...NO_FILTERS }) });
	}, [navigate]);

	return (
		<section>
			{/*
			 * The selected tab is the URL's `mode` and nothing else. RAC would hold
			 * the selection itself, but a second copy of it would fight the back
			 * button, so the search parameter stays the only state there is.
			 */}
			<Tabs
				selectedKey={search.mode}
				onSelectionChange={(key) => selectMode(key as ActivityMode)}
				className={() => stylex.props(styles.tabsRoot).className ?? ""}
			>
				<div {...stylex.props(styles.header)}>
					<h1 {...stylex.props(styles.heading)}>Activity</h1>
					<TabList aria-label="Activity mode" className={() => stylex.props(styles.switch).className ?? ""}>
						{MODES.map((option) => (
							<Tab
								key={option.mode}
								id={option.mode}
								className={({ isSelected, isFocusVisible }) =>
									stylex.props(
										styles.modeButton,
										isSelected ? styles.modeSelected : styles.modeIdle,
										isFocusVisible && styles.modeFocusVisible,
									).className ?? ""
								}
							>
								{option.label}
							</Tab>
						))}
					</TabList>
					<Link to="/activity/test" {...stylex.props(styles.simulationLink, shared.focusRing)}>
						Current policy simulation
					</Link>
				</div>

				{/*
				 * The toolbar belongs to History alone. It is not remounted on a search
				 * change: it resyncs its draft from the URL instead, because a remount
				 * mid-debounce would take the focus out of the input being typed in.
				 */}
				<TabPanel id="history" className={panelClass}>
					<ActivityFilters applied={search} onApply={apply} onClear={clear} />
					<HistoryActivity search={search} />
				</TabPanel>
				<TabPanel id="live" className={panelClass}>
					<p {...stylex.props(styles.liveNote)}>
						The stream carries every query the server answers; the History filters apply to history only.
					</p>
					<LiveActivity origin={search} />
				</TabPanel>
			</Tabs>
		</section>
	);
}
