import React, { useCallback, useEffect, useRef, useState } from 'react';
import { __, _n, sprintf } from '@wordpress/i18n';
import { api, env, moduleRunning } from '../api';
import { Card, Button, Badge, Notice, Bar, Segmented, Progress } from './ui';

/**
 * The Full Website Analyzer: how good is this site, page by page, as a whole.
 *
 * Built for ten pages and ten thousand alike. Every number is a server-side
 * aggregate from `/site-analyzer` (one response, two-minute cache); the only
 * list — the weakest pages — is paged on the server. Pressing the button runs
 * the existing bulk queue in slices from the browser, refilling itself page
 * by page, so a huge site finishes without a cron job or a long request, and
 * a run left half-done in another tab shows up here to be resumed.
 *
 * Two barometers because the two scores measure different things: GEO is
 * whether an answer engine can quote the page; SEO is whether Google can
 * rank it. The spread beside them exists because an average hides shape.
 */

const BANDS = ['0–19', '20–39', '40–59', '60–79', '80–100'];
const BAND_TONE = ['var(--ars-serious)', 'var(--ars-serious)', 'var(--ars-accent)', 'var(--ars-brand)', 'var(--ars-good)'];

const verdictOf = (pct) => (pct >= 80 ? __('Strong', 'ai-rank-suite') : pct >= 60 ? __('Solid', 'ai-rank-suite') : pct >= 40 ? __('Needs work', 'ai-rank-suite') : __('Weak', 'ai-rank-suite'));
const colorOf = (pct) => (pct >= 80 ? 'var(--ars-good)' : pct >= 60 ? 'var(--ars-brand)' : pct >= 40 ? 'var(--ars-accent)' : 'var(--ars-serious)');

/** A needle on a graded arc, with the verdict spelled out. */
function Barometer({ value, label, sub, delta }) {
	const pct = Math.max(0, Math.min(100, Number(value) || 0));
	const angle = -90 + (pct / 100) * 180;
	const C = Math.PI * 70;
	const color = colorOf(pct);
	return (
		<div className="ars-barometer">
			<svg viewBox="0 0 170 100" role="img" aria-label={sprintf(__('%1$s: %2$s of 100 — %3$s', 'ai-rank-suite'), label, pct, verdictOf(pct))}>
				<path d="M 15 90 A 70 70 0 0 1 155 90" fill="none" stroke="var(--ars-surface-3)" strokeWidth="14" strokeLinecap="round" />
				<path
					d="M 15 90 A 70 70 0 0 1 155 90"
					fill="none"
					stroke={color}
					strokeWidth="14"
					strokeLinecap="round"
					strokeDasharray={`${(pct / 100) * C} ${C}`}
					style={{ transition: 'stroke-dasharray 700ms cubic-bezier(0.2,0,0,1), stroke 300ms ease' }}
				/>
				<g style={{ transform: `rotate(${angle}deg)`, transformOrigin: '85px 90px', transition: 'transform 700ms cubic-bezier(0.2,0,0,1)' }}>
					<line x1="85" y1="90" x2="85" y2="34" stroke="var(--ars-ink)" strokeWidth="2.5" strokeLinecap="round" />
				</g>
				<circle cx="85" cy="90" r="5" fill="var(--ars-ink)" />
			</svg>
			<div className="ars-barometer-read">
				<span className="ars-barometer-value" style={{ color }}>
					{pct}
					{typeof delta === 'number' && delta !== 0 && (
						<span className="ars-barometer-delta" style={{ color: delta > 0 ? 'var(--ars-good)' : 'var(--ars-serious)' }}>
							{delta > 0 ? '▲' : '▼'}
							{Math.abs(delta)}
						</span>
					)}
				</span>
				<span className="ars-barometer-label">{label}</span>
				<span className="ars-barometer-verdict" style={{ color }}>
					{verdictOf(pct)}
				</span>
				{sub && <span className="t-caption">{sub}</span>}
			</div>
		</div>
	);
}

function Spread({ dist, label }) {
	const max = Math.max(1, ...dist);
	const total = dist.reduce((a, b) => a + b, 0);
	return (
		<div className="grid gap-1">
			<span className="t-overline">{label}</span>
			{/* The caption is the chart's whole meaning, and it used to be
			    nowhere: five numbers over five bars, and the reader had to work
			    out that the columns are score ranges and the numbers are pages.
			    One line says it. */}
			<span className="t-caption">{__('How many pages fall in each score range', 'ai-rank-suite')}</span>
			<div className="ars-spread">
				{dist.map((n, i) => (
					<div key={BANDS[i]} className="ars-spread-col" title={sprintf(__('%1$s pages scored %2$s', 'ai-rank-suite'), n, BANDS[i])}>
						<span className="ars-spread-n tabular-nums">{n.toLocaleString()}</span>
						{/* An empty range is drawn as a hairline in a neutral
						    tone, not as a 3%-tall stub in the band's own
						    colour — a red sliver under "0" reads as "a few
						    bad pages", which is the opposite of the truth. */}
						<span
							className={`ars-spread-bar${n === 0 ? ' is-empty' : ''}`}
							style={n === 0 ? undefined : { height: `${Math.max(6, (n / max) * 100)}%`, background: BAND_TONE[i] }}
						/>
						<span className="ars-spread-label">{BANDS[i]}</span>
						<span className="ars-spread-share tabular-nums">
							{total > 0 ? `${Math.round((n / total) * 100)}%` : '—'}
						</span>
					</div>
				))}
			</div>
		</div>
	);
}

/** Per-type / per-language rows: the same table twice. */
function BreakdownTable({ rows, keyField, labelField, onPick, active }) {
	return (
		<div className="ars-analyzer-table">
			<div className="ars-analyzer-row is-head">
				<span />
				<span>{__('Entries', 'ai-rank-suite')}</span>
				<span>{__('Scored', 'ai-rank-suite')}</span>
				<span>{__('GEO', 'ai-rank-suite')}</span>
				<span>{__('SEO', 'ai-rank-suite')}</span>
				<span>{__('Under 40', 'ai-rank-suite')}</span>
			</div>
			{rows.map((r) => (
				<button
					type="button"
					key={r[keyField]}
					className={`ars-analyzer-row${active === r[keyField] ? ' is-active' : ''}`}
					onClick={() => onPick(active === r[keyField] ? '' : r[keyField])}
					title={__('Click to scope the analyzer to this', 'ai-rank-suite')}
				>
					<span className="font-medium truncate">
						{r[labelField]}
						{r.rtl ? <Badge tone="neutral">{__('RTL', 'ai-rank-suite')}</Badge> : null}
					</span>
					<span className="tabular-nums">{r.total.toLocaleString()}</span>
					<span className="tabular-nums" style={{ color: r.analyzed < r.total ? 'var(--ars-accent)' : 'var(--ars-ink-2)' }}>
						{r.total > 0 ? Math.round((100 * r.analyzed) / r.total) : 0}%
					</span>
					<span className="tabular-nums font-semibold" style={{ color: colorOf(r.avg_geo) }}>
						{r.analyzed ? r.avg_geo : '–'}
					</span>
					<span className="tabular-nums font-semibold" style={{ color: colorOf(r.avg_seo) }}>
						{r.analyzed ? r.avg_seo : '–'}
					</span>
					<span className="tabular-nums" style={{ color: r.weak > 0 ? 'var(--ars-serious)' : 'var(--ars-ink-3)' }}>
						{r.weak.toLocaleString()}
					</span>
				</button>
			))}
		</div>
	);
}

/**
 * "de_DE" is a machine's word for a language. The browser knows the human
 * one in the reader's own locale, so a German admin reads "Deutsch (de_DE)"
 * and a code nobody recognises still shows its code rather than nothing.
 */
function languageName(code, fallback) {
	const tag = String(code || '').replace('_', '-');
	try {
		const name = new Intl.DisplayNames([document.documentElement.lang || 'en'], { type: 'language' }).of(tag);
		if (name && name.toLowerCase() !== tag.toLowerCase()) {
			return `${name} (${code})`;
		}
	} catch (e) {
		/* Intl.DisplayNames is missing or the tag is not one: fall through. */
	}
	return fallback || code;
}

function Weakest({ postType, lang }) {
	const [metric, setMetric] = useState('geo');
	const [page, setPage] = useState(1);
	const [data, setData] = useState(null);
	useEffect(() => {
		setPage(1);
	}, [postType, lang, metric]);
	useEffect(() => {
		const q = new URLSearchParams({ metric, page: String(page), per_page: '25' });
		if (postType) q.set('post_type', postType);
		if (lang) q.set('lang', lang);
		api.get(`/site-analyzer/weakest?${q}`).then(setData).catch(() => setData({ items: [], pagination: { pages: 0, total: 0 } }));
	}, [postType, lang, metric, page]);
	if (!data) return null;
	return (
		<div className="grid gap-2">
			<div className="flex items-center justify-between gap-3 flex-wrap">
				<span className="t-overline">{__('Weakest pages', 'ai-rank-suite')}</span>
				<Segmented value={metric} onChange={setMetric} options={[{ id: 'geo', label: __('by GEO', 'ai-rank-suite') }, { id: 'seo', label: __('by SEO', 'ai-rank-suite') }]} />
			</div>
			{data.items.length === 0 ? (
				<span className="t-caption">{__('No scored pages in this scope yet.', 'ai-rank-suite')}</span>
			) : (
				<div className="ars-analyzer-table is-pages">
					{data.items.map((it) => (
						<div key={it.id} className="ars-analyzer-row">
							<a href={`#/content/${it.id}`} className="truncate font-medium">
								{it.title || `#${it.id}`}
							</a>
							<span className="t-caption">{it.type}</span>
							<span className="tabular-nums font-semibold" style={{ color: colorOf(it.geo) }}>
								{it.geo}
							</span>
							<span className="tabular-nums font-semibold" style={{ color: colorOf(it.seo) }}>
								{it.seo}
							</span>
							<a href={it.edit} target="_blank" rel="noreferrer noopener" className="ars-linkbtn">
								{__('Edit', 'ai-rank-suite')} ↗
							</a>
						</div>
					))}
				</div>
			)}
			{data.pagination.pages > 1 && (
				<div className="flex items-center justify-between gap-3">
					<span className="t-caption">
						{__('Page', 'ai-rank-suite')} {page} {__('of', 'ai-rank-suite')} {data.pagination.pages} · {data.pagination.total.toLocaleString()} {__('pages', 'ai-rank-suite')}
					</span>
					<div className="flex items-center gap-1">
						<Button variant="ghost" disabled={page <= 1} onClick={() => setPage((p) => p - 1)}>
							‹
						</Button>
						<Button variant="ghost" disabled={page >= data.pagination.pages} onClick={() => setPage((p) => p + 1)}>
							›
						</Button>
					</div>
				</div>
			)}
		</div>
	);
}

const fmtSecs = (s) => (s < 60 ? `${s}s` : s < 3600 ? `${Math.round(s / 60)} min` : `${(s / 3600).toFixed(1)} h`);

export default function SiteAnalyzer({ onRefresh }) {
	const [board, setBoard] = useState(null);
	const [status, setStatus] = useState(null);
	const [scope, setScope] = useState('missing');
	const [postType, setPostType] = useState('');
	const [lang, setLang] = useState('');
	const [busy, setBusy] = useState(false);
	const [error, setError] = useState(null);
	const [tab, setTab] = useState('overview');
	const running = useRef(false);
	const alive = useRef(true);
	const [startedHere, setStartedHere] = useState(false);

	// The parent's refresh callback lives in a ref, never in a dependency
	// list. It is an inline arrow on the dashboard — a new function on every
	// render — and having it in `load`'s deps made load re-run on every
	// parent state change, which called onRefresh, which set parent state…
	// an infinite request loop that hammered /dashboard/kpis and
	// /score/content until the host reset the connections and every screen
	// read "Load failed". It is only called after a run finishes.
	const refreshRef = useRef(onRefresh);
	refreshRef.current = onRefresh;

	const load = useCallback(async () => {
		const q = new URLSearchParams();
		if (postType) q.set('post_type', postType);
		if (lang) q.set('lang', lang);
		try {
			setBoard(await api.get(`/site-analyzer${q.toString() ? `?${q}` : ''}`));
		} catch (e) {
			setError(e.message);
		}
	}, [postType, lang]);

	useEffect(() => {
		load();
	}, [load]);

	useEffect(() => {
		if (moduleRunning('bulk')) api.get('/bulk/status').then(setStatus).catch(() => {});
		return () => {
			alive.current = false;
		};
	}, []);

	const pump = useCallback(async () => {
		if (running.current) return;
		running.current = true;
		try {
			let s = await api.post('/bulk/tick');
			setStatus(s);
			let n = 0;
			while (s.state === 'running' && alive.current) {
				s = await api.post('/bulk/tick');
				setStatus(s);
				// Refresh the board every ~40 slices so the barometers move
				// during a long run, without hammering the aggregate query.
				if (++n % 40 === 0) await load();
			}
			await load();
			if (refreshRef.current) refreshRef.current();
		} catch (e) {
			setError(e.message);
		} finally {
			running.current = false;
			setBusy(false);
		}
	}, [load]);

	/*
	 * The queue is a paid module; scoring is not.
	 *
	 * /bulk/start lives in the Bulk module, which the free edition does not
	 * ship, so this button answered "no route matched the URL and request
	 * method" on every free install — on the screen whose entire purpose is
	 * scoring the site. The analyzer itself is local and free, so where there
	 * is no queue we drive the per-post route directly. Slower, and it needs
	 * the tab to stay open; it works, which the alternative did not.
	 */
	const HAS_BULK = !Array.isArray(env.modules) || env.modules.includes('bulk');

	const runWithoutQueue = async () => {
		setBusy(true);
		setError(null);
		setStartedHere(true);
		const seen = new Set();
		let pageNo = 1;
		try {
			for (;;) {
				const rows = await api.get(
					'all' === scope
						? `/content?type=any&per_page=50&page=${pageNo}`
						: '/content?type=any&filter=unscored&per_page=50&page=1'
				);
				if (!Array.isArray(rows) || rows.length === 0) break;
				const fresh = rows.filter((r) => !seen.has(r.id));
				// Scored rows leave the "unscored" filter, so that scope always
				// asks for page one. A pass with nothing new left in it means
				// the remaining rows cannot be scored — stop rather than spin.
				if (fresh.length === 0 && 'all' !== scope) break;
				for (const r of fresh) {
					seen.add(r.id);
					await api.post(`/content/${r.id}/analyze`);
				}
				if ('all' === scope) pageNo += 1;
				await load();
			}
			await load();
			if (refreshRef.current) refreshRef.current();
		} catch (e) {
			setError(e.message);
		} finally {
			setBusy(false);
		}
	};

	const start = async () => {
		if (!HAS_BULK) {
			runWithoutQueue();
			return;
		}
		setBusy(true);
		setError(null);
		try {
			setStartedHere(true);
			setStatus(await api.post('/bulk/start', { action: 'analyze', scope }));
			pump();
		} catch (e) {
			setError(e.message);
			setBusy(false);
		}
	};
	const cancel = async () => {
		try {
			setStatus(await api.post('/bulk/cancel', {}));
		} catch (e) {
			setError(e.message);
		}
	};

	const isRunning = status?.state === 'running';
	const isAnalyzeRun = isRunning && status?.action === 'analyze';
	const resumable = isAnalyzeRun && !running.current;

	const b = board;
	// One point per day the analyzer was read; each series is the same days.
	const samples = b?.samples || [];
	const day = (d) => d.day.slice(5).replace('-', '/');
	const trend = samples.map((d) => ({ date: day(d), label: day(d), value: d.geo }));
	const trendSeo = samples.map((d) => ({ date: day(d), label: day(d), value: d.seo }));
	const first = (arr) => (arr.length > 1 ? arr[0].value : null);
	const last = (arr) => (arr.length ? arr[arr.length - 1].value : null);
	const deltaGeo = trend.length > 1 ? last(trend) - first(trend) : null;
	const deltaSeo = trendSeo.length > 1 ? last(trendSeo) - first(trendSeo) : null;
	const coverage = b && b.total > 0 ? Math.round((100 * b.analyzed) / b.total) : 0;
	// Every published page already carries a score: the only work left is a
	// re-score, so that is what the button does.
	const nothingUnscored = !!b && (b.total || 0) > 0 && (b.unscored || 0) === 0;
	useEffect(() => {
		if (nothingUnscored && scope !== 'all') {
			setScope('all');
		}
	}, [nothingUnscored, scope]);

	const coldAreas = (b?.areas || []).filter((a) => a.pages > 0 && a.average !== null).sort((x, y) => x.average - y.average).slice(0, 4);

	return (
		<Card
			title={__('Full website analyzer', 'ai-rank-suite')}
			subtitle={__('Score every published page for AI search and for Google, then see where the site stands as a whole — by type, by language, over time.', 'ai-rank-suite')}
			action={
				isAnalyzeRun && running.current ? (
					<Button variant="ghost" onClick={cancel}>
						{__('Stop', 'ai-rank-suite')}
					</Button>
				) : (
					<div className="flex items-center gap-2 flex-wrap">
						{/* With nothing left unscored, "Unscored only" is a button
						    that cannot work: it returned a red error saying every
						    page already has this. The choice is only offered while
						    it has something to do. */}
						{nothingUnscored ? null : (
							<Segmented
								value={scope}
								onChange={setScope}
								options={[
									{ id: 'missing', label: __('Unscored only', 'ai-rank-suite') },
									{ id: 'all', label: __('Re-score everything', 'ai-rank-suite') },
								]}
							/>
						)}
						{resumable ? (
							<Button onClick={() => { setBusy(true); pump(); }} busy={busy}>
								{__('Resume run', 'ai-rank-suite')} ({(status.remaining || 0).toLocaleString()} {__('left', 'ai-rank-suite')})
							</Button>
						) : (
							<Button onClick={start} busy={busy} disabled={isRunning && !isAnalyzeRun}>
								{nothingUnscored
									? __('Re-score everything', 'ai-rank-suite')
									: b && b.analyzed > 0
										? __('Analyze site', 'ai-rank-suite')
										: __('Run first analysis', 'ai-rank-suite')}
							</Button>
						)}
					</div>
				)
			}
		>
			{error && <Notice tone="serious">{error}</Notice>}
			{isRunning && !isAnalyzeRun && (
				<Notice tone="warning">
					{__('Another bulk job is running', 'ai-rank-suite')} ({status.action}). {__('The analyzer waits until it finishes.', 'ai-rank-suite')}
				</Notice>
			)}

			{(isAnalyzeRun || (startedHere && status?.state === 'done' && status?.action === 'analyze')) && (
				<div className="mb-4">
					<Progress
						label={
							<>
								{isAnalyzeRun ? __('Scoring pages…', 'ai-rank-suite') : __('Finished', 'ai-rank-suite')} {(status.done || 0).toLocaleString()} {__('of', 'ai-rank-suite')} {(status.total || 0).toLocaleString()}
								{status.failed ? ` · ${status.failed} ${__('failed', 'ai-rank-suite')}` : ''}
								{isAnalyzeRun && b?.estimate?.per_post_ms && status.remaining ? ` · ~${fmtSecs(Math.ceil((status.remaining * b.estimate.per_post_ms) / 1000))} ${__('left', 'ai-rank-suite')}` : ''}
							</>
						}
						total={100}
						percent={status.progress ?? 0}
					/>
				</div>
			)}

			{/* Scope: post type and language push into SQL.
			 *
			 * These were two chip rows. A site with ten post types and
			 * fourteen languages — the size this plugin is built for — wrapped
			 * them into four lines of pills before the first number appeared,
			 * and a language chip read "de_DE" rather than "Deutsch". Two
			 * selects hold any number of entries in one line and name the
			 * languages. */}
			{b && (b.types.length > 1 || b.languages.length > 1) && (
				<div className="ars-scopebar mb-4">
					{b.types.length > 1 && (
						<label className="ars-scope">
							<span className="ars-scope-label">{__('Type', 'ai-rank-suite')}</span>
							<select value={postType} onChange={(e) => setPostType(e.target.value)}>
								<option value="">{sprintf(__('All types (%d)', 'ai-rank-suite'), b.types.length)}</option>
								{b.types.map((t) => (
									<option key={t.id} value={t.id}>{t.label}</option>
								))}
							</select>
						</label>
					)}
					{b.languages.length > 1 && (
						<label className="ars-scope">
							<span className="ars-scope-label">{__('Language', 'ai-rank-suite')}</span>
							<select value={lang} onChange={(e) => setLang(e.target.value)}>
								<option value="">{sprintf(__('All languages (%d)', 'ai-rank-suite'), b.languages.length)}</option>
								{b.languages.map((l) => (
									<option key={l.code} value={l.code}>{languageName(l.code, l.label)}</option>
								))}
							</select>
						</label>
					)}
				</div>
			)}

			{!b ? (
				<span className="t-caption">{__('Loading…', 'ai-rank-suite')}</span>
			) : b.analyzed === 0 ? (
				<div className="grid gap-2 py-4 text-center">
					<span className="font-semibold text-[13.5px]">
						{b.total.toLocaleString()} {__('published pages, none scored yet', 'ai-rank-suite')}
					</span>
					<span className="text-[12.5px]" style={{ color: 'var(--ars-ink-2)' }}>
						{__('The analysis runs on your own server, costs nothing, and can be stopped at any time. Every page gets a AI score for AI search and an SEO score for Google.', 'ai-rank-suite')}
						{b.estimate.seconds > 0 ? ` ${__('Estimated', 'ai-rank-suite')}: ~${fmtSecs(b.estimate.seconds)}.` : ''}
					</span>
				</div>
			) : (
				<div className="grid gap-5">
					<div className="grid gap-5 lg:grid-cols-[auto_auto_minmax(0,1fr)] items-center">
						<Barometer value={b.avg_geo} label={__('GEO', 'ai-rank-suite')} sub={__('AI search readiness', 'ai-rank-suite')} delta={deltaGeo} />
						<Barometer value={b.avg_seo} label={__('SEO', 'ai-rank-suite')} sub={__('Classic search', 'ai-rank-suite')} delta={deltaSeo} />
						<div className="grid gap-3 min-w-0">
							<div className="flex items-center justify-between gap-3 flex-wrap">
								<span className="text-[13px]">
									{/* One sentence, not five translated fragments glued together:
									    German puts the verb elsewhere, so "of" + "pages scored"
									    + "unscored, ~" could never read as German however well
									    each word was translated. */}
									{sprintf(
										/* translators: 1: scored entries, 2: total, 3: what is counted (post type or "entries"), 4: coverage percentage. */
										__('%1$s of %2$s %3$s scored (%4$s%%)', 'ai-rank-suite'),
										b.analyzed.toLocaleString(),
										b.total.toLocaleString(),
										postType ? ((b.types || []).find((t) => t.id === postType)?.label || __('entries', 'ai-rank-suite')) : __('entries', 'ai-rank-suite'),
										coverage
									)}
									{b.unscored > 0 && b.estimate.seconds > 0
										? ' · ' + sprintf(
											/* translators: 1: unscored pages, 2: estimated time. */
											__('%1$s unscored, about %2$s', 'ai-rank-suite'),
											b.unscored.toLocaleString(),
											fmtSecs(b.estimate.seconds)
										)
										: ''}
								</span>
								<span className="flex items-center gap-1.5 flex-wrap">
									{b.weak.geo > 0 ? <Badge tone="serious">{b.weak.geo.toLocaleString()} {__('GEO under 40', 'ai-rank-suite')}</Badge> : <Badge tone="good">{__('No GEO page under 40', 'ai-rank-suite')}</Badge>}
									{b.strong.geo > 0 && <Badge tone="good">{b.strong.geo.toLocaleString()} {__('GEO 80+', 'ai-rank-suite')}</Badge>}
								</span>
							</div>
							<div className="grid gap-3 sm:grid-cols-2">
								<Spread dist={b.dist.geo} label={__('GEO spread', 'ai-rank-suite')} />
								<Spread dist={b.dist.seo} label={__('SEO spread', 'ai-rank-suite')} />
							</div>
						</div>
					</div>

					{/*
					 * The movement charts live on the dashboard now, as three
					 * full-width cards beside the audit's. Repeating them inside
					 * this card put six "over time" charts on one screen, three
					 * of them saying the same thing at a third of the size.
					 */}
					<Segmented
						value={tab}
						onChange={setTab}
						options={[
							{ id: 'overview', label: __('Where it is weak', 'ai-rank-suite') },
							{ id: 'types', label: __('By type', 'ai-rank-suite') },
							...(b.by_lang.length > 0 ? [{ id: 'langs', label: __('By language', 'ai-rank-suite') }] : []),
							{ id: 'pages', label: __('Weakest pages', 'ai-rank-suite') },
						]}
					/>

					{tab === 'overview' && (
						<div className="grid gap-3">
							<span className="t-overline">{__('Coldest areas across the site', 'ai-rank-suite')}</span>
							{coldAreas.length === 0 ? (
								<span className="t-caption">{__('Area scores appear after the first analysis.', 'ai-rank-suite')}</span>
							) : (
								<div className="grid gap-2 sm:grid-cols-2">
									{coldAreas.map((a) => (
										<div key={a.key} className="ars-analyzer-area">
											<div className="flex items-center justify-between gap-2">
												<span className="font-semibold text-[13px]">{a.label}</span>
												<span className="tabular-nums font-bold" style={{ color: colorOf(a.average) }}>
													{a.average}
												</span>
											</div>
											<Bar value={a.average} color={colorOf(a.average)} />
											<span className="t-caption">
												{a.critical.toLocaleString()} {__('critical', 'ai-rank-suite')} · {a.weak.toLocaleString()} {__('weak', 'ai-rank-suite')} · {a.good.toLocaleString()} {__('good', 'ai-rank-suite')} · {a.pages.toLocaleString()} {__('pages', 'ai-rank-suite')}
											</span>
											{a.hint && <span className="t-caption">{a.hint}</span>}
										</div>
									))}
								</div>
							)}
							<span className="t-caption">
								{__('The same fix repeated across pages beats a perfect single page: start with the coldest area, and the autopilot or "Let AI do" can apply it site-wide.', 'ai-rank-suite')}
							</span>
						</div>
					)}

					{tab === 'types' && <BreakdownTable rows={b.by_type} keyField="type" labelField="label" onPick={setPostType} active={postType} />}
					{tab === 'langs' && (b.by_lang.length > 1 && b.by_lang.every((l) => l.total === b.by_lang[0].total && l.avg_geo === b.by_lang[0].avg_geo && l.avg_seo === b.by_lang[0].avg_seo) ? (
						<p className="m-0 px-4 py-3 t-caption">
							{sprintf(
								/* translators: %d: number of languages. */
								__('Your translation plugin translates per request, so every entry exists in all %d languages and every language mirrors the source content — a per-language table would repeat the same line %d times. Scores are shown for the source content above.', 'ai-rank-suite'),
								b.by_lang.length,
								b.by_lang.length
							)}
						</p>
					) : (
						<BreakdownTable rows={b.by_lang} keyField="code" labelField="label" onPick={setLang} active={lang} />
					))}

					{tab === 'pages' && <Weakest postType={postType} lang={lang} />}
				</div>
			)}
		</Card>
	);
}
