import { __, _n, sprintf } from '@wordpress/i18n';
import React, { useEffect, useMemo, useState } from 'react';
import { api, env } from '../api';
import { Card, Button, Switch, Badge, Notice, Tabs, MetricGrid, PageState } from '../components/ui';
import Identity from '../components/Identity';
import { IconX, IconSpark } from '../components/icons';
import { copyText } from '../copy';
import ScreenVerdict from '../components/ScreenVerdict';

/* ---------------------------------------------------------------- Overview */

/**
 * The graph exactly as it ships — no view-source, no external validator round
 * trip to answer "is my structured data actually there?".
 */
function LiveGraph() {
	const [data, setData] = useState(null);
	const [tab, setTab] = useState(0);
	const [copied, setCopied] = useState(false);

	useEffect(() => {
		api.get('/schema/live').then(setData).catch(() => setData({ samples: [], validators: [] }));
	}, []);

	if (!data) return <Card title={__('Live structured data', 'ai-rank-suite')}>{__('Loading…', 'ai-rank-suite')}</Card>;
	if (!data.samples?.length) return null;

	const sample = data.samples[Math.min(tab, data.samples.length - 1)];

	return (
		<Card
			title={__('Live structured data', 'ai-rank-suite')}
			subtitle={__('Exactly the JSON-LD your visitors and Google receive — built by the same code that writes your pages', 'ai-rank-suite')}
			action={
				data.samples.length > 1 && (
					<Tabs
						tabs={data.samples.map((s2, i) => ({ id: String(i), label: s2.label }))}
						active={String(tab)}
						onChange={(v) => setTab(Number(v))}
					/>
				)
			}
		>
			<div className="grid gap-3">
				<div className="flex items-center gap-1.5 flex-wrap">
					{sample.types.map((t) => (
						<Badge key={t} tone="brand">
							{t}
						</Badge>
					))}
					{sample.issues.length === 0 ? (
						<Badge tone="good">✓ No validation issues</Badge>
					) : (
						<Badge tone="warning">{sprintf(_n('%d to fix', '%d to fix', sample.issues.length, 'ai-rank-suite'), sample.issues.length)}</Badge>
					)}
				</div>

				{sample.issues.length > 0 && (
					<Notice tone="warning">
						<ul className="m-0 pl-4 grid gap-0.5">
							{sample.issues.map((i) => (
								<li key={i}>{i}</li>
							))}
						</ul>
					</Notice>
				)}

				<div className="flex items-center gap-2 flex-wrap">
					<Button
						variant="ghost"
						onClick={async () => {
							if (!(await copyText(sample.json))) return;
							setCopied(true);
							setTimeout(() => setCopied(false), 2000);
						}}
					>
						{copied ? '✓ Copied' : __('Copy JSON-LD', 'ai-rank-suite')}
					</Button>
					{data.validators.map((v) => (
						<a
							key={v.label}
							href={v.url}
							target="_blank"
							rel="noreferrer"
							className="text-[12.5px] font-medium"
							style={{ color: 'var(--ars-brand)', textDecoration: 'none' }}
						>
							{v.label} ↗
						</a>
					))}
				</div>

				<pre
					className="ars-mono m-0 p-4 ars-r-lg overflow-auto text-[11.5px] leading-relaxed"
					style={{ background: 'var(--ars-code-bg)', color: 'var(--ars-code-ink)', maxHeight: 380 }}
				>
					{sample.json}
				</pre>
				<p className="m-0 text-[11.5px]" style={{ color: 'var(--ars-ink-3)' }}>
					{sample.url}
				</p>
			</div>
		</Card>
	);
}


/**
 * The types worth putting in front of somebody.
 *
 * The library holds 77 and hides all of them equally: Article, Product and
 * Recipe end up as grey chips in the third tab, so a customer looking for
 * "does this do Product schema" concludes it does not. These twelve are the
 * ones people come looking for, on the first screen, with the field count and
 * whether they are already live.
 */
function RichResults() {
	const [meta, setMeta] = useState(null);

	useEffect(() => {
		api.get('/schema/types').then(setMeta).catch(() => {});
	}, []);

	if (!meta?.featured?.length) return null;

	return (
		<Card
			title={__('Rich results you can produce', 'ai-rank-suite')}
			subtitle={sprintf(__('%d schema.org types are available — these are the ones most sites need', 'ai-rank-suite'), Object.keys(meta.types).length)}
		>
			<div className="ars-cardgrid" style={{ ["--min"]: "250px" }}>
				{meta.featured.map((f) => {
					const used = meta.usage?.[f.type] || 0;
					return (
						<div
							key={f.type}
							className="p-3"
							style={{
								border: '1px solid var(--ars-border)',
								borderRadius: 'var(--ars-r-sm)',
								background: 'var(--ars-surface)',
							}}
						>
							{/*
							  * Name and status wrap among themselves; the field
							  * count keeps its own column and never breaks. A
							  * single flex row put "12 fields" outside the card
							  * as soon as a translation ran long.
							  */}
							<div className="ars-richhead">
								<div className="ars-richhead-name">
									<span className="font-semibold text-[13px]">{f.label}</span>
									{f.auto ? (
										<span
											className="ars-tag"
											style={{ background: 'var(--ars-c-green)', color: 'var(--ars-c-green-ink)' }}
											title={__('Emitted automatically, no setup needed', 'ai-rank-suite')}
										>{__('Automatic', 'ai-rank-suite')}</span>
									) : used > 0 ? (
										<span
											className="ars-tag"
											style={{ background: 'var(--ars-c-blue)', color: 'var(--ars-c-blue-ink)' }}
										>
											{sprintf(
												_n('on %d page', 'on %d pages', used, 'ai-rank-suite'),
												used
											)}
										</span>
									) : (
										<span
											className="ars-tag"
											style={{ background: 'var(--ars-surface-3)', color: 'var(--ars-ink-3)' }}
										>{__('Not used yet', 'ai-rank-suite')}</span>
									)}
								</div>
								<span
									className="ars-richhead-count"
									style={{ color: 'var(--ars-ink-3)' }}
									title={__('How many fields this type can carry', 'ai-rank-suite')}
								>
									{sprintf(
										_n('%d field', '%d fields', f.fields, 'ai-rank-suite'),
										f.fields
									)}
								</span>
							</div>
							<p className="m-0 mt-1 text-[11.5px] leading-snug" style={{ color: 'var(--ars-ink-2)' }}>
								{f.why}
							</p>
						</div>
					);
				})}
			</div>
			<p className="m-0 mt-3 text-[12px]" style={{ color: 'var(--ars-ink-3)' }}>{__('Add any of them to a single page in', 'ai-rank-suite')}<strong>{__('Content → open a page → Schema', 'ai-rank-suite')}</strong>, or to a whole post type
				under <strong>{__('Templates', 'ai-rank-suite')}</strong>. Every other type lives in the <strong>{__('Library', 'ai-rank-suite')}</strong> tab.
			</p>
		</Card>
	);
}

function Overview({ data, setData }) {
	const [busy, setBusy] = useState(false);
	const [saved, setSaved] = useState(false);
	const [error, setError] = useState(null);
	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/schema', (({ identity, ...rest }) => rest)(s)));
			setSaved(true);
			setTimeout(() => setSaved(false), 2200);
		} catch (e) {
			setError(e.message);
		} finally {
			setBusy(false);
		}
	};

	return (
		<div className="grid gap-4">
			{data.conflict && !s.force_output && (
				<Notice>
					<strong>{data.conflict}</strong> already outputs structured data — AI Rank Suite defers automatically to avoid a
					duplicate graph.
				</Notice>
			)}

			<Card
				title={__('Structured data (JSON-LD)', 'ai-rank-suite')}
				subtitle={__('One connected @graph: Organization, WebSite, WebPage/Article, Breadcrumbs, FAQPage — plus anything your templates add', 'ai-rank-suite')}
				action={saved && <Badge tone="good">✓ Saved</Badge>}
			>
				<div className="grid gap-4">
					<Switch checked={!!s.enabled} onChange={(v) => update({ enabled: v })} label={__('Output JSON-LD', 'ai-rank-suite')} />

					<div className="grid gap-3 sm:grid-cols-2">
						<div>
							<label className="block font-medium mb-1">{__('Publisher name', 'ai-rank-suite')}</label>
							<input
								type="text"
								placeholder={env.siteName}
								value={s.org_name}
								onChange={(e) => update({ org_name: e.target.value })}
							/>
						</div>
						<div>
							<label className="block font-medium mb-1">{__('Publisher type', 'ai-rank-suite')}</label>
							<select value={s.org_type} onChange={(e) => update({ org_type: e.target.value })}>
								<option value="Organization">{__('Organization', 'ai-rank-suite')}</option>
								<option value="Person">{__('Person', 'ai-rank-suite')}</option>
								<option value="LocalBusiness">{__('LocalBusiness', 'ai-rank-suite')}</option>
							</select>
						</div>
					</div>

					{data.conflict && (
						<Switch
							checked={!!s.force_output}
							onChange={(v) => update({ force_output: v })}
							label={`Force output although ${data.conflict} is active (advanced)`}
						/>
					)}

					<div>
						<Button onClick={save} busy={busy}>{__('Save', 'ai-rank-suite')}</Button>
					</div>
					{error && <Notice tone="serious">{error}</Notice>}
				</div>
			</Card>

			<Card title={__('FAQ schema', 'ai-rank-suite')} subtitle={__('Generated FAQs are emitted as FAQPage automatically', 'ai-rank-suite')}>
				<p className="m-0 text-[13px]" style={{ color: 'var(--ars-ink-2)' }}>{__('Generate FAQs per page in', 'ai-rank-suite')}<strong>{__('Content', 'ai-rank-suite')}</strong> → open a page → <em>{__('FAQ', 'ai-rank-suite')}</em>. Saved items appear in the
					JSON-LD graph and can be rendered anywhere with the{' '}
					<code className="ars-mono px-1.5 py-0.5 rounded" style={{ background: 'var(--ars-surface-2)' }}>
						[airanksuite_faq]
					</code>{' '}
					shortcode.
				</p>
			</Card>

			<LiveGraph />
		</div>
	);
}

/* --------------------------------------------------------------- Templates */

const uid = () => `tpl_${Math.random().toString(36).slice(2, 10)}`;

/** One template row, expanded for editing. */
function TemplateEditor({ template, meta, onChange, onRemove }) {
	const fields = meta.types[template.type]?.fields || [];
	const required = fields.filter((f) => f.required);
	const recommended = fields.filter((f) => f.recommended && !f.required);
	const rest = fields.filter((f) => !f.required && !f.recommended);
	const [showRest, setShowRest] = useState(false);

	const setData = (key, value) => onChange({ ...template, data: { ...template.data, [key]: value } });
	const setCond = (key, value) => onChange({ ...template, conditions: { ...template.conditions, [key]: value } });

	const missing = required.filter((f) => !String(template.data[f.key] ?? '').trim());
	const scoped =
		template.conditions.post_types.length > 0 ||
		template.conditions.terms.length > 0 ||
		template.conditions.post_ids.length > 0;

	const Field = ({ f }) => (
		<div key={f.key}>
			<label className="block font-medium mb-1 text-[12.5px]">
				{f.label}
				{f.required && <span style={{ color: 'var(--ars-serious)' }}> *</span>}
			</label>
			{f.type === 'select' ? (
				<select value={template.data[f.key] ?? ''} onChange={(e) => setData(f.key, e.target.value)}>
					<option value="">— not set —</option>
					{(f.options || []).map((o) => (
						<option key={o} value={o}>
							{o}
						</option>
					))}
				</select>
			) : f.type === 'textarea' ? (
				<textarea rows={2} value={template.data[f.key] ?? ''} onChange={(e) => setData(f.key, e.target.value)} />
			) : (
				<input
					type="text"
					placeholder={f.hint}
					value={template.data[f.key] ?? ''}
					onChange={(e) => setData(f.key, e.target.value)}
				/>
			)}
		</div>
	);

	return (
		<div className="ars-r-lg p-4 grid gap-4" style={{ background: 'var(--ars-surface-2)' }}>
			<div className="flex items-start justify-between gap-3 flex-wrap">
				<div className="flex items-center gap-3 flex-wrap min-w-0">
					<input
						type="text"
						value={template.name}
						onChange={(e) => onChange({ ...template, name: e.target.value })}
						style={{ maxWidth: 240 }}
						aria-label={__('Template name', 'ai-rank-suite')}
					/>
					<Badge tone="brand">{meta.types[template.type]?.label || template.type}</Badge>
					{missing.length > 0 && <Badge tone="warning">{sprintf(_n('%s required field empty', '%s required fields empty', missing.length, 'ai-rank-suite'), missing.length)}</Badge>}
					{!scoped && <Badge tone="serious">no scope — never applies</Badge>}
				</div>
				<div className="flex items-center gap-2">
					<Switch checked={!!template.enabled} onChange={(v) => onChange({ ...template, enabled: v })} label={__('Active', 'ai-rank-suite')} />
					<button
						type="button"
						onClick={onRemove}
						aria-label={__('Remove template', 'ai-rank-suite')}
						className="border-0 bg-transparent cursor-pointer p-1"
						style={{ color: 'var(--ars-ink-3)' }}
					>
						<IconX size={14} />
					</button>
				</div>
			</div>

			<div className="grid gap-3">
				<div className="text-[11px] font-semibold uppercase tracking-wider" style={{ color: 'var(--ars-ink-3)' }}>{__('Where it applies', 'ai-rank-suite')}</div>
				<div className="grid gap-3 sm:grid-cols-2">
					<div>
						<label className="block font-medium mb-1 text-[12.5px]">{__('Content types', 'ai-rank-suite')}</label>
						<div className="flex flex-wrap gap-2">
							{meta.post_types.map((pt) => {
								const on = template.conditions.post_types.includes(pt);
								return (
									<button
										key={pt}
										type="button"
										onClick={() =>
											setCond(
												'post_types',
												on
													? template.conditions.post_types.filter((x) => x !== pt)
													: [...template.conditions.post_types, pt]
											)
										}
										className="px-3 py-1 ars-r-sm text-[12px] cursor-pointer"
										style={{
											border: `1px solid ${on ? 'var(--ars-brand)' : 'var(--ars-border)'}`,
											background: on ? 'var(--ars-brand-tonal)' : 'var(--ars-surface)',
											color: on ? 'var(--ars-brand-tonal-ink)' : 'var(--ars-ink-2)',
										}}
									>
										{pt}
									</button>
								);
							})}
						</div>
					</div>
					<div>
						<label className="block font-medium mb-1 text-[12.5px]">{__('Only in these terms (optional)', 'ai-rank-suite')}</label>
						<select
							multiple
							size={4}
							value={template.conditions.terms.map(String)}
							onChange={(e) =>
								setCond(
									'terms',
									[...e.target.selectedOptions].map((o) => Number(o.value))
								)
							}
						>
							{meta.taxonomies.map((tax) => (
								<optgroup key={tax.name} label={tax.name}>
									{tax.terms.map((t) => (
										<option key={t.id} value={t.id}>
											{t.name}
										</option>
									))}
								</optgroup>
							))}
						</select>
					</div>
				</div>
				{!scoped && (
					<p className="m-0 text-[11.5px]" style={{ color: 'var(--ars-serious)' }}>
						{__('Pick at least one content type or term. A template with no scope is never applied — that is deliberate, so a half-finished rule can never blanket the whole site.', 'ai-rank-suite')}
					</p>
				)}
			</div>

			<div className="grid gap-3">
				<div className="text-[11px] font-semibold uppercase tracking-wider" style={{ color: 'var(--ars-ink-3)' }}>{__('Values', 'ai-rank-suite')}</div>
				{required.length > 0 && <div className="grid gap-3 sm:grid-cols-2">{required.map((f) => <Field key={f.key} f={f} />)}</div>}
				{recommended.length > 0 && (
					<div className="grid gap-3 sm:grid-cols-2">{recommended.map((f) => <Field key={f.key} f={f} />)}</div>
				)}
				{rest.length > 0 && (
					<>
						<button
							type="button"
							onClick={() => setShowRest(!showRest)}
							className="border-0 bg-transparent cursor-pointer p-0 text-[12.5px] font-semibold text-left"
							style={{ color: 'var(--ars-brand)' }}
						>
							{showRest ? 'Hide' : `Show ${rest.length} optional field${rest.length === 1 ? '' : 's'}`}
						</button>
						{showRest && <div className="grid gap-3 sm:grid-cols-2">{rest.map((f) => <Field key={f.key} f={f} />)}</div>}
					</>
				)}
				<p className="m-0 text-[11.5px]" style={{ color: 'var(--ars-ink-3)' }}>
					Variables are replaced per page:{' '}
					{Object.keys(meta.variables)
						.slice(0, 6)
						.map((v) => (
							<code key={v} className="ars-mono px-1 py-0.5 rounded mr-1" style={{ background: 'var(--ars-surface)' }}>
								{v}
							</code>
						))}
				</p>
			</div>
		</div>
	);
}

function Templates({ onSaved }) {
	const [meta, setMeta] = useState(null);
	const [templates, setTemplates] = useState([]);
	const [picking, setPicking] = useState(false);
	const [search, setSearch] = useState('');
	const [busy, setBusy] = useState(false);
	const [saved, setSaved] = useState(false);
	const [error, setError] = useState(null);
	const [dirty, setDirty] = useState(false);

	const load = (payload) => {
		const byKey = {};
		Object.entries(payload.types).forEach(([key, t]) => {
			byKey[key] = t;
		});
		setMeta({
			types: byKey,
			grouped: payload.grouped,
			variables: payload.variables,
			post_types: payload.post_types,
			taxonomies: payload.taxonomies,
		});
		setTemplates(payload.templates);
	};

	useEffect(() => {
		api.get('/schema/templates').then(load).catch((e) => setError(e.message));
	}, []);

	const matches = useMemo(() => {
		if (!meta) return [];
		const q = search.trim().toLowerCase();
		return Object.entries(meta.grouped)
			.map(([group, items]) => [group, items.filter((i) => !q || i.label.toLowerCase().includes(q))])
			.filter(([, items]) => items.length > 0);
	}, [meta, search]);

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

	const add = (type) => {
		setTemplates([
			...templates,
			{
				id: uid(),
				name: meta.types[type]?.label || type,
				type,
				enabled: true,
				data: {},
				conditions: { post_types: [], terms: [], post_ids: [], exclude: [] },
			},
		]);
		setPicking(false);
		setSearch('');
		setDirty(true);
	};

	const save = async () => {
		setBusy(true);
		setError(null);
		try {
			load(await api.put('/schema/templates', { templates }));
			setDirty(false);
			setSaved(true);
			// The KPI strip lives one level up; a stale "0 / 0 templates" right
			// after creating one reads as a failed save.
			onSaved?.();
			setTimeout(() => setSaved(false), 2200);
		} catch (e) {
			setError(e.message);
		} finally {
			setBusy(false);
		}
	};

	return (
		<div className="grid gap-4">
			{error && <Notice tone="serious">{error}</Notice>}

			<Card
				title={__('Schema templates', 'ai-rank-suite')}
				subtitle={__('Rules instead of chores: “every product in Shoes is a Product with this brand and size system”, written once and applied to every matching page forever', 'ai-rank-suite')}
				action={
					<div className="flex items-center gap-2">
						{saved && <Badge tone="good">✓ Saved</Badge>}
						{/* With no templates the empty state below already offers this,
						    in a different colour and with a different label. One call to
						    action per screen. */}
						{(picking || templates.length > 0) && (
							<Button variant="ghost" onClick={() => setPicking(!picking)}>
								{picking ? 'Cancel' : __('Add template', 'ai-rank-suite')}
							</Button>
						)}
						{dirty && (
							<Button onClick={save} busy={busy}>{__('Save templates', 'ai-rank-suite')}</Button>
						)}
					</div>
				}
			>
				<div className="grid gap-4">
					{picking && (
						<div className="ars-r-lg p-4 grid gap-3" style={{ background: 'var(--ars-grad-soft)' }}>
							<div className="flex items-center gap-3 flex-wrap">
								<span className="font-semibold text-[13px] flex items-center gap-1.5">
									<IconSpark size={13} />{__('Pick a type', 'ai-rank-suite')}</span>
								<input
									type="text"
									placeholder={__('Search 77 types…', 'ai-rank-suite')}
									value={search}
									onChange={(e) => setSearch(e.target.value)}
									style={{ maxWidth: 260 }}
									autoFocus
								/>
							</div>
							<div className="grid gap-3" style={{ maxHeight: 340, overflowY: 'auto' }}>
								{matches.map(([group, items]) => (
									<div key={group} className="grid gap-1.5">
										<div
											className="text-[11px] font-semibold uppercase tracking-wider"
											style={{ color: 'var(--ars-ink-3)' }}
										>
											{group}
										</div>
										<div className="flex flex-wrap gap-1.5">
											{items.map((i) => (
												<button
													key={i.value}
													type="button"
													onClick={() => add(i.value)}
													className="px-3 py-1.5 ars-r-sm text-[12.5px] cursor-pointer"
													style={{ background: 'var(--ars-surface)', border: '1px solid var(--ars-border)' }}
												>
													{i.label}
												</button>
											))}
										</div>
									</div>
								))}
								{matches.length === 0 && (
									<div className="text-[12.5px]" style={{ color: 'var(--ars-ink-3)' }}>
										No type matches “{search}”.
									</div>
								)}
							</div>
						</div>
					)}

					{templates.length === 0 && !picking && (
						<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 templates yet', 'ai-rank-suite')}</div>
							<div className="text-[12px] max-w-[74ch]" style={{ color: 'var(--ars-ink-3)' }}>
								{__('Your pages already get Organization, WebSite, WebPage/Article, Breadcrumbs and FAQPage automatically. A template adds a richer type — Recipe, Event, Course, SoftwareApplication, JobPosting — to a whole group of pages at once.', 'ai-rank-suite')}
							</div>
							<Button onClick={() => setPicking(true)}>{__('Add template', 'ai-rank-suite')}</Button>
						</div>
					)}

					{templates.map((t, i) => (
						<TemplateEditor
							key={t.id}
							template={t}
							meta={meta}
							onChange={(next) => {
								setTemplates(templates.map((x, j) => (j === i ? next : x)));
								setDirty(true);
							}}
							onRemove={() => {
								setTemplates(templates.filter((_, j) => j !== i));
								setDirty(true);
							}}
						/>
					))}

					{dirty && <Notice tone="info">{__('Nothing is live until you press “Save templates”.', 'ai-rank-suite')}</Notice>}
				</div>
			</Card>
		</div>
	);
}

/* ------------------------------------------------------------ Type library */

function TypeLibrary() {
	const [meta, setMeta] = useState(null);
	const [search, setSearch] = useState('');
	const [open, setOpen] = useState(null);
	const [error, setError] = useState(null);

	useEffect(() => {
		api.get('/schema/types').then(setMeta).catch((e) => setError(e.message));
	}, []);

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

	const q = search.trim().toLowerCase();
	const groups = Object.entries(meta.grouped)
		.map(([group, items]) => [group, items.filter((i) => !q || i.label.toLowerCase().includes(q) || i.value.toLowerCase().includes(q))])
		.filter(([, items]) => items.length > 0);
	const total = Object.keys(meta.types).length;

	return (
		<div className="grid gap-4">
			<Card
				title={__('Type library', 'ai-rank-suite')}
				subtitle={sprintf(__('All %d schema.org types this plugin can emit, with the fields Google actually reads for each', 'ai-rank-suite'), total)}
				action={
					<input
						type="text"
						placeholder={__('Search types…', 'ai-rank-suite')}
						value={search}
						onChange={(e) => setSearch(e.target.value)}
						style={{ maxWidth: 240 }}
						aria-label={__('Search schema types', 'ai-rank-suite')}
					/>
				}
			>
				<div className="grid gap-5">
					{groups.map(([group, items]) => (
						<div key={group} className="grid gap-2">
							<div className="text-[11px] font-semibold uppercase tracking-wider" style={{ color: 'var(--ars-ink-3)' }}>
								{group} · {items.length}
							</div>
							<div className="flex flex-wrap gap-1.5">
								{items.map((i) => {
									const active = open === i.value;
									const fields = meta.types[i.value]?.fields || [];
									return (
										<button
											key={i.value}
											type="button"
											onClick={() => setOpen(active ? null : i.value)}
											className="px-3 py-1.5 ars-r-sm text-[12.5px] cursor-pointer"
											style={{
												border: `1px solid ${active ? 'var(--ars-brand)' : 'var(--ars-border)'}`,
												background: active ? 'var(--ars-brand-tonal)' : 'var(--ars-surface-2)',
												color: active ? 'var(--ars-brand-tonal-ink)' : 'var(--ars-ink-2)',
											}}
										>
											{i.label}
											<span className="ml-1.5 text-[11px]" style={{ opacity: 0.65 }}>
												{fields.length}
											</span>
										</button>
									);
								})}
							</div>
						</div>
					))}
					{groups.length === 0 && (
						<p className="m-0 text-[13px]" style={{ color: 'var(--ars-ink-3)' }}>
							No type matches “{search}”.
						</p>
					)}
				</div>
			</Card>

			{open && meta.types[open] && (
				<Card
					title={meta.types[open].label}
					subtitle={`schema.org/${open} — ${(meta.types[open].fields || []).length} fields`}
					action={
						<a
							href={`https://schema.org/${open}`}
							target="_blank"
							rel="noreferrer"
							className="text-[12.5px] font-medium"
							style={{ color: 'var(--ars-brand)', textDecoration: 'none' }}
						>
							schema.org ↗
						</a>
					}
				>
					<div className="ars-table-wrap"><table className="ars-table">
						<thead>
							<tr>
								<th>{__('Field', 'ai-rank-suite')}</th>
								<th>{__('Property', 'ai-rank-suite')}</th>
								<th>{__('Example', 'ai-rank-suite')}</th>
								<th className="is-num">{__('Weight', 'ai-rank-suite')}</th>
							</tr>
						</thead>
						<tbody>
							{(meta.types[open].fields || []).map((f) => (
								<tr key={f.key}>
									<td className="font-medium">{f.label}</td>
									<td className="ars-mono text-[11.5px]" style={{ color: 'var(--ars-ink-2)' }}>
										{f.prop}
									</td>
									<td className="text-[12px]" style={{ color: 'var(--ars-ink-3)' }}>
										{f.hint || '—'}
									</td>
									<td className="is-num">
										{f.required ? (
											<Badge tone="serious">required</Badge>
										) : f.recommended ? (
											<Badge tone="warning">recommended</Badge>
										) : (
											<Badge tone="neutral">optional</Badge>
										)}
									</td>
								</tr>
							))}
						</tbody>
					</table></div>
				</Card>
			)}

			<Card title={__('Variables', 'ai-rank-suite')} subtitle={__('Placeholders replaced per page wherever a template value accepts text', 'ai-rank-suite')}>
				<div className="ars-cardgrid" style={{ ["--min"]: "300px", gap: 6 }}>
					{Object.entries(meta.variables).map(([token, label]) => (
						<div
							key={token}
							className="flex items-center justify-between gap-3 ars-r-lg px-3 py-2"
							style={{ background: 'var(--ars-surface-2)' }}
						>
							<code className="ars-mono text-[12px]">{token}</code>
							<span className="text-[12px]" style={{ color: 'var(--ars-ink-2)' }}>
								{label}
							</span>
						</div>
					))}
				</div>
			</Card>
		</div>
	);
}

/* ---------------------------------------------------------------- Products */

/**
 * WooCommerce product schema. Lives here because it *is* structured data —
 * a separate nav item for one card would only fragment the mental model.
 */
function Products() {
	const [data, setData] = useState(null);
	const [busy, setBusy] = useState(false);
	const [saved, setSaved] = useState(false);
	const [error, setError] = useState(null);

	useEffect(() => {
		api.get('/settings/woocommerce').then(setData).catch(() => setData({ active: false }));
	}, []);

	if (!data) return <Card>{__('Loading…', 'ai-rank-suite')}</Card>;
	if (data.active === false) {
		return (
			<Card title={__('Product structured data', 'ai-rank-suite')} subtitle={__('Appears as soon as WooCommerce is active', 'ai-rank-suite')}>
				<p className="m-0 text-[13px]" style={{ color: 'var(--ars-ink-2)' }}>
					{__('WooCommerce is not active on this site, so there are no products to describe. Everything else on this page works regardless.', 'ai-rank-suite')}
				</p>
			</Card>
		);
	}
	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/woocommerce', s));
			setSaved(true);
			setTimeout(() => setSaved(false), 2200);
		} catch (e) {
			setError(e.message);
		} finally {
			setBusy(false);
		}
	};

	const Num = ({ label, k, max = 30 }) => (
		<div>
			<label className="block font-medium mb-1 text-[12.5px]">{label}</label>
			<input
				type="number"
				min="0"
				max={max}
				value={s[k] ?? 0}
				onChange={(e) => update({ [k]: Math.max(0, Number(e.target.value) || 0) })}
			/>
		</div>
	);

	return (
		<Card
			title={__('Product structured data', 'ai-rank-suite')}
			subtitle={__('Every field Google Merchant Center asks for — identifiers, shipping, returns and reviews', 'ai-rank-suite')}
			action={
				<div className="flex items-center gap-2">
					{saved && <Badge tone="good">✓ Saved</Badge>}
					<Button onClick={save} busy={busy}>{__('Save', 'ai-rank-suite')}</Button>
				</div>
			}
		>
			<div className="grid gap-4">
				{error && <Notice tone="serious">{error}</Notice>}

				<Switch
					checked={!!s.identifiers}
					onChange={(v) => update({ identifiers: v })}
					label={__('Publish global identifiers (GTIN, EAN, UPC, ISBN, MPN)', 'ai-rank-suite')}
				/>
				<p className="m-0 -mt-2 text-[11.5px]" style={{ color: 'var(--ars-ink-3)' }}>
					{__('Read automatically from WooCommerce core and the common GTIN plugins — no re-entry needed. Without one of these Google rejects the product entirely.', 'ai-rank-suite')}
				</p>

				<div className="sm:max-w-[260px]">
					<label className="block font-medium mb-1">{__('Default condition', 'ai-rank-suite')}</label>
					<select value={s.condition} onChange={(e) => update({ condition: e.target.value })}>
						<option value="new">{__('New', 'ai-rank-suite')}</option>
						<option value="refurbished">{__('Refurbished', 'ai-rank-suite')}</option>
						<option value="used">{__('Used', 'ai-rank-suite')}</option>
						<option value="damaged">{__('Damaged', 'ai-rank-suite')}</option>
					</select>
				</div>

				<div className="grid gap-3 ars-r-lg p-4" style={{ background: 'var(--ars-surface-2)' }}>
					<Switch
						checked={!!s.shipping_details}
						onChange={(v) => update({ shipping_details: v })}
						label={__('Publish shipping details (handling + transit time)', 'ai-rank-suite')}
					/>
					{!!s.shipping_details && (
						<div className="grid gap-3 sm:grid-cols-4">
							<Num label={__('Handling min (days)', 'ai-rank-suite')} k="handling_days_min" />
							<Num label={__('Handling max', 'ai-rank-suite')} k="handling_days_max" />
							<Num label={__('Transit min', 'ai-rank-suite')} k="transit_days_min" />
							<Num label={__('Transit max', 'ai-rank-suite')} k="transit_days_max" />
						</div>
					)}
				</div>

				<div className="grid gap-3 ars-r-lg p-4" style={{ background: 'var(--ars-surface-2)' }}>
					<Switch
						checked={!!s.return_policy}
						onChange={(v) => update({ return_policy: v })}
						label={__('Publish a merchant return policy', 'ai-rank-suite')}
					/>
					{!!s.return_policy && (
						<div className="grid gap-3 sm:grid-cols-2 items-end">
							<Num label={__('Return window (days)', 'ai-rank-suite')} k="return_days" max={365} />
							<Switch checked={!!s.return_free} onChange={(v) => update({ return_free: v })} label={__('Returns are free', 'ai-rank-suite')} />
						</div>
					)}
				</div>

				<Switch
					checked={!!s.reviews}
					onChange={(v) => update({ reviews: v })}
					label={__('Include up to five customer reviews in the markup', 'ai-rank-suite')}
				/>
			</div>
		</Card>
	);
}

/* -------------------------------------------------------------------- Page */

export default function Schema({ tab = 'overview', onTab }) {
	const [data, setData] = useState(null);
	const [stats, setStats] = useState(null);
	const [error, setError] = useState(null);

	const loadStats = () =>
		Promise.allSettled([api.get('/schema/templates'), api.get('/schema/live')]).then(([tpl, live]) => {
			// Every field guarded: a template list that is not an array, or a
			// live sample without `issues`, must not take the screen down.
			const t = tpl.status === 'fulfilled' && tpl.value ? tpl.value : {};
			const l = live.status === 'fulfilled' && live.value ? live.value : {};
			const templates = Array.isArray(t.templates) ? t.templates : [];
			const samples = Array.isArray(l.samples) ? l.samples : [];
			setStats({
				types: t.types && typeof t.types === 'object' ? Object.keys(t.types).length : null,
				templates: templates.length,
				active: templates.filter((x) => x && x.enabled).length,
				emitted: Array.isArray(samples[0]?.types) ? samples[0].types.length : 0,
				issues: samples.reduce((a, x) => a + (Array.isArray(x?.issues) ? x.issues.length : 0), 0),
			});
		});

	useEffect(() => {
		api.get('/settings/schema').then(setData).catch((e) => setError(e.message));
		loadStats();
		// eslint-disable-next-line react-hooks/exhaustive-deps
	}, []);

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

	// The settings, under the name every tab below uses. This line was
	// missing: the verdict referenced `s` that only the tabs declared, and the
	// whole screen threw before it drew a pixel.
	const s = data.settings || {};

	// Schema is only worth anything if it names who you are; the organisation
	// is the root every other block hangs from.
	const named = !!(s.org_name && String(s.org_name).trim());
	const verdict = data.conflict && !s.force_output
		? {
				tone: 'neutral',
				text: sprintf(__('%s is already writing structured data, so this plugin steps back.', 'ai-rank-suite'), data.conflict),
				why: __('Two schema graphs on one page contradict each other. If you would rather this plugin owned it, force output below and switch it off in the other one.', 'ai-rank-suite'),
			}
		: !s.enabled
			? {
					tone: 'warning',
					text: __('Structured data is switched off — pages say nothing machine-readable about themselves.', 'ai-rank-suite'),
					why: __('Schema is how a page tells Google and an assistant what it is: an article, a product, a business. Without it they guess.', 'ai-rank-suite'),
				}
			: !named
				? {
						tone: 'warning',
						text: __('Schema is on but the organisation has no name.', 'ai-rank-suite'),
						why: __('Every block references the site\'s identity. An unnamed one is a graph with a hole at the root — fill in the identity first.', 'ai-rank-suite'),
						action: __('Open identity', 'ai-rank-suite'),
						onAction: () => onTab && onTab('identity'),
					}
				: {
						tone: 'good',
						text: sprintf(__('Structured data is live for %s.', 'ai-rank-suite'), s.org_name),
						why: __('The preview below is exactly what a crawler reads. Check it whenever the identity or the templates change.', 'ai-rank-suite'),
					};

	return (
		<div className="ars-page">
			<ScreenVerdict {...verdict} />
			{stats && (
				<MetricGrid
					columns={4}
					metrics={[
						{
							id: 'output',
							label: __('Output', 'ai-rank-suite'),
							hint: __('Structured data is how a machine knows a page is a recipe rather than an essay about one — the single biggest lever for rich results and for being quoted correctly by an assistant.', 'ai-rank-suite'),
							value: data.settings.enabled ? 'Live' : 'Off',
							accent: data.settings.enabled ? undefined : 'var(--ars-accent)',
							sub: data.conflict ? sprintf(__('%d also active', 'ai-rank-suite'), data.conflict) : __('on every page of the site', 'ai-rank-suite'),
						},
						{
							id: 'emitted',
							label: __('Types on your home page', 'ai-rank-suite'),
							hint: __('Counted from the connected @graph the home page actually emits — see it verbatim under Live structured data.', 'ai-rank-suite'),
							value: stats.emitted,
						},
						{
							id: 'templates',
							label: __('Templates', 'ai-rank-suite'),
							hint: __('Rules that add a richer type — Recipe, Event, Product — to a whole group of pages at once.', 'ai-rank-suite'),
							value: `${stats.active} / ${stats.templates}`,
							sub: stats.templates === 0 ? 'none created yet' : 'active rules',
						},
						{
							id: 'issues',
							label: __('Validation issues', 'ai-rank-suite'),
							hint: __('Counted across the live JSON-LD samples on this site.', 'ai-rank-suite'),
							value: stats.issues,
							accent: stats.issues > 0 ? 'var(--ars-accent)' : undefined,
							sub: stats.types ? sprintf(__('%d types available', 'ai-rank-suite'), stats.types) : undefined,
						},
					]}
				/>
			)}

			<Tabs
				tabs={[
					{ id: 'overview', label: __('Overview', 'ai-rank-suite') },
					{ id: 'identity', label: __('Identity', 'ai-rank-suite') },
					{ id: 'templates', label: __('Templates', 'ai-rank-suite') },
					{ id: 'library', label: __('Library', 'ai-rank-suite') },
					{ id: 'products', label: __('Products', 'ai-rank-suite') },
				]}
				active={tab}
				onChange={onTab}
			/>

			{tab === 'overview' && (
				<>
					<RichResults />
					<Overview data={data} setData={setData} />
				</>
			)}
			{tab === 'identity' && <Identity />}
			{tab === 'templates' && <Templates onSaved={loadStats} />}
			{tab === 'library' && <TypeLibrary />}
			{tab === 'products' && <Products />}
		</div>
	);
}
