import React, { useEffect, useState } from 'react';
import { __, _n, sprintf } from '@wordpress/i18n';
import { api } from '../api';
import { Card, Button, Badge, Notice, Switch, LineChart, Delta, RankBar, Sparkline, Tabs, PageState, ScoreRing, Progress } from '../components/ui';
import { IconSpark, IconX } from '../components/icons';
import FirstRun from '../components/FirstRun';

function ListEditor({ items, onChange, placeholder, max = 25, addLabel = __('Add', 'ai-rank-suite'), stats = null }) {
	const [draft, setDraft] = useState('');
	const add = () => {
		const value = draft.trim();
		if (!value || items.includes(value) || items.length >= max) return;
		onChange([...items, value]);
		setDraft('');
	};
	return (
		<div className="grid gap-2">
			<form
				className="flex gap-2"
				onSubmit={(e) => {
					e.preventDefault();
					add();
				}}
			>
				<input
					type="text"
					placeholder={placeholder}
					value={draft}
					onChange={(e) => setDraft(e.target.value)}
					disabled={items.length >= max}
				/>
				<Button variant="ghost" onClick={add} disabled={!draft.trim() || items.length >= max}>
					{addLabel}
				</Button>
			</form>
			{items.length > 0 && stats && (
				<div className="ars-table-wrap">
					<table className="ars-table">
						<thead>
							<tr>
								<th>{__('Question', 'ai-rank-suite')}</th>
								<th className="is-num" style={{ width: 90 }}>{__('Hit rate', 'ai-rank-suite')}</th>
								<th className="is-num" style={{ width: 70 }}>{__('Best', 'ai-rank-suite')}</th>
								<th className="is-num" style={{ width: 120 }}>{__('Last run', 'ai-rank-suite')}</th>
								<th style={{ width: 70 }} />
							</tr>
						</thead>
						<tbody>
							{items.map((item) => {
								const st = stats(item) || {};
								return (
									<tr key={item}>
										<td className="font-medium">{item}</td>
										<td className="is-num">
											<span
												className="font-semibold tabular-nums"
												style={{
													color:
														st.hit_rate == null
															? 'var(--ars-ink-3)'
															: st.hit_rate >= 70
																? 'var(--ars-good)'
																: st.hit_rate >= 30
																	? 'var(--ars-warning)'
																	: 'var(--ars-serious)',
												}}
											>
												{st.hit_rate == null ? __('new', 'ai-rank-suite') : `${st.hit_rate}%`}
											</span>
										</td>
										<td className="is-num is-muted">{st.best ? `#${st.best}` : '–'}</td>
										<td className="is-num">
											{st.last === undefined ? (
												<span className="is-muted">–</span>
											) : st.last ? (
												<Badge tone="good">{__('named', 'ai-rank-suite')}{st.lastPos ? ` #${st.lastPos}` : ''}</Badge>
											) : (
												<Badge tone="serious">{__('not named', 'ai-rank-suite')}</Badge>
											)}
										</td>
										<td className="is-num">
											<button
												type="button"
												className="ars-chip"
												onClick={() => onChange(items.filter((x) => x !== item))}
												aria-label={sprintf(__('Remove %s', 'ai-rank-suite'), item)}
											>{__('Remove', 'ai-rank-suite')}</button>
										</td>
									</tr>
								);
							})}
						</tbody>
					</table>
				</div>
			)}
			{items.length > 0 && !stats && (
				<div className="flex flex-wrap gap-1.5">
					{items.map((item, i) => (
						<span
							key={`${item}-${i}`}
							className="inline-flex items-center gap-1.5 pl-3 pr-2 py-1 ars-r-sm text-[12.5px]"
							style={{ background: 'var(--ars-surface-2)', border: '1px solid var(--ars-border)' }}
						>
							{item}
							<button
								type="button"
								aria-label={sprintf(__('Remove %s', 'ai-rank-suite'), item)}
								className="border-0 bg-transparent cursor-pointer p-0 inline-flex"
								style={{ color: 'var(--ars-ink-3)' }}
								onClick={() => onChange(items.filter((_, j) => j !== i))}
							>
								<IconX size={12} />
							</button>
						</span>
					))}
				</div>
			)}
		</div>
	);
}

const SENTIMENT = {
	positive: { tone: 'good', label: __('positive', 'ai-rank-suite') },
	neutral: { tone: 'neutral', label: __('neutral', 'ai-rank-suite') },
	negative: { tone: 'serious', label: __('negative', 'ai-rank-suite') },
};

/**
 * One run of one prompt, as a single cell. Colour carries the outcome and
 * height carries the rank, so a row reads as a shape before it reads as data.
 */
function Cell({ cell, label }) {
	if (!cell) {
		return (
			<span
				title={sprintf(__('%s: not monitored yet', 'ai-rank-suite'), label)}
				className="ars-cell"
				style={{ background: 'var(--ars-surface-3)', opacity: 0.5 }}
			/>
		);
	}
	if (!cell.mentioned) {
		return <span title={sprintf(__('%s: not mentioned', 'ai-rank-suite'), label)} className="ars-cell" style={{ background: '#f6aea9' }} />;
	}
	// Position 1–3 is the answer; 4+ is a footnote. The palette says so.
	const color = cell.position > 0 && cell.position <= 3 ? '#188038' : cell.position <= 6 ? '#81c995' : '#f9ab00';
	return (
		<span
			title={`${cell.position > 0 ? sprintf(__('%1$s: mentioned at #%2$d', 'ai-rank-suite'), label, cell.position) : sprintf(__('%s: mentioned', 'ai-rank-suite'), label)}${cell.cited ? ` · ${__('cited', 'ai-rank-suite')}` : ''}`}
			className="ars-cell"
			style={{ background: color }}
		>
			{cell.cited && <span className="ars-cell-dot" aria-hidden />}
		</span>
	);
}

function PromptDrawer({ row, track, runs, onClose }) {
	if (!row) return null;
	const s = SENTIMENT[row.sentiment] || SENTIMENT.neutral;

	// What to actually do about this prompt. Generic advice is worthless, so
	// the hint follows the outcome the run recorded.
	const advice = ! row.mentioned
		? __('Publish a page that answers this question directly in its opening paragraph, name your brand in that answer, and mark it up as FAQPage or Article. Assistants quote the source that states the answer plainly.', 'ai-rank-suite')
		: row.position > 3
			? __('You are in the answer but late. Assistants lead with the source that is most specific — add concrete numbers, a comparison table, or a clear "best for X" statement to the page that covers this.', 'ai-rank-suite')
			: row.linked
				? __('You own this one: your URL appears in the simulated answer. Keep the page fresh — assistants drop stale sources first.', 'ai-rank-suite')
				: row.cited
					? __('The model says it would cite you, but no link to your site appeared in its answer — an estimate, not an observation. A canonical, well-structured page usually turns that into a real link.', 'ai-rank-suite')
					: __('You are named early but not linked. A citation usually follows from a canonical, well-structured page: check that this topic has one page rather than three overlapping ones.', 'ai-rank-suite');
	return (
		<>
			<button type="button" className="ars-scrim" aria-label={__('Close panel', 'ai-rank-suite')} onClick={onClose} />
			<aside className="ars-drawer" role="dialog" aria-modal="true" aria-label={__('Prompt result', 'ai-rank-suite')}>
				<div className="p-5 grid gap-4">
					<header className="flex items-start justify-between gap-3">
						<h2 className="m-0 text-[16px] font-bold leading-snug">{row.prompt}</h2>
						<button
							type="button"
							onClick={onClose}
							aria-label={__('Close', 'ai-rank-suite')}
							className="border-0 bg-transparent cursor-pointer p-1"
							style={{ color: 'var(--ars-ink-3)' }}
						>
							<IconX size={16} />
						</button>
					</header>

					<div className="flex flex-wrap gap-2">
						{row.mentioned ? (
							<Badge tone="good">✓ {row.position > 0 ? sprintf(__('Mentioned at #%d', 'ai-rank-suite'), row.position) : __('Mentioned', 'ai-rank-suite')}</Badge>
						) : (
							<Badge tone="serious">✕ {__('Not mentioned', 'ai-rank-suite')}</Badge>
						)}
						{row.linked && <Badge tone="brand">{__('Your URL in the answer', 'ai-rank-suite')}</Badge>}
						{!row.linked && row.cited && <Badge tone="neutral" title={__('The model\'s own estimate — no link was observed', 'ai-rank-suite')}>{__('would cite (estimate)', 'ai-rank-suite')}</Badge>}
						{row.model && (
							<span className="t-caption" title={__('This is a simulation of one model\'s answer, not a live query of a real assistant', 'ai-rank-suite')}>
								{__('simulated', 'ai-rank-suite')} · {row.provider} {row.model}
							</span>
						)}
						<Badge tone={s.tone}>{sprintf(__('%s tone', 'ai-rank-suite'), s.label)}</Badge>
					</div>

					<div>
						<div className="text-[12px] font-semibold mb-1" style={{ color: 'var(--ars-ink-3)' }}>{__('What the assistant answered', 'ai-rank-suite')}</div>
						<div className="ars-r-lg px-4 py-3 text-[13px] leading-relaxed" style={{ background: 'var(--ars-surface-2)' }}>
							{row.snippet || __('No answer text was stored for this run.', 'ai-rank-suite')}
						</div>
					</div>

					{row.competitors?.length > 0 && (
						<div>
							<div className="text-[12px] font-semibold mb-1.5" style={{ color: 'var(--ars-ink-3)' }}>{__('Named in the same answer', 'ai-rank-suite')}</div>
							<div className="flex flex-wrap gap-1.5">
								{row.competitors.map((c) => (
									<span
										key={c}
										className="px-3 py-1 ars-r-sm text-[12.5px]"
										style={{ background: 'var(--ars-surface-2)', border: '1px solid var(--ars-border)' }}
									>
										{c}
									</span>
								))}
							</div>
						</div>
					)}

					{track && track.series.length > 0 && (
						<div>
							<div className="text-[12px] font-semibold mb-1.5" style={{ color: 'var(--ars-ink-3)' }}>{__('Every run for this question', 'ai-rank-suite')}</div>
							<div className="grid gap-1.5">
								{track.series.map((cell, i) => (
									<div
										key={i}
										className="flex items-center justify-between gap-3 ars-r-lg px-3 py-2 text-[12.5px]"
										style={{ background: 'var(--ars-surface-2)' }}
									>
										<span style={{ color: 'var(--ars-ink-3)' }}>{runs[i]?.label ?? sprintf(__('Run %d', 'ai-rank-suite'), i + 1)}</span>
										<span className="flex items-center gap-2">
											{!cell ? (
												<span style={{ color: 'var(--ars-ink-3)' }}>{__('not monitored yet', 'ai-rank-suite')}</span>
											) : cell.mentioned ? (
												<>
													<Badge tone="good">#{cell.position || '?'}</Badge>
													{cell.linked && <Badge tone="brand">{__('linked', 'ai-rank-suite')}</Badge>}
													{!cell.linked && cell.cited && <Badge tone="neutral">{__('would cite', 'ai-rank-suite')}</Badge>}
												</>
											) : (
												<Badge tone="serious">{__('not mentioned', 'ai-rank-suite')}</Badge>
											)}
										</span>
									</div>
								))}
							</div>
						</div>
					)}

					<div>
						<div className="text-[12px] font-semibold mb-1" style={{ color: 'var(--ars-ink-3)' }}>{__('What to do next', 'ai-rank-suite')}</div>
						<div
							className="ars-r-lg px-4 py-3 text-[12.5px] leading-relaxed"
							style={{ background: 'var(--ars-grad-soft)' }}
						>
							{advice}
						</div>
					</div>

					<div className="text-[11.5px]" style={{ color: 'var(--ars-ink-3)' }}>
						{row.checked_at ? sprintf(__('Checked %s', 'ai-rank-suite'), new Date(row.checked_at.replace(' ', 'T') + 'Z').toLocaleString()) : null}
					</div>
				</div>
			</aside>
		</>
	);
}

/**
 * Every tracked question as one weakness row.
 *
 * The score is deliberately blunt. Being named is 60 of it, because an answer
 * has no page two — a brand named third of three is in the answer and a brand
 * not named is invisible, however good its site is. Position adds up to 25 on
 * top, and a citation with a working link adds the last 15, since a link is the
 * only part of an AI answer that can still send a visitor.
 */
function promptRows(rows) {
	return rows
		.map((r, i) => {
			const pos = Number(r.position) || 0;
			let score = 0;
			if (r.mentioned) {
				score += 60;
				score += pos === 1 ? 25 : pos === 2 ? 18 : pos === 3 ? 12 : 6;
				// A real link in the answer is worth the full 15; the model's own
				// estimate that it "would" cite is worth a third of that.
				if (r.linked) score += 15;
				else if (r.cited) score += 5;
			}
			const rivals = (r.competitors || []).length;
			return {
				id: `${i}-${r.prompt}`,
				label: r.prompt,
				score: Math.min(100, score),
				sub: r.mentioned
					? [
							pos > 0
								? pos === 1
									? __('named first', 'ai-rank-suite')
									: pos === 2
										? __('named second', 'ai-rank-suite')
										: sprintf(__('named %dth', 'ai-rank-suite'), pos)
								: __('named', 'ai-rank-suite'),
							r.cited ? __('with a link', 'ai-rank-suite') : __('no link', 'ai-rank-suite'),
							r.sentiment && r.sentiment !== 'neutral' ? SENTIMENT[r.sentiment]?.label || r.sentiment : null,
						]
							.filter(Boolean)
							.join(' · ')
					: rivals > 0
						? sprintf(__('not named — %s was', 'ai-rank-suite'), (r.competitors || []).slice(0, 2).join(', '))
						: __('not named in the answer', 'ai-rank-suite'),
				segments: [
					{ label: __('You', 'ai-rank-suite'), value: r.mentioned ? 1 : 0, color: 'var(--ars-c-green-ink)' },
					{ label: __('Rivals named instead', 'ai-rank-suite'), value: rivals, color: 'var(--ars-accent)' },
					{ label: __('Nobody', 'ai-rank-suite'), value: r.mentioned || rivals ? 0 : 1, color: 'var(--ars-ink-3)' },
				],
				reach: r.mentioned ? __('in the answer', 'ai-rank-suite') : sprintf(_n('%d rival', '%d rivals', rivals, 'ai-rank-suite'), rivals),
			};
		})
		.sort((a, b) => a.score - b.score);
}

export default function AiVisibility({ tab = 'track', onTab }) {
	const [data, setData] = useState(null);
	const [results, setResults] = useState(null);
	const [matrix, setMatrix] = useState(null);
	// Share of voice across runs — who is gaining ground and who is losing it.
	const [trend, setTrend] = useState(null);
	const [busy, setBusy] = useState(false);
	const [running, setRunning] = useState(false);
	// Slice-job progress (checked / total prompts) for the button label.
	const [progress, setProgress] = useState(null);
	const [suggesting, setSuggesting] = useState(false);
	const [suggestions, setSuggestions] = useState(null);
	const [saved, setSaved] = useState(false);
	const [error, setError] = useState(null);
	const [open, setOpen] = useState(null);

	const loadResults = () => {
		api.get('/ai-visibility/results').then(setResults).catch(() => {});
		api.get('/ai-visibility/matrix').then(setMatrix).catch(() => {});
		api.get('/ai-visibility/trend').then(setTrend).catch(() => {});
	};

	useEffect(() => {
		api.get('/settings/ai-visibility').then(setData).catch((e) => setError(e.message));
		loadResults();
	}, []);

	if (!data) return <PageState error={error} />;
	const s = data.settings;
	const update = (patch) => setData({ ...data, settings: { ...s, ...patch } });

	const save = async () => {
		setBusy(true);
		setError(null);
		try {
			setData(await api.put('/settings/ai-visibility', s));
			setSaved(true);
			setTimeout(() => setSaved(false), 2200);
		} catch (e) {
			setError(e.message);
		} finally {
			setBusy(false);
		}
	};

	const runNow = async () => {
		setRunning(true);
		setError(null);
		try {
			// Persist the prompt list first so the run uses what's on screen.
			await api.put('/settings/ai-visibility', s);
			// One prompt per tick: the run is a slice job (locked, resumable,
			// finished by cron if this tab goes away) — never one long request.
			let st = await api.post('/ai-visibility/run');
			setProgress(st);
			while (st.state === 'running') {
				if (st.busy || st.error) await new Promise((r) => setTimeout(r, 2000));
				st = await api.post('/ai-visibility/run/tick');
				setProgress(st);
			}
			setProgress(null);
			setResults(await api.get('/ai-visibility/results'));
			api.get('/ai-visibility/matrix').then(setMatrix).catch(() => {});
			api.get('/ai-visibility/trend').then(setTrend).catch(() => {});
		} catch (e) {
			setError(e.message);
			setProgress(null);
		} finally {
			setRunning(false);
		}
	};

	const suggest = async () => {
		setSuggesting(true);
		setError(null);
		try {
			// Save first: the suggester reads brand, competitors and the current
			// prompt list so it never proposes something already monitored.
			await api.put('/settings/ai-visibility', s);
			const r = await api.post('/ai-visibility/suggest', { count: 10 });
			setSuggestions(r.prompts);
		} catch (e) {
			setError(e.message);
		} finally {
			setSuggesting(false);
		}
	};

	const addSuggestion = (prompt) => {
		if (s.prompts.includes(prompt) || s.prompts.length >= data.max_prompts) return;
		update({ prompts: [...s.prompts, prompt] });
		setSuggestions(suggestions.filter((p) => p !== prompt));
	};

	const rows = results?.rows || [];
	const mentioned = rows.filter((r) => r.mentioned).length;
	const cited = rows.filter((r) => r.cited).length;
	const owned = rows.filter((r) => r.mentioned && r.position > 0 && r.position <= 3).length;
	const history = results?.history || [];
	/*
	 * Two columns only when there are two cards.
	 *
	 * Share of voice needs more than one run to say anything, so on a site
	 * that has simulated once the chart beside it sat alone in a two-column
	 * grid and the right half of the row was empty canvas — on the screen
	 * that opens with the product's headline number. The row is as wide as it
	 * has cards, and the chart takes the whole width when it is the only one.
	 */
	const hasShareOfVoice = !! ( trend && trend.series && trend.series.length > 0 && trend.runs && trend.runs.length > 1 );
	/*
	 * Whether a second card will actually be drawn beside the chart.
	 *
	 * The row used to split in two whenever there was a trend to plot, but the
	 * card on the right only appears when the last run named a competitor. A
	 * site nobody is compared against — "Genannte Konkurrenzmarken: 0" — got a
	 * half-width chart with an empty half beside it, which reads as something
	 * that failed to load rather than something that does not exist.
	 */
	const hasSecondCard = !! ( results && Array.isArray( results.competitors ) && results.competitors.length > 0 );
	const runs = matrix?.runs || [];

	// Cost and model of the last run, from the rows themselves.
	const runCost = rows.reduce((a, r) => a + (Number(r.cost_micro) || 0), 0);
	const runModel = rows.find((r) => r.model)?.model || '';

	// Your own URLs in the last run's answers, most cited first.
	const citedPages = (() => {
		const map = new Map();
		rows.forEach((r) => (r.urls || []).forEach((u) => map.set(u, (map.get(u) || 0) + 1)));
		return [...map.entries()]
			.map(([url, count]) => {
				let path = url;
				try {
					const x = new URL(url);
					path = x.pathname === '/' ? x.host : x.pathname;
				} catch (e) {
					/* keep the raw string */
				}
				return { url, count, path };
			})
			.sort((a, b) => b.count - a.count)
			.slice(0, 8);
	})();

	const verdict = (() => {
		const score = results?.score ?? 0;
		const n = rows.length;
		const missing = n - mentioned;
		const top = results?.competitors?.[0];
		const ourShare = n ? Math.round((mentioned / n) * 100) : 0;
		const rivalAhead = top && top.share > ourShare ? top.name : null;
		// Say what this is: the configured model answering these prompts, not a
		// measurement of what people currently see in ChatGPT or Perplexity.
		const named = rivalAhead
			? sprintf(__('Your model names you in %1$d of %2$d simulated answers — %3$s in more.', 'ai-rank-suite'), mentioned, n, rivalAhead)
			: sprintf(__('Your model names you in %1$d of %2$d simulated answers.', 'ai-rank-suite'), mentioned, n);
		if (score >= 75) {
			return {
				tone: 'good',
				badge: __('Recommended', 'ai-rank-suite'),
				text: named,
				why:
					cited > 0
						? sprintf(__('%d of them would link to you — keep those pages strong; a link is the only part of an AI answer that sends a visitor.', 'ai-rank-suite'), cited)
						: __('Named, but never linked: the next gain is a page per question that answers it directly enough to be cited.', 'ai-rank-suite'),
			};
		}
		if (score >= 45) {
			return {
				tone: 'warning',
				badge: __('Sometimes named', 'ai-rank-suite'),
				text: named,
				why:
					missing > 0
						? `${sprintf(_n('%d question your customers ask still gets answered without you — the work list below, worst first.', '%d questions your customers ask still get answered without you — the work list below, worst first.', missing, 'ai-rank-suite'), missing)}${owned < mentioned ? ` ${sprintf(__('And of the answers that name you, only %d put you in the top three.', 'ai-rank-suite'), owned)}` : ''}`
						: sprintf(__('Named everywhere, but late: only %1$d of %2$d answers put you in the top three, and %3$d link to you. Position and citations are the remaining score.', 'ai-rank-suite'), owned, n, cited),
			};
		}
		return {
			tone: 'bad',
			badge: __('Mostly invisible', 'ai-rank-suite'),
			text: mentioned === 0 ? sprintf(__('Assistants do not name you for any of the %d monitored questions yet.', 'ai-rank-suite'), n) : named,
			why: __('A generated answer has no page two: a brand that is not named does not exist for that question. Each row of the work list is a page to write or sharpen.', 'ai-rank-suite'),
		};
	})();

	return (
		<div className="ars-page">
			{error && <Notice tone="serious">{error}</Notice>}
			{!data.has_ai_key && rows.length > 0 && (
				<Notice tone="info">
					{__('Connect an AI key first (AI Engine page) — answer simulations query your configured model.', 'ai-rank-suite')}{' '}
					<button
						type="button"
						className="border-0 bg-transparent cursor-pointer p-0 font-semibold"
						style={{ color: 'var(--ars-brand)' }}
						onClick={() => {
							window.location.hash = '/ai';
						}}
					>{__('Open AI Engine →', 'ai-rank-suite')}</button>
				</Notice>
			)}

			{rows.length === 0 && (
				<FirstRun
					badge={__('No simulation yet', 'ai-rank-suite')}
					ringLabel={__('Answer score', 'ai-rank-suite')}
					title={__('No answer has been simulated yet — you do not know whether assistants recommend you.', 'ai-rank-suite')}
					why={__('Each run asks your configured AI model the questions your customers ask an assistant, exactly as they would, and records whether you are named, how early, whether you are linked, and who is named instead. From the second run it is a trend; from the first it is a work list.', 'ai-rank-suite')}
					action={running ? (progress?.total ? sprintf(__('Checking… %1$s / %2$s', 'ai-rank-suite'), progress.done, progress.total) : __('Checking…', 'ai-rank-suite')) : __('Run the first check', 'ai-rank-suite')}
					onAction={runNow}
					busy={running}
					disabled={!data.has_ai_key || !s.prompts.length}
					stats={[
						{ label: __('Named (simulated)', 'ai-rank-suite') },
						{ label: __('In the top 3 (simulated)', 'ai-rank-suite') },
						{ label: __('Would cite (simulated)', 'ai-rank-suite') },
						{ label: __('Rival brands named (simulated)', 'ai-rank-suite') },
					]}
					steps={[
						{
							label: __('Connect an AI model', 'ai-rank-suite'),
							detail: data.has_ai_key ? __('Connected — the simulation uses your configured model and key.', 'ai-rank-suite') : __('The simulation queries your own model. OpenAI, Anthropic, Mistral and others under AI Engine.', 'ai-rank-suite'),
							done: !!data.has_ai_key,
							action: __('Open AI Engine', 'ai-rank-suite'),
							onAction: () => {
								window.location.hash = '/ai';
							},
						},
						{
							label: __('Add the questions your customers ask', 'ai-rank-suite'),
							detail: s.prompts.length ? sprintf(_n('%1$d prompt monitored — up to %2$d.', '%1$d prompts monitored — up to %2$d.', s.prompts.length, 'ai-rank-suite'), s.prompts.length, data.max_prompts) : __('Five to twenty-five real questions — “best … in …”, “where to …”. The AI can suggest them from your content.', 'ai-rank-suite'),
							done: s.prompts.length > 0,
							action: __('Add prompts', 'ai-rank-suite'),
							onAction: () => onTab && onTab('prompts'),
						},
						{
							label: __('Name your brand and rivals', 'ai-rank-suite'),
							detail: s.brand ? `${s.brand}${s.competitors?.length ? ` · ${sprintf(_n('%d rival', '%d rivals', s.competitors.length, 'ai-rank-suite'), s.competitors.length)}` : ''}` : __('What the AI must say for a mention to count, and who else you expect it to name.', 'ai-rank-suite'),
							done: !!s.brand,
							action: __('Open Brand', 'ai-rank-suite'),
							onAction: () => onTab && onTab('brand'),
						},
						{ label: __('Run the first check', 'ai-rank-suite'), detail: __('One model call per prompt. Weekly automation is on the Brand tab.', 'ai-rank-suite'), done: false },
					]}
					previews={[
						{ title: __('Share of voice', 'ai-rank-suite'), text: __('You and each rival across every run — who is gaining ground in the answers and who is losing it.', 'ai-rank-suite') },
						{ title: __('Work list', 'ai-rank-suite'), text: __('The questions you are not the answer to, worst first, with who was named instead and the answer itself.', 'ai-rank-suite') },
						{ title: __('Pages the assistant cites', 'ai-rank-suite'), text: __('Your URLs that appear in answers — the only part of an AI answer that sends a visitor.', 'ai-rank-suite') },
					]}
				/>
			)}

			{rows.length > 0 && (
				<>
					{/*
					 * The result as one hero — ring, verdict, the four numbers that
					 * make it checkable, and the run button — the same anatomy the
					 * dashboard and the audit use. The previous head said the score
					 * three times (gauge, tile, trend legend) and left a third of
					 * the width empty beside the trend card.
					 */}
					<div className="ars-hero">
						<div className="ars-hero-ring">
							<ScoreRing value={results.score} size={132} label={__('Answer score', 'ai-rank-suite')} onLight />
						</div>
						<div className="ars-hero-body">
							<span className={`ars-hero-badge is-${verdict.tone}`}>{verdict.badge}</span>
							<h2 className="ars-hero-title">{verdict.text}</h2>
							<p className="ars-hero-why">{verdict.why}</p>
							<div className="ars-hero-actions">
								<button
									type="button"
									className="ars-btn ars-btn-primary"
									onClick={runNow}
									disabled={running || !data.has_ai_key || !s.prompts.length}
								>
									{running ? (progress?.total ? sprintf(__('Checking… %1$s / %2$s', 'ai-rank-suite'), progress.done, progress.total) : __('Checking…', 'ai-rank-suite')) : __('Run check now', 'ai-rank-suite')}
								</button>
								<button type="button" className="ars-btn ars-btn-ghost" onClick={() => onTab && onTab('prompts')}>{__('Edit prompts', 'ai-rank-suite')}</button>
								<span className="t-caption">
									{results.last_run ? `${sprintf(__('Last run %s', 'ai-rank-suite'), new Date(results.last_run.time * 1000).toLocaleString())} · ` : ''}
									{sprintf(_n('%d prompt', '%d prompts', rows.length, 'ai-rank-suite'), rows.length)}
									{runModel ? ` · ${runModel}` : ''}
									{runCost > 0 ? ` · ${sprintf(__('$%s per run', 'ai-rank-suite'), (runCost / 1e6).toFixed(runCost >= 1e5 ? 2 : 3))}` : ''}
								</span>
							</div>
							{/*
							  * The slowest thing in the plugin, and until now the
							  * only sign it was moving was a number inside the
							  * button label. Every prompt is a round trip to a
							  * model; on a dozen of them that is a long minute of
							  * a screen that looks stuck.
							  */}
							{running && progress?.total > 0 && (
								<div className="mt-2">
									<Progress
										label={sprintf(
											/* translators: 1: prompts done, 2: prompts total. */
											__('Asking the engines — %1$s of %2$s', 'ai-rank-suite'),
											Number(progress.done).toLocaleString(),
											Number(progress.total).toLocaleString()
										)}
										done={progress.done}
										total={progress.total}
									/>
								</div>
							)}
						</div>
						<div className="ars-hero-stats">
							{[
								{ label: __('Named (simulated)', 'ai-rank-suite'), value: `${mentioned} / ${rows.length}`, tone: mentioned >= rows.length / 2 ? 'good' : 'warn' },
								{ label: __('In the top 3', 'ai-rank-suite'), value: owned, tone: owned > 0 ? 'good' : undefined },
								{ label: __('Would cite (simulated)', 'ai-rank-suite'), value: cited, tone: cited > 0 ? 'good' : 'warn' },
								{ label: __('Rival brands named (simulated)', 'ai-rank-suite'), value: results.competitors ? results.competitors.length : 0, tone: results.competitors?.length ? 'warn' : undefined },
							].map((st) => (
								<div key={st.label} className="ars-hero-stat" style={{ cursor: 'default' }}>
									<span className={`ars-hero-stat-v${st.tone ? ' is-' + st.tone : ''}`}>{st.value}</span>
									<span className="ars-hero-stat-l">{st.label}</span>
								</div>
							))}
						</div>
					</div>

					<div className={`grid gap-4 items-stretch${hasShareOfVoice && hasSecondCard ? ' lg:grid-cols-2' : ''}`}>
						<Card
							title={__('Answer score over time', 'ai-rank-suite')}
							subtitle={__('One point per simulation run — the number that should be rising', 'ai-rank-suite')}
							action={history.length > 1 ? <Delta value={history[history.length - 1].value - history[0].value} suffix={` ${__('since first run', 'ai-rank-suite')}`} /> : null}
						>
							<LineChart data={history} label={__('Answer score', 'ai-rank-suite')} formatValue={(v) => `${v}/100`} height={150} />
						</Card>

						{hasShareOfVoice ? (
							<Card
								title={__('Share of voice', 'ai-rank-suite')}
								subtitle={__('How often each name is present in the answers — across every run, not just the last one', 'ai-rank-suite')}
							>
								<div className="grid gap-2.5">
									{[...trend.series]
										.sort((a, b) => b.latest - a.latest)
										.map((sv) => {
											const d = sv.latest - sv.first;
											return (
												<div key={sv.name} className="flex items-center gap-3">
													<span
														className="truncate text-[13px]"
														style={{ width: 150, fontWeight: sv.isBrand ? 600 : 400, color: sv.isBrand ? 'var(--ars-ink)' : 'var(--ars-ink-2)' }}
													>
														{sv.name}
														{sv.isBrand ? ` · ${__('you', 'ai-rank-suite')}` : ''}
													</span>
													<span className="flex-1 min-w-0">
														<Sparkline data={sv.points} color={sv.color} height={26} />
													</span>
													<span className="t-num text-[14px]" style={{ width: 46, textAlign: 'right' }}>
														{sv.latest}%
													</span>
													<span style={{ width: 54, textAlign: 'right' }}>
														<Delta value={d} suffix="pp" />
													</span>
												</div>
											);
										})}
								</div>
							</Card>
						) : (
							results?.competitors?.length > 0 && (
								<Card title={__('Share of voice', 'ai-rank-suite')} subtitle={__('Who the assistant names instead of, or alongside, you — from the last run', 'ai-rank-suite')}>
									<RankBar
										items={results.competitors.map((c) => ({ key: c.name, label: c.name, value: c.share }))}
										max={100}
										color="var(--ars-series-3)"
										format={(v) => `${v}%`}
									/>
								</Card>
							)
						)}
					</div>

					<div className="grid gap-4 lg:grid-cols-2 items-stretch">
						{/* The five questions to work on, with the one action each: open
						    the answer and see who was named instead. */}
						<Card title={__('Work list', 'ai-rank-suite')} subtitle={__('The questions you are not the answer to — worst first', 'ai-rank-suite')} flush>
							<div className="ars-worklist">
								{promptRows(rows)
									.slice(0, 6)
									.map((r) => {
										const src = rows.find((x) => x.prompt === r.label);
										return (
											<button key={r.id} type="button" className="ars-work" onClick={() => src && setOpen(src)}>
												<span className={`ars-work-score t-num is-${r.score >= 80 ? 'good' : r.score >= 50 ? 'mid' : 'bad'}`}>{r.score}</span>
												<span className="ars-work-body">
													<span className="ars-work-label">{r.label}</span>
													<span className="ars-work-sub">{r.sub}</span>
												</span>
												<span className="ars-work-go" aria-hidden>
													›
												</span>
											</button>
										);
									})}
							</div>
						</Card>

						{/* Which of your pages the assistant actually points to. The
						    only part of an AI answer that can send a visitor, so the
						    pages on this list are the ones to keep strong. */}
						<Card
							title={__('Pages the assistant cites', 'ai-rank-suite')}
							subtitle={citedPages.length ? __('Your URLs found in the last run’s answers, most cited first', 'ai-rank-suite') : __('No URL of yours appeared in an answer yet', 'ai-rank-suite')}
							flush
						>
							{citedPages.length === 0 ? (
								<div className="px-4 py-4 text-[12.5px]" style={{ color: 'var(--ars-ink-2)' }}>
									{__('A citation needs a page that answers the question directly — a heading that is the question, a first paragraph that is the answer. The work list on the left is where to start.', 'ai-rank-suite')}
								</div>
							) : (
								<div className="ars-citelist">
									{citedPages.map((c) => (
										<a key={c.url} className="ars-cite" href={c.url} target="_blank" rel="noreferrer">
											<span className="ars-cite-url ars-mono" title={c.url}>
												{c.path}
											</span>
											<span className="ars-cite-n tabular-nums">
												{c.count} {_n('answer', 'answers', c.count, 'ai-rank-suite')}
											</span>
										</a>
									))}
								</div>
							)}
						</Card>
					</div>
				</>
			)}

			<Tabs
				tabs={[
					{ id: 'track', label: __('Tracker', 'ai-rank-suite') },
					{ id: 'prompts', label: sprintf(__('Prompts (%d)', 'ai-rank-suite'), s.prompts.length) },
					{ id: 'brand', label: __('Brand', 'ai-rank-suite') },
				]}
				active={tab}
				onChange={onTab}
				right={
					// With results the hero carries the run button; before the first
					// run this is the only place it can live.
					rows.length === 0 && (
						<Button onClick={runNow} busy={running} disabled={!data.has_ai_key || !s.prompts.length}>
							{running ? (progress?.total ? sprintf(__('Checking… %1$s / %2$s', 'ai-rank-suite'), progress.done, progress.total) : __('Checking…', 'ai-rank-suite')) : __('Run first check', 'ai-rank-suite')}
						</Button>
					)
				}
			/>

			{tab === 'track' && (
				<div className="grid gap-4">

					<Card
						title={__('Prompt tracker', 'ai-rank-suite')}
						subtitle={__('One row per question, one cell per run. Weakest questions first — those are your work list.', 'ai-rank-suite')}
						action={
							runs.length > 0 && (
								<span className="text-[11.5px]" style={{ color: 'var(--ars-ink-3)' }}>
									{sprintf(_n('%d run', '%d runs', runs.length, 'ai-rank-suite'), runs.length)} · {runs[0].label} → {runs[runs.length - 1].label}
								</span>
							)
						}
					>
						{!matrix || matrix.prompts.length === 0 ? (
							<div className="ars-r-lg px-4 py-5 grid gap-2 place-items-start" style={{ background: 'var(--ars-surface-2)' }}>
								<div className="font-medium text-[13px]">{__('No runs yet', 'ai-rank-suite')}</div>
								<div className="text-[12px]" style={{ color: 'var(--ars-ink-3)' }}>
									{__('Add the questions your customers ask, then run a check. From the second run onward this becomes a tracker.', 'ai-rank-suite')}
								</div>
								<Button variant="tonal" onClick={() => onTab('prompts')}>{__('Add prompts', 'ai-rank-suite')}</Button>
							</div>
						) : (
							<div className="grid gap-3">
								<div className="ars-table-wrap"><table className="ars-table">
									<thead>
										<tr>
											<th>{__('Question', 'ai-rank-suite')}</th>
											<th style={{ width: 190 }}>{__('Runs', 'ai-rank-suite')}</th>
											<th className="is-num" style={{ width: 100 }}>{__('Hit rate', 'ai-rank-suite')}</th>
											<th className="is-num" style={{ width: 80 }}>{__('Best', 'ai-rank-suite')}</th>
										</tr>
									</thead>
									<tbody>
										{matrix.prompts.map((p) => {
											const row = rows.find((r) => r.prompt === p.prompt);
											return (
												<tr
													key={p.prompt}
													style={{ cursor: row ? 'pointer' : 'default' }}
													onClick={() => row && setOpen(row)}
												>
													<td className="font-medium" style={{ maxWidth: 420 }}>
														{p.prompt}
													</td>
													<td>
														<span className="flex items-end gap-[3px]">
															{p.series.map((cell, i) => (
																<Cell key={i} cell={cell} label={runs[i]?.label ?? sprintf(__('run %d', 'ai-rank-suite'), i + 1)} />
															))}
														</span>
													</td>
													<td className="is-num">
														<span
															className="font-semibold tabular-nums"
															style={{
																color:
																	p.hit_rate == null
																		? 'var(--ars-ink-3)'
																		: p.hit_rate >= 70
																			? 'var(--ars-good)'
																			: p.hit_rate >= 30
																				? 'var(--ars-warning)'
																				: 'var(--ars-serious)',
															}}
														>
															{p.hit_rate == null ? '–' : `${p.hit_rate}%`}
														</span>
													</td>
													<td className="is-num" style={{ color: 'var(--ars-ink-2)' }}>
														{p.best ? `#${p.best}` : '–'}
													</td>
												</tr>
											);
										})}
									</tbody>
								</table></div>
								<div className="flex flex-wrap gap-x-4 gap-y-1 text-[11.5px]" style={{ color: 'var(--ars-ink-2)' }}>
									{[
										['#188038', __('top 3', 'ai-rank-suite')],
										['#81c995', __('top 6', 'ai-rank-suite')],
										['#f9ab00', __('mentioned, further down', 'ai-rank-suite')],
										['#f6aea9', __('not mentioned', 'ai-rank-suite')],
										['var(--ars-surface-3)', __('not monitored yet', 'ai-rank-suite')],
									].map(([color, label]) => (
										<span key={label} className="inline-flex items-center gap-1.5">
											<span aria-hidden style={{ width: 10, height: 10, borderRadius: 3, background: color, display: 'inline-block' }} />
											{label}
										</span>
									))}
									<span>· {__('a dot means the assistant linked to you', 'ai-rank-suite')}</span>
								</div>
							</div>
						)}
					</Card>

				</div>
			)}

			{tab === 'prompts' && (
				<Card
					title={__('Monitored prompts', 'ai-rank-suite')}
					subtitle={sprintf(__('Questions your customers ask AI assistants — up to %d', 'ai-rank-suite'), data.max_prompts)}
					action={
						<div className="flex items-center gap-2">
							{saved && <Badge tone="good">✓ {__('Saved', 'ai-rank-suite')}</Badge>}
							<Button variant="ghost" onClick={suggest} busy={suggesting} disabled={!data.has_ai_key}>{__('Suggest for me', 'ai-rank-suite')}</Button>
							<Button onClick={save} busy={busy}>{__('Save', 'ai-rank-suite')}</Button>
						</div>
					}
				>
					<div className="grid gap-4">
						<ListEditor
							items={s.prompts}
							onChange={(prompts) => update({ prompts })}
							placeholder={__('e.g. What is the best SEO plugin for WordPress?', 'ai-rank-suite')}
							max={data.max_prompts}
							addLabel={__('Add prompt', 'ai-rank-suite')}
							stats={
								matrix && matrix.prompts && matrix.prompts.length > 0
									? (prompt) => {
											const m = matrix.prompts.find((p) => p.prompt === prompt);
											const r = rows.find((x) => x.prompt === prompt);
											if (!m && !r) return {};
											return {
												hit_rate: m ? m.hit_rate : null,
												best: m ? m.best : null,
												last: r ? !!r.mentioned : undefined,
												lastPos: r ? Number(r.position) || 0 : 0,
											};
										}
									: null
							}
						/>

						{suggestions && (
							<div
								className="ars-r-lg px-4 py-4 grid gap-3"
								style={{ background: 'var(--ars-grad-soft)', border: '1px solid var(--ars-border)' }}
							>
								<div className="flex items-center justify-between gap-3">
									<div className="font-semibold text-[13px] flex items-center gap-1.5">
										<IconSpark size={13} />{__('Suggested from your own site', 'ai-rank-suite')}</div>
									<button
										type="button"
										aria-label={__('Dismiss suggestions', 'ai-rank-suite')}
										className="border-0 bg-transparent cursor-pointer p-1"
										style={{ color: 'var(--ars-ink-3)' }}
										onClick={() => setSuggestions(null)}
									>
										<IconX size={14} />
									</button>
								</div>
								{suggestions.length === 0 ? (
									<div className="text-[12.5px]" style={{ color: 'var(--ars-ink-2)' }}>{__('All suggestions added. Press “Suggest for me” again for a fresh set.', 'ai-rank-suite')}</div>
								) : (
									<>
										<div className="grid gap-1.5">
											{suggestions.map((p) => (
												<button
													key={p}
													type="button"
													onClick={() => addSuggestion(p)}
													className="flex items-center justify-between gap-3 text-left ars-r-lg px-3.5 py-2.5 cursor-pointer"
													style={{ background: 'var(--ars-surface)', border: '1px solid var(--ars-border)' }}
												>
													<span className="text-[12.5px]">{p}</span>
													<span className="text-[12px] font-semibold shrink-0" style={{ color: 'var(--ars-brand)' }}>
														+ {__('Add', 'ai-rank-suite')}
													</span>
												</button>
											))}
										</div>
										<div className="flex items-center gap-2">
											<Button
												variant="tonal"
												onClick={() => {
													const room = data.max_prompts - s.prompts.length;
													update({ prompts: [...s.prompts, ...suggestions.slice(0, room)] });
													setSuggestions([]);
												}}
											>{__('Add all', 'ai-rank-suite')}</Button>
											<span className="text-[11.5px]" style={{ color: 'var(--ars-ink-3)' }}>{__('Nothing is saved until you press Save.', 'ai-rank-suite')}</span>
										</div>
									</>
								)}
							</div>
						)}

						<p className="m-0 text-[12px]" style={{ color: 'var(--ars-ink-3)' }}>
							{__('Each check asks your configured AI model to answer these questions the way an assistant would — with no hint that you are asking — then records whether it recommends you, how early, and who else it names. Every prompt is one model call, so keep the list to the questions that actually convert.', 'ai-rank-suite')}
						</p>
					</div>
				</Card>
			)}

			{tab === 'brand' && (
				<div className="grid gap-5 lg:grid-cols-2 items-start">
					<Card
						title={__('Brand', 'ai-rank-suite')}
						subtitle={__('What the AI must say for a mention to count', 'ai-rank-suite')}
						action={saved && <Badge tone="good">✓ {__('Saved', 'ai-rank-suite')}</Badge>}
					>
						<div className="grid gap-4">
							<div>
								<label className="block font-medium mb-1">{__('Brand name', 'ai-rank-suite')}</label>
								<input
									type="text"
									placeholder={data.brand_auto}
									value={s.brand}
									onChange={(e) => update({ brand: e.target.value })}
								/>
								<div className="text-[11.5px] mt-1" style={{ color: 'var(--ars-ink-3)' }}>{__('Leave empty to use the site title.', 'ai-rank-suite')}</div>
							</div>
							<div>
								<label className="block font-medium mb-1.5">{__('Also known as', 'ai-rank-suite')}</label>
								<ListEditor
									items={s.aliases}
									onChange={(aliases) => update({ aliases })}
									placeholder={__('e.g. short name, domain', 'ai-rank-suite')}
									max={10}
								/>
							</div>
							<div>
								<label className="block font-medium mb-1.5">{__('Competitors to watch', 'ai-rank-suite')}</label>
								<ListEditor
									items={s.competitors}
									onChange={(competitors) => update({ competitors })}
									placeholder={__('e.g. competitor brand', 'ai-rank-suite')}
									max={10}
								/>
								<div className="text-[11.5px] mt-1" style={{ color: 'var(--ars-ink-3)' }}>
									{__('Rivals are detected automatically too — this list just makes sure the ones you care about are never missed.', 'ai-rank-suite')}
								</div>
							</div>
							<div>
								<Button onClick={save} busy={busy}>{__('Save', 'ai-rank-suite')}</Button>
							</div>
						</div>
					</Card>

					<Card title={__('Schedule', 'ai-rank-suite')} subtitle={__('Run answer simulations automatically so it becomes a trend', 'ai-rank-suite')}>
						<div className="grid gap-4">
							<Switch checked={!!s.auto} onChange={(v) => update({ auto: v })} label={__('Run simulations automatically', 'ai-rank-suite')} />
							<div className="sm:max-w-[260px]">
								<label className="block font-medium mb-1">{__('Frequency', 'ai-rank-suite')}</label>
								<select value={s.frequency} onChange={(e) => update({ frequency: e.target.value })}>
									<option value="weekly">{__('Weekly', 'ai-rank-suite')}</option>
									<option value="daily">{__('Daily', 'ai-rank-suite')}</option>
								</select>
								<div className="text-[11.5px] mt-1" style={{ color: 'var(--ars-ink-3)' }}>
									{sprintf(_n('Every prompt is one model call — with %1$d prompt, weekly costs about %2$d calls a month.', 'Every prompt is one model call — with %1$d prompts, weekly costs about %2$d calls a month.', s.prompts.length, 'ai-rank-suite'), s.prompts.length, s.prompts.length * 4)}
								</div>
							</div>
							<div>
								<Button variant="ghost" onClick={save} busy={busy}>{__('Save', 'ai-rank-suite')}</Button>
							</div>
						</div>
					</Card>
				</div>
			)}

			<PromptDrawer
				row={open}
				track={open ? (matrix?.prompts || []).find((p) => p.prompt === open.prompt) : null}
				runs={runs}
				onClose={() => setOpen(null)}
			/>
		</div>
	);
}
