import { __, _n, sprintf } from '@wordpress/i18n';
import React, { useEffect, useState } from 'react';
import { api } from '../api';
import { Card, Button, Badge, Notice, Switch, Segmented, Advisor, Panel, MetricGrid, PageState } from '../components/ui';
import Explain from '../components/Explain';
import AgencyFloor from '../components/AgencyFloor';
import ProposalQueue from '../components/ProposalQueue';
import { IconRefresh, IconBot } from '../components/icons';

/** "a, b and c" — because a verdict is a sentence, not an array dump. */
const listJoin = (items) =>
	items.length <= 1 ? items.join('') : sprintf(/* translators: 1: all items but the last, comma-separated. 2: the last item. */ __('%1$s and %2$s', 'ai-rank-suite'), items.slice(0, -1).join(', '), items[items.length - 1]);

/**
 * The autopilot.
 *
 * Every other AI feature in this plugin is a button on one page. That works
 * for one page and is useless for four hundred — which is why, on a real site,
 * the AI features were the ones nobody ever used. This screen is the same
 * capabilities pointed at the whole site.
 */
export default function Autopilot() {
	const [data, setData] = useState(null);
	const [busy, setBusy] = useState(false);
	const [error, setError] = useState(null);
	const [tasks, setTasks] = useState(['description', 'faq', 'title']);
	// Safety level and policy. `mode` is derived for the older copy below.
	const [level, setLevel] = useState('suggest');
	const [maxChanges, setMaxChanges] = useState(0);
	const [budgetUsd, setBudgetUsd] = useState(0);
	const mode = level === 'auto' ? 'apply' : 'draft';
	const [types, setTypes] = useState([]);
	const [limit, setLimit] = useState(25);
	// Undo shares `busy` with the run loop, but the verdict must not claim a
	// run is going while what is actually happening is the opposite of one.
	const [undoing, setUndoing] = useState(false);
	const load = () =>
		api
			.get('/autopilot/status')
			.then((next) => {
				setData(next);
				if (!next.running) setBusy(false);
			})
			.catch((e) => setError(e.message));
	useEffect(() => {
		load();
	}, []);

	// Work runs in bounded server-side slices. This timer only reads progress;
	// closing the tab no longer stops the queue or spends another model call.
	useEffect(() => {
		if (!busy && !data?.running) return undefined;
		const id = setInterval(load, 5000);
		return () => clearInterval(id);
	}, [busy, data?.running]);

	if (!data) return <PageState error={error} />;

	const s = data.state || {};
	const total = s.total || 0;
	const done = s.done || 0;
	const pct = total > 0 ? Math.round((done / total) * 100) : 0;

	/** Plan once; WordPress cron works through the persisted queue. */
	const run = async () => {
		setBusy(true);
		setError(null);
		// The toolbar light listens for this, so it turns on with the button
		// rather than on the next page load.
		window.dispatchEvent(new CustomEvent('airanksuite:working'));
		try {
			const next = await api.post('/autopilot/plan', { tasks, level, max_changes: maxChanges, budget_usd: budgetUsd, post_types: types, limit });
			setData(next);
			if (!next.running) setBusy(false);
		} catch (e) {
			setError(e.message);
			setBusy(false);
		}
	};

	const resume = async () => {
		setBusy(true);
		setError(null);
		try {
			const next = await api.post('/autopilot/resume');
			setData(next);
			if (!next.running) setBusy(false);
		} catch (e) {
			setError(e.message);
			setBusy(false);
		}
	};

	const cancel = async () => {
		setData(await api.post('/autopilot/cancel'));
		setBusy(false);
	};

	const undo = async () => {
		setBusy(true);
		setUndoing(true);
		setError(null);
		try {
			const next = await api.post('/autopilot/undo', { since: s.started || 0 });
			setData(next);
		} catch (e) {
			setError(e.message);
		} finally {
			setBusy(false);
			setUndoing(false);
		}
	};

	const toggleTask = (id) => setTasks(tasks.includes(id) ? tasks.filter((t) => t !== id) : [...tasks, id]);
	const toggleType = (name) => setTypes(types.includes(name) ? types.filter((t) => t !== name) : [...types, name]);

	/**
	 * The screen's own conclusion, before its evidence.
	 *
	 * This screen used to open on the wiring diagram — a pleasure to look at
	 * once, and not what anybody came for. What they came for is one of four
	 * sentences: connect an engine, resume the unfinished run, stop the run
	 * that is going, or here is exactly what the next run will do. Each with
	 * the one button that resolves it.
	 */
	const taskDefs = data.tasks || {};
	const verdict = (() => {
		if (!data.has_key) {
			return {
				tone: 'warning',
				text: __('No AI engine is connected, so the autopilot has nothing to write with.', 'ai-rank-suite'),
				why: __('One key, stored encrypted on your own server, switches it on — descriptions, FAQs and titles across the whole site.', 'ai-rank-suite'),
				action: __('Connect an engine', 'ai-rank-suite'),
				onAction: () => {
					window.location.hash = '/ai';
				},
			};
		}
		if (undoing) {
			return {
				tone: 'neutral',
				text: __('Undoing the last run…', 'ai-rank-suite'),
				why: __('Every value the run replaced is being put back exactly as it was.', 'ai-rank-suite'),
			};
		}
		if (busy || data.running) {
			return {
				tone: 'neutral',
				text: sprintf(/* translators: 1: done, 2: total. */ __('Writing — %1$d of %2$d AI tasks done.', 'ai-rank-suite'), done, total),
				why: __('The queue runs safely on the server. You can close this tab; stopping keeps everything already written.', 'ai-rank-suite'),
				action: __('Stop', 'ai-rank-suite'),
				onAction: cancel,
			};
		}
		if (data.stalled) {
			return {
				tone: 'warning',
				text: __('A previous run was left unfinished.', 'ai-rank-suite'),
				why: __('The server queue paused before every planned task finished. Resuming picks up where it left off — nothing already written is repeated.', 'ai-rank-suite'),
				action: __('Resume the run', 'ai-rank-suite'),
				onAction: resume,
			};
		}
		if (tasks.length === 0) {
			return {
				tone: 'neutral',
				text: __('Nothing is selected for the autopilot to write.', 'ai-rank-suite'),
				why: __('Turn on at least one task under “What it should write” below.', 'ai-rank-suite'),
			};
		}
		const names = tasks.map((id) => taskDefs[id]?.label || id);
		return {
			tone: 'neutral',
			text: sprintf(__('The next run will write %s for every page missing them.', 'ai-rank-suite'), listJoin(names)),
			why:
				mode === 'apply'
					? __('Changes apply directly. Every write is logged with the value it replaced, so the whole run can be undone — and it never overwrites something you wrote.', 'ai-rank-suite')
					: __('Nothing is published — the suggestions are parked on each page for you to accept or ignore.', 'ai-rank-suite'),
			action: __('Run the autopilot', 'ai-rank-suite'),
			onAction: run,
		};
	})();

	return (
		<div className="ars-page">
			{error && <Notice tone="serious">{error}</Notice>}

			<Advisor
				tone={verdict.tone}
				text={verdict.text}
				sub={verdict.why}
				action={verdict.action}
				onAction={verdict.onAction}
			/>

			{/*
			 * The safety level, first and unmissable.
			 *
			 * It lived in a collapsed panel next to the task checkboxes, which
			 * put the single decision that governs whether this agent writes to
			 * a live site behind a disclosure triangle. Four cards, each stating
			 * plainly what it will and will not do, and the run button reading
			 * back the level it is about to use.
			 */}
			<Card
				title={__('How much the autopilot may do', 'ai-rank-suite')}
				subtitle={__('Every level below Controlled auto parks its work for review. Nothing is written to a live page except at the last one.', 'ai-rank-suite')}
				action={
					<Switch
						checked={!!data.killed}
						onChange={async (v) => {
							try {
								setData(await api.post('/autopilot/kill', { on: v }));
							} catch (e) {
								setError(e.message);
							}
						}}
						label={__('Kill switch', 'ai-rank-suite')}
					/>
				}
			>
				{data.killed && (
					<Notice tone="serious">{__('The kill switch is on: no run may write to a live page, whatever level is selected. Proposals are still made.', 'ai-rank-suite')}</Notice>
				)}

				<div className="ars-levels">
					{[
						{
							id: 'observe',
							name: __('Observe', 'ai-rank-suite'),
							one: __('Plans and prices the work', 'ai-rank-suite'),
							detail: __('A dry run. Matches the pages, counts the calls and estimates the cost. No model is called and nothing is written.', 'ai-rank-suite'),
							writes: false,
						},
						{
							id: 'suggest',
							name: __('Suggest', 'ai-rank-suite'),
							one: __('Writes proposals, not pages', 'ai-rank-suite'),
							detail: __('Calls the engine and parks every result beside its page. Nothing on the live site changes until you apply it.', 'ai-rank-suite'),
							writes: false,
						},
						{
							id: 'approve',
							name: __('Approve', 'ai-rank-suite'),
							one: __('Proposals plus a review queue', 'ai-rank-suite'),
							detail: __('As Suggest, and every proposal lands in the queue below where you apply it — as written or edited — or reject it.', 'ai-rank-suite'),
							writes: false,
						},
						{
							id: 'auto',
							name: __('Controlled auto', 'ai-rank-suite'),
							one: __('Writes directly, within limits', 'ai-rank-suite'),
							detail: __('Only descriptions, FAQs and titles on pages that had none. Capped per run, logged with the previous value, undoable, stopped instantly by the kill switch.', 'ai-rank-suite'),
							writes: true,
						},
					].map((l) => (
						<button
							key={l.id}
							type="button"
							className={`ars-level${level === l.id ? ' is-active' : ''}${l.writes ? ' is-writing' : ''}`}
							onClick={() => setLevel(l.id)}
							aria-pressed={level === l.id}
						>
							<span className="ars-level-head">
								<span className="ars-level-name">{l.name}</span>
								<span className={`ars-level-tag${l.writes ? ' is-writing' : ''}`}>
									{l.writes ? __('writes live', 'ai-rank-suite') : __('no live writes', 'ai-rank-suite')}
								</span>
							</span>
							<span className="ars-level-one">{l.one}</span>
							<span className="ars-level-detail">{l.detail}</span>
						</button>
					))}
				</div>

				{level === 'auto' && (
					<div className="ars-level-policy">
						<label className="grid gap-1">
							<span className="t-caption">{__('Stop after this many changes (0 = the plan\'s own limit)', 'ai-rank-suite')}</span>
							<input type="number" min="0" value={maxChanges} onChange={(e) => setMaxChanges(Number(e.target.value))} />
						</label>
						<label className="grid gap-1">
							<span className="t-caption">{__('Stop at this spend, USD (0 = the per-job budget)', 'ai-rank-suite')}</span>
							<input type="number" min="0" step="0.5" value={budgetUsd} onChange={(e) => setBudgetUsd(Number(e.target.value))} />
						</label>
					</div>
				)}

				<div className="flex items-center gap-3 flex-wrap mt-1">
					<Button onClick={run} busy={busy} disabled={data.running || data.killed === true && level === 'auto'}>
						{level === 'observe' ? __('Plan and estimate', 'ai-rank-suite') : level === 'auto' ? __('Run and write', 'ai-rank-suite') : __('Run and propose', 'ai-rank-suite')}
					</Button>
					<span className="t-caption">
						{sprintf(__('%1$d of %2$d tasks · up to %3$d pages', 'ai-rank-suite'), tasks.length, Object.keys(taskDefs).length, limit)}
						{types.length > 0 ? ` · ${sprintf(_n('%d type', '%d types', types.length, 'ai-rank-suite'), types.length)}` : ` · ${__('all types', 'ai-rank-suite')}`}
					</span>
				</div>
			</Card>

			{(busy || data.running || total > 0) && (
				<Card title={__('The run', 'ai-rank-suite')} subtitle={__('What the plan matched, and what has happened to it', 'ai-rank-suite')}>
					<div className="grid gap-4">
						<MetricGrid
							columns={4}
							metrics={[
								{
									id: 'queued',
									label: __('AI tasks queued', 'ai-rank-suite'),
									hint: __('One paid model call per queue item. The queue survives a closed tab and never runs two items at once.', 'ai-rank-suite'),
									value: total,
									sub: data.running ? __('server queue active', 'ai-rank-suite') : __('in the last plan', 'ai-rank-suite'),
								},
								{
									id: 'written',
									label: __('Written', 'ai-rank-suite'),
									hint: __('Values the engine produced and that were applied or parked, depending on the mode.', 'ai-rank-suite'),
									value: s.written || 0,
								},
								{
									id: 'skipped',
									label: __('Skipped', 'ai-rank-suite'),
									hint: __('These pages already had a value — the autopilot never overwrites a sentence a person wrote.', 'ai-rank-suite'),
									value: s.skipped || 0,
								},
								{
									id: 'failed',
									label: __('Failed', 'ai-rank-suite'),
									hint: __('The engine refused or errored. The page is left untouched and listed below.', 'ai-rank-suite'),
									value: s.failed || 0,
									accent: s.failed > 0 ? 'var(--ars-c-red-ink)' : undefined,
									sub: s.failed > 0 ? __('the engine refused', 'ai-rank-suite') : __('no errors', 'ai-rank-suite'),
								},
							]}
						/>

						<div>
							<div
								className="ars-r-sm overflow-hidden"
								style={{ height: 8, background: 'var(--ars-surface-3)' }}
							>
								<div
									style={{
										width: `${pct}%`,
										height: '100%',
										background: 'var(--ars-grad)',
										transition: 'width 320ms cubic-bezier(0.2,0,0,1)',
									}}
								/>
							</div>
							<p className="m-0 mt-1.5 t-caption">
								{sprintf(__('%1$d of %2$d AI tasks across %3$d pages', 'ai-rank-suite'), done, total, s.planned_pages || 0)}
								{data.running ? ` — ${sprintf(__('server queue active · up to %d AI calls planned', 'ai-rank-suite'), s.planned_calls || 0)}` : ''}
								{s.level ? ` · ${sprintf(__('level: %s', 'ai-rank-suite'), s.level)}` : ''}
								{s.estimate?.calls ? ` · ${sprintf(/* translators: 1: number of AI calls. 2: cost estimate. */ __('estimated %1$s calls ≈ %2$s', 'ai-rank-suite'), s.estimate.calls, s.estimate.cost)}` : ''}
								{s.stopped ? ` · ${sprintf(__('stopped: %s', 'ai-rank-suite'), s.stopped.replace('_', ' '))}` : ''}
							</p>
						</div>
					</div>
				</Card>
			)}
			{s.level === 'observe' && s.finished > 0 && (
				<Notice tone="neutral">
					{sprintf(
						/* translators: 1: AI calls, 2: pages, 3: estimated cost. */
						__('Dry run complete: %1$s AI calls across %2$s pages would cost about %3$s. Nothing was called or written. Switch the level to run for real.', 'ai-rank-suite'),
						(s.total ?? 0).toLocaleString(),
						(s.planned_pages || 0).toLocaleString(),
						s.estimate?.cost ?? ''
					)}
				</Notice>
			)}
			<ProposalQueue refreshKey={`${s.finished || 0}-${s.done || 0}`} />
			{(s.errors || []).length > 0 && (
				<Notice tone="serious">
					{s.errors.length} page{s.errors.length === 1 ? '' : 's'} failed — most recently “{s.errors[s.errors.length - 1].title}”:{' '}
					{s.errors[s.errors.length - 1].error}
				</Notice>
			)}

			<Card
				title={__('What the agent changed', 'ai-rank-suite')}
				subtitle={__('Every write, with the value that was there before it', 'ai-rank-suite')}
				action={
					(data.log || []).some((l) => l.status === 'applied') && (
						<Button variant="ghost" onClick={undo} busy={busy}>
							<IconRefresh size={13} />{__('Undo the last run', 'ai-rank-suite')}</Button>
					)
				}
				flush
			>
				{(data.log || []).length === 0 ? (
					<p className="m-0 p-5 text-[13px]" style={{ color: 'var(--ars-ink-2)' }}>{__('Nothing yet. Nothing is written until you press the button.', 'ai-rank-suite')}</p>
				) : (
					<div className="ars-table-wrap">
						<table className="ars-table">
							<thead>
								<tr>
									<th style={{ width: 220 }}>{__('Page', 'ai-rank-suite')}</th>
									<th className="is-mid">{__('What', 'ai-rank-suite')}</th>
									<th>{__('Before', 'ai-rank-suite')}</th>
									<th>{__('After', 'ai-rank-suite')}</th>
									<th className="is-mid">{__('Status', 'ai-rank-suite')}</th>
								</tr>
							</thead>
							<tbody>
								{(data.log || []).map((entry, i) => (
									<tr key={i}>
										<td className="truncate">{entry.title}</td>
										<td className="is-mid">{entry.task}</td>
										<td style={{ color: 'var(--ars-ink-3)' }}>
											<span className="truncate inline-block" style={{ maxWidth: 260 }}>
												{entry.before || '—'}
											</span>
										</td>
										<td style={{ color: 'var(--ars-ink-2)' }}>
											<span className="truncate inline-block" style={{ maxWidth: 320 }}>
												{entry.after}
											</span>
										</td>
										<td className="is-mid">
											<Badge tone={entry.status === 'applied' ? 'good' : entry.status === 'undone' ? 'neutral' : 'brand'}>
												{entry.status}
											</Badge>
										</td>
									</tr>
								))}
							</tbody>
						</table>
					</div>
				)}
			</Card>

			{/*
			 * Configuration, folded away from the thing it configures.
			 *
			 * The switches were the second thing on the screen and are set once,
			 * then read never — the verdict above already says what the next run
			 * will do with them. Collapsed, they cost one line; the note carries
			 * the current state so opening is only ever needed to change it.
			 */}
			<Panel
				title={__('What it should write', 'ai-rank-suite')}
				note={`${sprintf(__('%1$d of %2$d tasks on', 'ai-rank-suite'), tasks.length, Object.keys(taskDefs).length)} · ${
					mode === 'apply' ? __('applies directly', 'ai-rank-suite') : __('proposes only', 'ai-rank-suite')
				}${types.length > 0 ? ` · ${sprintf(_n('%d type', '%d types', types.length, 'ai-rank-suite'), types.length)}` : ''}`}
			>
				<div className="grid gap-4">
					<div className="grid gap-3 md:grid-cols-2">
						<div>
							<div className="t-overline mb-2">{__('What it should write', 'ai-rank-suite')}</div>
							<div className="grid gap-2">
								{Object.entries(taskDefs).map(([id, task]) => (
									<div
										key={id}
										className="ars-r-sm px-3 py-2.5"
										style={{ border: '1px solid var(--ars-border)', background: 'var(--ars-surface)' }}
									>
										<Switch checked={tasks.includes(id)} onChange={() => toggleTask(id)} label={task.label} />
										<p className="m-0 mt-1 ml-[48px] text-[12px]" style={{ color: 'var(--ars-ink-2)' }}>
											{task.why}
										</p>
									</div>
								))}
							</div>
						</div>

						<div className="grid gap-3 content-start">
							{/* Level, policy and kill switch live in their own card at the
							    top of this screen now — the decision is too important to
							    sit behind a disclosure triangle beside the checkboxes. */}
							<div>
								<div className="t-overline mb-2">{__('Where', 'ai-rank-suite')}</div>
								<div className="flex flex-wrap gap-1.5">
									{(data.post_types || []).map((t) => (
										<button
											key={t.name}
											type="button"
											onClick={() => toggleType(t.name)}
											className={`ars-chip${types.includes(t.name) ? ' is-active' : ''}`}
										>
											{t.label}
										</button>
									))}
								</div>
								<p className="m-0 mt-1.5 text-[12px]" style={{ color: 'var(--ars-ink-3)' }}>
									{types.length === 0 ? __('Everything, unless you pick a type.', 'ai-rank-suite') : sprintf(_n('%d type selected.', '%d types selected.', types.length, 'ai-rank-suite'), types.length)}
								</p>
							</div>

							<label className="grid gap-1.5">
								<span className="t-overline">{__('Maximum pages this run', 'ai-rank-suite')}</span>
								<input
									type="number"
									min="1"
									max="100"
									value={limit}
									onChange={(e) => setLimit(Math.max(1, Math.min(100, Number(e.target.value) || 1)))}
								/>
								<span className="t-caption">{__('Hard limit: 100 pages. The plan reports the maximum number of paid AI calls before work begins.', 'ai-rank-suite')}</span>
							</label>
						</div>
					</div>

					<Explain title={__('What it will never do', 'ai-rank-suite')}>
						<p>
							{__('It never overwrites something you wrote. Every task checks for an existing value first and skips the page rather than “improving” your sentence — and it checks again immediately before writing, so a page you edited while the run was going is still safe. Everything it does write is recorded together with the previous value, which is what makes the undo above work months later without a database backup.', 'ai-rank-suite')}
						</p>
					</Explain>
				</div>
			</Panel>

			{/*
			 * The floor stays — it is the best explanation of the routing table
			 * anywhere in the product and a pleasure to look at once. But it is
			 * looked at once, which is why it lives below everything a person
			 * returns for.
			 */}
			<Card
				icon={<IconBot size={16} />}
				title={__('The floor', 'ai-rank-suite')}
				subtitle={__('Every circuit, every desk and every gear — live, and only moving when something really is', 'ai-rank-suite')}
				flush
			>
				<AgencyFloor routing={data.routing} systems={data.systems} now={data.now} running={busy || data.running} />
				<RoutingEditor
					routing={data.routing}
					onSaved={(next) => setData({ ...data, routing: next })}
					onError={setError}
				/>
			</Card>
		</div>
	);
}

/**
 * Assignment: one engine per job, chosen by hand.
 *
 * A dropdown per job rather than a clever heuristic. The plugin cannot know
 * that your cheap engine is the one with the generous rate limit this month,
 * and a routing table that changes itself is one nobody can debug.
 */
function RoutingEditor({ routing, onSaved, onError }) {
	const [draft, setDraft] = useState(null);
	const [exclusiveDraft, setExclusiveDraft] = useState(null);
	const [busy, setBusy] = useState(false);
	const [saved, setSaved] = useState(false);

	if (!routing || (routing.engines || []).length === 0) return null;

	const stored = Object.fromEntries((routing.jobs || []).map((j) => [j.id, j.assigned ? j.provider : '']));
	const current = draft || stored;
	const exclusive = exclusiveDraft === null ? !!routing.exclusive : exclusiveDraft;

	/*
	 * Edits are held until Save.
	 *
	 * Every dropdown used to write to the server the moment it changed. That
	 * is fine for one deliberate change and awful for the real case — walking
	 * down ten jobs to move them all onto a new engine fired ten round trips,
	 * each one a chance to half-apply, and there was no way back from a
	 * mis-click because there was nothing to cancel.
	 */
	const dirty =
		(draft !== null && (routing.jobs || []).some((j) => (current[j.id] || '') !== (stored[j.id] || ''))) ||
		(exclusiveDraft !== null && exclusiveDraft !== !!routing.exclusive);

	const cancel = () => {
		setDraft(null);
		setExclusiveDraft(null);
	};

	const save = async () => {
		setBusy(true);
		try {
			const board = await api.put('/ai/routing', { assignments: current, exclusive });
			onSaved(board);
			setDraft(null);
			setExclusiveDraft(null);
			setSaved(true);
			setTimeout(() => setSaved(false), 1800);
		} catch (e) {
			onError(e.message);
		} finally {
			setBusy(false);
		}
	};

	return (
		<div className="ars-routing">
			<div className="ars-routing-head">
				<div>
					<div className="t-overline">{__('Assignment', 'ai-rank-suite')}</div>
					<p className="m-0 t-caption">
						{__('Each job has exactly one engine. There is no fallback chain, because a job that quietly changes vendor mid-run is how one site ends up written in two voices.', 'ai-rank-suite')}
					</p>
				</div>
				<div className="flex items-center gap-2">
					{saved && <Badge tone="good">✓ {__('Saved', 'ai-rank-suite')}</Badge>}
					<Switch
						checked={exclusive}
						disabled={busy}
						onChange={setExclusiveDraft}
						label={__('One writer per field', 'ai-rank-suite')}
					/>
				</div>
			</div>

			{(routing.conflicts || []).length > 0 && (
				<div className="grid gap-1.5 mb-3">
					{routing.conflicts.map((c, i) => (
						<div key={i} className={`ars-head-issue is-${c.level}`}>
							<span aria-hidden className="ars-head-issue-mark">
								{c.level === 'error' ? '✕' : c.level === 'warn' ? '!' : 'i'}
							</span>
							<div>
								<div className="font-semibold text-[12.5px]">{c.title}</div>
								<div className="text-[12px] mt-0.5" style={{ color: 'var(--ars-ink-2)' }}>
									{c.text}
								</div>
							</div>
						</div>
					))}
				</div>
			)}

			<div className="ars-routing-grid">
				{(routing.jobs || []).map((job) => (
					<div key={job.id} className="ars-routing-row">
						<div className="min-w-0">
							<div className="font-medium text-[12.5px]">{job.label}</div>
							<div className="t-caption">{job.about}</div>
							<div className="flex gap-1 mt-1 flex-wrap">
								{(job.traits || []).map((t) => (
									<span key={t} className="ars-trait">
										{t}
									</span>
								))}
							</div>
						</div>
						<select
							value={current[job.id] || ''}
							disabled={busy}
							onChange={(e) => setDraft({ ...current, [job.id]: e.target.value })}
						>
							<option value="">{__('Default engine', 'ai-rank-suite')}</option>
							{(routing.engines || []).map((e) => (
								<option key={e.id} value={e.id}>
									{e.label}
								</option>
							))}
						</select>
					</div>
				))}
			</div>

			{dirty && (
				<div className="ars-routing-actions">
					<span className="t-caption">
						{(routing.jobs || []).filter((j) => (current[j.id] || '') !== (stored[j.id] || '')).length +
							(exclusiveDraft !== null && exclusiveDraft !== !!routing.exclusive ? 1 : 0)}{' '}
						{__('unsaved change(s)', 'ai-rank-suite')}
					</span>
					<div className="flex items-center gap-2">
						<Button variant="ghost" onClick={cancel} disabled={busy}>{__('Cancel', 'ai-rank-suite')}</Button>
						<Button onClick={save} busy={busy}>{__('Save routing', 'ai-rank-suite')}</Button>
					</div>
				</div>
			)}
		</div>
	);
}
