import { useState, type FormEvent, type ReactNode } from "react";
import { useQuery, useSuspenseQuery } from "@tanstack/react-query";
import { useSearch } from "@tanstack/react-router";
import * as stylex from "@stylexjs/stylex";
import { ApiError } from "@/lib/api";
import { groupsQuery, lookupQuery } from "@/lib/queries";
import type { Group, LookupResult } from "@/lib/types";
import { defaultGroupId } from "@/lib/defaultGroup";
import Select from "@/ui/Select";
import { styles as shared } from "@/ui/styles";
import { colors } from "@/ui/tokens.stylex";

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

interface Submitted {
	domain: string;
	groupId: number;
}

interface Verdict {
	label: string;
	tone: "local" | "blocked" | "forwarded" | "allowed";
	description: string;
}

const styles = stylex.create({
	heading: {
		fontSize: "1.5rem",
		lineHeight: "2rem",
		fontWeight: 600,
	},
	intro: {
		marginTop: "0.5rem",
		color: colors.textMuted,
	},
	form: {
		marginTop: "1.5rem",
		display: "flex",
		maxWidth: "42rem",
		flexWrap: "wrap",
		alignItems: "flex-end",
		gap: "0.75rem",
	},
	domainField: {
		minWidth: "14rem",
		flexGrow: 1,
	},
	fieldLabel: {
		display: "block",
		fontSize: "0.875rem",
		lineHeight: "1.25rem",
		fontWeight: 500,
	},
	note: {
		marginTop: "1.5rem",
		color: colors.textMuted,
	},
	error: {
		marginTop: "1.5rem",
		fontSize: "0.875rem",
		lineHeight: "1.25rem",
		color: colors.danger,
	},
	card: {
		marginTop: "1.5rem",
		borderRadius: "0.25rem",
		borderWidth: 1,
		borderStyle: "solid",
		borderColor: colors.border,
	},
	banner: {
		borderStartStartRadius: "0.25rem",
		borderStartEndRadius: "0.25rem",
		paddingInline: "1rem",
		paddingBlock: "0.75rem",
	},
	/** Four verdicts need four tints; only "blocked" maps onto a token role. */
	local: {
		backgroundColor: { default: "oklch(93.2% 0.032 255.585)", [DARK]: "oklch(28.2% 0.091 267.935)" },
		color: { default: "oklch(42.4% 0.199 265.638)", [DARK]: "oklch(80.9% 0.105 251.813)" },
	},
	blocked: {
		backgroundColor: { default: "oklch(93.6% 0.032 17.717)", [DARK]: "oklch(25.8% 0.092 26.042)" },
		color: { default: "oklch(44.4% 0.177 26.899)", [DARK]: "oklch(80.8% 0.114 19.571)" },
	},
	forwarded: {
		backgroundColor: { default: "oklch(96.2% 0.059 95.617)", [DARK]: "oklch(27.9% 0.077 45.635)" },
		color: { default: "oklch(47.3% 0.137 46.201)", [DARK]: "oklch(87.9% 0.169 91.605)" },
	},
	allowed: {
		backgroundColor: { default: "oklch(96.2% 0.044 156.743)", [DARK]: "oklch(26.6% 0.065 152.934)" },
		color: { default: "oklch(44.8% 0.119 151.328)", [DARK]: "oklch(87.1% 0.15 154.449)" },
	},
	verdictLabel: {
		fontSize: "1.125rem",
		lineHeight: "1.75rem",
		fontWeight: 600,
	},
	verdictDescription: {
		fontSize: "0.875rem",
		lineHeight: "1.25rem",
	},
	details: {
		paddingInline: "1rem",
		paddingBlock: "0.5rem",
		fontSize: "0.875rem",
		lineHeight: "1.25rem",
	},
	/** `divide-y`: a hairline between rows, so the first row carries none. */
	detailRow: {
		display: "flex",
		gap: "1rem",
		paddingBlock: "0.5rem",
		borderTopWidth: { default: 1, ":first-child": 0 },
		borderTopStyle: "solid",
		borderTopColor: colors.border,
	},
	detailTerm: {
		width: "10rem",
		flexShrink: 0,
		color: colors.textMuted,
	},
	detailValue: {
		minWidth: 0,
		overflowWrap: "break-word",
	},
	sourceLink: {
		color: colors.primaryOnSurface,
		textDecorationLine: "underline",
	},
});

function toneStyle(tone: Verdict["tone"]) {
	if (tone === "local") return styles.local;
	if (tone === "blocked") return styles.blocked;
	return tone === "forwarded" ? styles.forwarded : styles.allowed;
}

/**
 * Header priority follows the pipeline order the lookup handler documents
 * (PLAN §6): local records answer first, then the block decision, then
 * forward zones, then plain forwarding to the upstream pool.
 */
export function verdictOf(result: LookupResult): Verdict {
	if (result.local_records) {
		return {
			label: "Local answer",
			tone: "local",
			description: "A local record answers this name directly.",
		};
	}
	if (result.blocked) {
		return {
			label: "Blocked",
			tone: "blocked",
			description: "Queries for this name get a blocked response.",
		};
	}
	if (result.forward_zone !== null) {
		return {
			label: "Forwarded",
			tone: "forwarded",
			description: `Queries go to the resolver for zone ${result.forward_zone}.`,
		};
	}
	return {
		label: "Allowed",
		tone: "allowed",
		description: "Queries resolve through the upstream pool.",
	};
}

function errorMessage(error: unknown): string {
	if (error instanceof ApiError) {
		if (error.status === 503) {
			return "No filter snapshot is loaded yet — the server is starting or degraded. Try again shortly.";
		}
		if (error.status === 429) {
			return error.retryAfter !== undefined
				? `Rate limited. Try again in ${error.retryAfter}s.`
				: "Rate limited. Try again shortly.";
		}
		return error.message;
	}
	return "Could not reach the server.";
}

function DetailRow({ label, children }: { label: string; children: ReactNode }) {
	return (
		<div {...stylex.props(styles.detailRow)}>
			<dt {...stylex.props(styles.detailTerm)}>{label}</dt>
			<dd {...stylex.props(styles.detailValue)}>{children}</dd>
		</div>
	);
}

function VerdictCard({ result, groups }: { result: LookupResult; groups: Group[] }) {
	const verdict = verdictOf(result);
	const groupName = groups.find((group) => group.id === result.group_id)?.name ?? `#${result.group_id}`;

	return (
		<div {...stylex.props(styles.card)}>
			<div {...stylex.props(styles.banner, toneStyle(verdict.tone))}>
				<h2 {...stylex.props(styles.verdictLabel)}>{verdict.label}</h2>
				<p {...stylex.props(styles.verdictDescription)}>{verdict.description}</p>
			</div>
			<dl {...stylex.props(styles.details)}>
				<DetailRow label="Domain">
					<span {...stylex.props(shared.mono)}>{result.domain}</span>
				</DetailRow>
				<DetailRow label="Group">{groupName}</DetailRow>
				<DetailRow label="Local record">{result.local_records ? "Yes" : "No"}</DetailRow>
				<DetailRow label="Forward zone">
					{result.forward_zone !== null ? (
						<span {...stylex.props(shared.mono)}>{result.forward_zone}</span>
					) : (
						"—"
					)}
				</DetailRow>
				<DetailRow label="Blocked">{result.blocked ? "Yes" : "No"}</DetailRow>
				<DetailRow label="Reason">
					<span {...stylex.props(shared.mono)}>{result.reason}</span>
				</DetailRow>
				<DetailRow label="Matched pattern">
					{result.matched !== "" ? <span {...stylex.props(shared.mono)}>{result.matched}</span> : "—"}
				</DetailRow>
				<DetailRow label="Blocklist source">
					{result.source_url !== null ? (
						<a
							href={result.source_url}
							target="_blank"
							rel="noreferrer"
							{...stylex.props(styles.sourceLink, shared.focusRing)}
						>
							{result.source_url}
						</a>
					) : (
						"—"
					)}
				</DetailRow>
				<DetailRow label="Safe search rewrite">
					{result.safe_search_rewrite !== null ? (
						<span {...stylex.props(shared.mono)}>{result.safe_search_rewrite}</span>
					) : (
						"—"
					)}
				</DetailRow>
			</dl>
		</div>
	);
}

export default function PolicyTestPage() {
	const groups = useSuspenseQuery(groupsQuery()).data;
	const preselectedGroupId = defaultGroupId(groups);

	// A `?domain=` link (from a query's detail page) arrives already asking the
	// question, so it runs the simulation rather than leaving a filled-in form.
	const search = useSearch({ from: "/shell/activity/test" });
	const [domain, setDomain] = useState(search.domain ?? "");
	const [groupId, setGroupId] = useState(preselectedGroupId);
	const [submitted, setSubmitted] = useState<Submitted | null>(
		search.domain === undefined ? null : { domain: search.domain, groupId: preselectedGroupId },
	);

	const lookup = useQuery({
		...lookupQuery(submitted?.domain ?? "", submitted?.groupId),
		enabled: submitted !== null,
	});

	function onSubmit(event: FormEvent<HTMLFormElement>) {
		event.preventDefault();
		const trimmed = domain.trim();
		if (trimmed === "") return;
		if (submitted !== null && submitted.domain === trimmed && submitted.groupId === groupId) {
			void lookup.refetch();
			return;
		}
		setSubmitted({ domain: trimmed, groupId });
	}

	return (
		<section>
			<h1 {...stylex.props(styles.heading)}>Current policy simulation</h1>
			<p {...stylex.props(styles.intro)}>
				What the pipeline <em>would</em> do with a domain right now: local records, forward zones, block
				decision, safe search. This reads the configuration in force at this moment, so it explains nothing
				about a query already answered — a detail page does that.
			</p>
			<form onSubmit={onSubmit} {...stylex.props(styles.form)}>
				<div {...stylex.props(styles.domainField)}>
					<label htmlFor="policy-test-domain" {...stylex.props(styles.fieldLabel)}>
						Domain
					</label>
					<input
						id="policy-test-domain"
						required
						value={domain}
						onChange={(event) => setDomain(event.target.value)}
						placeholder="ads.example.com"
						{...stylex.props(shared.input, shared.focusRing)}
					/>
				</div>
				<div>
					<Select
						label="Group"
						value={String(groupId)}
						onChange={(value) => setGroupId(Number(value))}
						options={groups.map((group) => ({ value: String(group.id), label: group.name }))}
					/>
				</div>
				<button
					type="submit"
					disabled={lookup.isFetching}
					{...stylex.props(shared.largePrimaryButton, shared.focusRing)}
				>
					Simulate
				</button>
			</form>
			{lookup.isFetching && <p {...stylex.props(styles.note)}>Simulating…</p>}
			{!lookup.isFetching && lookup.isError && (
				<p role="alert" {...stylex.props(styles.error)}>
					{errorMessage(lookup.error)}
				</p>
			)}
			{!lookup.isFetching && lookup.data !== undefined && !lookup.isError && (
				<VerdictCard result={lookup.data} groups={groups} />
			)}
		</section>
	);
}
