/**
 * The filter row over the Activity table.
 *
 * The applied state is the URL, never this form: what the reader sees is what
 * the link they can paste to a housemate will show. So this holds a draft only,
 * and the page remounts it whenever the applied search changes — a back button
 * or a pasted URL has to move the form with it, and a form that seeded itself
 * once would keep showing the previous investigation's filters.
 *
 * In live mode the row stays visible and disabled rather than disappearing: the
 * filters are retained in the URL and apply again the moment history comes
 * back, and hiding them would read as having lost them. The stream itself is
 * unfiltered — the server sends every query — so a row that looked usable here
 * would promise filtering that is not happening.
 */

import { useState, type FormEvent } from "react";
import * as stylex from "@stylexjs/stylex";
import Select from "@/ui/Select";
import { styles as shared } from "@/ui/styles";
import { colors } from "@/ui/tokens.stylex";
import { datetimeField, editDatetimeField, resolveDatetimeField, type DatetimeField } from "./datetime";
import type { ActivitySearch } from "./search";

const STATUS_OPTIONS = [
	{ value: "any", label: "All" },
	{ value: "blocked", label: "Blocked only" },
	{ value: "allowed", label: "Allowed only" },
];

const styles = stylex.create({
	/** One column on a phone, two from `sm`, five from `lg`. */
	grid: {
		marginTop: "1rem",
		display: "grid",
		gap: "0.75rem",
		gridTemplateColumns: {
			default: "repeat(1, minmax(0, 1fr))",
			"@media (min-width: 640px)": "repeat(2, minmax(0, 1fr))",
			"@media (min-width: 1024px)": "repeat(5, minmax(0, 1fr))",
		},
	},
	label: {
		display: "block",
		fontSize: "0.875rem",
		lineHeight: "1.25rem",
	},
	input: {
		marginTop: "0.25rem",
		width: "100%",
		// A disabled native input keeps its value legible but reads as inert,
		// matching what RAC does to the Select trigger beside it.
		cursor: { default: null, ":disabled": "not-allowed" },
		opacity: { default: null, ":disabled": 0.55 },
	},
	buttonRow: {
		display: "flex",
		alignItems: "flex-end",
		gap: "0.5rem",
		gridColumn: {
			default: null,
			"@media (min-width: 640px)": "span 2 / span 2",
			"@media (min-width: 1024px)": "span 5 / span 5",
		},
	},
	toolbarButton: {
		fontWeight: 500,
	},
	error: {
		marginTop: "0.5rem",
		fontSize: "0.875rem",
		lineHeight: "1.25rem",
		color: colors.dangerText,
	},
});

/** The applied filters, with `mode` left to the page that owns the switch. */
export type AppliedFilters = Omit<ActivitySearch, "mode">;

/** Every filter off — what Clear applies, and the loader's empty-filter case. */
export const NO_FILTERS: AppliedFilters = {
	domain: undefined,
	client: undefined,
	blocked: undefined,
	since: undefined,
	until: undefined,
};

function blockedOption(blocked: boolean | undefined): string {
	if (blocked === undefined) return "any";
	return blocked ? "blocked" : "allowed";
}

function optionBlocked(value: string): boolean | undefined {
	if (value === "blocked") return true;
	return value === "allowed" ? false : undefined;
}

function boundError(label: string, reason: "unparseable" | "nonexistent"): string {
	return reason === "unparseable"
		? `${label} is not a complete date and time.`
		: `${label} names a local time that does not exist — the clock jumps over it for daylight saving.`;
}

interface Props {
	applied: AppliedFilters;
	isDisabled: boolean;
	onApply: (filters: AppliedFilters) => void;
	onClear: () => void;
}

export default function ActivityFilters({ applied, isDisabled, onApply, onClear }: Props) {
	const [domain, setDomain] = useState(applied.domain ?? "");
	const [client, setClient] = useState(applied.client ?? "");
	const [blocked, setBlocked] = useState(blockedOption(applied.blocked));
	const [since, setSince] = useState<DatetimeField>(() => datetimeField(applied.since));
	const [until, setUntil] = useState<DatetimeField>(() => datetimeField(applied.until));
	const [error, setError] = useState<string | null>(null);

	function submit(event: FormEvent) {
		event.preventDefault();
		const sinceValue = resolveDatetimeField(since);
		if (!sinceValue.ok) {
			setError(boundError("Since", sinceValue.reason));
			return;
		}
		const untilValue = resolveDatetimeField(until);
		if (!untilValue.ok) {
			setError(boundError("Until", untilValue.reason));
			return;
		}
		setError(null);
		onApply({
			domain: domain.trim() === "" ? undefined : domain.trim(),
			client: client.trim() === "" ? undefined : client.trim(),
			blocked: optionBlocked(blocked),
			since: sinceValue.value,
			until: untilValue.value,
		});
	}

	function clear() {
		setDomain("");
		setClient("");
		setBlocked("any");
		setSince(datetimeField(undefined));
		setUntil(datetimeField(undefined));
		setError(null);
		onClear();
	}

	return (
		<>
			<form onSubmit={submit} {...stylex.props(styles.grid)}>
				<label {...stylex.props(styles.label)}>
					Domain contains
					<input
						type="text"
						value={domain}
						disabled={isDisabled}
						onChange={(event) => setDomain(event.target.value)}
						{...stylex.props(shared.smallInput, styles.input, shared.focusRing)}
					/>
				</label>
				<label {...stylex.props(styles.label)}>
					Client (exact)
					<input
						type="text"
						value={client}
						disabled={isDisabled}
						onChange={(event) => setClient(event.target.value)}
						{...stylex.props(shared.smallInput, styles.input, shared.focusRing)}
					/>
				</label>
				<Select
					variant="compactField"
					label="Result"
					value={blocked}
					isDisabled={isDisabled}
					onChange={setBlocked}
					options={STATUS_OPTIONS}
				/>
				<label {...stylex.props(styles.label)}>
					Since
					<input
						type="datetime-local"
						step={1}
						value={since.text}
						disabled={isDisabled}
						onChange={(event) => setSince(editDatetimeField(since, event.target.value))}
						{...stylex.props(shared.smallInput, styles.input, shared.focusRing)}
					/>
				</label>
				<label {...stylex.props(styles.label)}>
					Until
					<input
						type="datetime-local"
						step={1}
						value={until.text}
						disabled={isDisabled}
						onChange={(event) => setUntil(editDatetimeField(until, event.target.value))}
						{...stylex.props(shared.smallInput, styles.input, shared.focusRing)}
					/>
				</label>
				<div {...stylex.props(styles.buttonRow)}>
					<button
						type="submit"
						disabled={isDisabled}
						{...stylex.props(shared.button, styles.toolbarButton, shared.focusRing)}
					>
						Apply filters
					</button>
					<button
						type="button"
						onClick={clear}
						disabled={isDisabled}
						{...stylex.props(shared.button, styles.toolbarButton, shared.focusRing)}
					>
						Clear
					</button>
				</div>
			</form>
			{error !== null && (
				<p role="alert" {...stylex.props(styles.error)}>
					{error}
				</p>
			)}
		</>
	);
}
