/**
 * The Pause/Resume control, at the foot of the sidebar. A pause stops filtering
 * for the whole resolver, so it belongs to the shell rather than to any page,
 * and it has no second placement.
 *
 * It reads `Health.protection` rather than `/api/pause` so it cannot contradict
 * the Diagnostics health strip, and it renders nothing at all while protection
 * is unavailable or unknown — pausing a resolver that has no filter snapshot
 * would change nothing an operator could observe, and a state health has not
 * confirmed does not name an action either.
 *
 * A pause says so, wherever the control is. "Resume" alone names an action
 * without stating the state it would end, and with the header indicator and the
 * Overview status row both gone the sidebar is the only place most pages can
 * carry that fact at all: a paused resolver would otherwise leave no trace
 * outside the Diagnostics page. The line and the health strip cannot disagree —
 * one `protection` reading, one clock format, and the expiry refetch in
 * `useProtection` retires both at the same moment. An active resolver gets no
 * line: the button says Pause, which is the whole message.
 */

import { useEffect } from "react";
import { useMutation, useQueryClient } from "@tanstack/react-query";
import * as stylex from "@stylexjs/stylex";
import { Button, Menu, MenuItem, MenuTrigger, Popover } from "react-aria-components";
import { formatClock } from "@/lib/format";
import { pauseMutation } from "@/lib/queries";
import InlineError from "@/lib/InlineError";
import { styles as shared } from "@/ui/styles";
import { colors } from "@/ui/tokens.stylex";
import { useProtection } from "./protection";

const DURATIONS = [
	{ label: "60 seconds", seconds: 60 },
	{ label: "5 minutes", seconds: 300 },
	{ label: "30 minutes", seconds: 1800 },
	{ label: "Indefinitely", seconds: null },
] as const;

const styles = stylex.create({
	/**
	 * Disabled text darkens in light scheme and lightens in dark, the opposite
	 * direction from `textMuted`, so the token cannot express it.
	 */
	trigger: {
		color: {
			default: null,
			":disabled": "oklch(70.5% 0.015 286.067)",
			"@media (prefers-color-scheme: dark)": { default: null, ":disabled": "oklch(44.2% 0.017 285.786)" },
		},
		/** The sidebar foot is the only placement, and there Log out sets the width. */
		width: "100%",
	},
	row: {
		display: "flex",
		flexDirection: "column",
		alignItems: "flex-start",
		gap: "0.25rem",
	},
	/** Text, never a colour or an icon alone: this is the state, spelled out. */
	state: {
		fontSize: "0.75rem",
		lineHeight: "1rem",
		color: colors.textSecondary,
	},
	/** `--trigger-width` is RAC's: the menu is as wide as the button that opened it. */
	popover: {
		width: "var(--trigger-width)",
		borderRadius: "0.25rem",
		borderWidth: 1,
		borderStyle: "solid",
		borderColor: colors.border,
		backgroundColor: colors.surfaceRaised,
		color: colors.text,
		boxShadow: "0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1)",
	},
	menu: {
		outlineStyle: "none",
		paddingBlock: "0.25rem",
	},
	menuItem: {
		cursor: "pointer",
		paddingInline: "0.75rem",
		paddingBlock: "0.375rem",
		fontSize: "0.875rem",
		lineHeight: "1.25rem",
	},
	/**
	 * RAC focuses the item's own node, so the shared ring applies; it is inset
	 * because an item flush against the popover edge clips an outset one, and
	 * recoloured because the focus token is the blue this row just painted.
	 */
	menuItemFocused: {
		backgroundColor: colors.primary,
		color: colors.primaryText,
		outlineColor: { default: null, ":focus-visible": colors.primaryText },
	},
});

export default function PauseControl() {
	const queryClient = useQueryClient();
	const protection = useProtection();
	const mutation = useMutation(pauseMutation(queryClient));
	const paused = protection.state === "paused";
	const { reset } = mutation;
	useEffect(() => reset(), [paused, reset]);

	// Nothing to offer, by the same rule in both cases: unavailable has no action
	// worth taking, and unknown has no way to tell which of the two it would be.
	// A disabled "Pause" beside a reading that says "Paused until 14:05" names
	// the wrong action, which is the contradiction this control exists to end.
	const actionable = protection.state === "active" || protection.state === "paused";

	if (!actionable) return null;

	if (paused) {
		const until = protection.state === "paused" ? protection.until : null;
		return (
			<div {...stylex.props(styles.row)}>
				<p {...stylex.props(styles.state)}>
					{until === null ? "Paused" : `Paused until ${formatClock(until)}`}
				</p>
				<button
					type="button"
					onClick={() => mutation.mutate({ paused: false })}
					disabled={mutation.isPending}
					{...stylex.props(shared.button, styles.trigger, shared.focusRing)}
				>
					Resume
				</button>
				<InlineError error={mutation.error} />
			</div>
		);
	}

	return (
		<div {...stylex.props(styles.row)}>
			<MenuTrigger>
				<Button
					isDisabled={mutation.isPending}
					className={() => stylex.props(shared.button, styles.trigger, shared.focusRing).className ?? ""}
				>
					Pause
				</Button>
				<Popover className={() => stylex.props(styles.popover).className ?? ""}>
					<Menu {...stylex.props(styles.menu)}>
						{DURATIONS.map(({ label, seconds }) => (
							<MenuItem
								key={label}
								onAction={() =>
									mutation.mutate(
										seconds === null
											? { paused: true }
											: { paused: true, duration_seconds: seconds },
									)
								}
								className={({ isFocused }) =>
									stylex.props(
										styles.menuItem,
										shared.insetFocusRing,
										isFocused && styles.menuItemFocused,
									).className ?? ""
								}
							>
								{label}
							</MenuItem>
						))}
					</Menu>
				</Popover>
			</MenuTrigger>
			<InlineError error={mutation.error} />
		</div>
	);
}
