import React, { useEffect, useMemo, useState } from 'react';
import { __, _n, sprintf } from '@wordpress/i18n';
import { api, env } from '../api';
import { Button, Notice, Spinner } from '../components/ui';
import { VendorIcon } from './Import';

/**
 * First-run setup, structured the way SEOPress structures it: a numbered
 * step rail on the left that shows the whole journey at once, and one
 * working card on the right. Every step writes a real setting; "Save and
 * continue" is a save, not a page turn.
 */

/* ------------------------------------------------------------------ */
/* Shared chrome                                                       */
/* ------------------------------------------------------------------ */

const IconCheckSmall = () => (
	<svg width="12" height="12" viewBox="0 0 24 24" fill="none" aria-hidden>
		<path d="M20 6 9 17l-5-5" stroke="currentColor" strokeWidth="3" strokeLinecap="round" strokeLinejoin="round" />
	</svg>
);

const IconChevronLeft = () => (
	<svg width="12" height="12" viewBox="0 0 24 24" fill="none" aria-hidden>
		<path d="M15 18l-6-6 6-6" stroke="currentColor" strokeWidth="2.4" strokeLinecap="round" strokeLinejoin="round" />
	</svg>
);

const IconArrowRight = () => (
	<svg width="14" height="14" viewBox="0 0 24 24" fill="none" aria-hidden>
		<path d="M5 12h14m-6-6 6 6-6 6" stroke="currentColor" strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round" />
	</svg>
);

const IconUpload = () => (
	<svg width="30" height="30" viewBox="0 0 24 24" fill="none" aria-hidden>
		<path d="M12 16V4m0 0 5 5m-5-5-5 5" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
		<path d="M4 20h16" stroke="currentColor" strokeWidth="2" strokeLinecap="round" />
	</svg>
);

const IconDone = () => (
	<svg width="17" height="17" viewBox="0 0 24 24" fill="none" aria-hidden>
		<path d="M20 6 9 17l-5-5" stroke="currentColor" strokeWidth="2.6" strokeLinecap="round" strokeLinejoin="round" />
	</svg>
);

/** One-colour marks for the social step — recognisable, not trademark art. */
const SOCIAL_MARKS = {
	facebook: (
		<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor" aria-hidden>
			<path d="M13.5 21v-7.2h2.4l.4-2.9h-2.8V9.1c0-.8.3-1.4 1.5-1.4h1.4V5.1c-.3 0-1.1-.1-2-.1-2.1 0-3.5 1.2-3.5 3.6v2.3H8.5v2.9h2.4V21h2.6Z" />
		</svg>
	),
	x: (
		<svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor" aria-hidden>
			<path d="M17.8 4h2.9l-6.4 7.3L21.8 21h-5.9l-4.6-6-5.3 6H3.2l6.9-7.8L2.8 4h6l4.2 5.5L17.8 4Zm-1 15.2h1.6L7.9 5.7H6.2l10.6 13.5Z" />
		</svg>
	),
	pinterest: (
		<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor" aria-hidden>
			<path d="M12 3a9 9 0 0 0-3.3 17.4c-.1-.7-.2-1.9 0-2.7l1.2-5s-.3-.6-.3-1.5c0-1.4.8-2.4 1.8-2.4.9 0 1.3.6 1.3 1.4 0 .9-.5 2.2-.8 3.4-.2 1 .5 1.8 1.5 1.8 1.8 0 3.1-1.9 3.1-4.6 0-2.4-1.7-4-4.2-4a4.4 4.4 0 0 0-4.6 4.4c0 .9.3 1.8.8 2.3l.1.4-.3 1.2c0 .2-.2.2-.4.1-1.2-.6-2-2.4-2-3.9 0-3.2 2.3-6.1 6.7-6.1 3.5 0 6.2 2.5 6.2 5.8 0 3.5-2.2 6.3-5.2 6.3-1 0-2-.5-2.3-1.1l-.6 2.4c-.2.9-.8 1.9-1.2 2.6A9 9 0 1 0 12 3Z" />
		</svg>
	),
	instagram: (
		<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.9" aria-hidden>
			<rect x="4" y="4" width="16" height="16" rx="4.5" />
			<circle cx="12" cy="12" r="3.6" />
			<circle cx="17" cy="7" r="1.15" fill="currentColor" stroke="none" />
		</svg>
	),
	youtube: (
		<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor" aria-hidden>
			<path d="M21.3 8s-.2-1.4-.8-2c-.7-.8-1.6-.8-2-.9C15.9 5 12 5 12 5s-3.9 0-6.5.2c-.4 0-1.3 0-2 .8-.6.6-.8 2-.8 2S2.5 9.6 2.5 11.3v1.5c0 1.6.2 3.3.2 3.3s.2 1.4.8 2c.7.8 1.7.7 2.1.8 1.6.2 6.4.2 6.4.2s3.9 0 6.5-.2c.4 0 1.3-.1 2-.9.6-.6.8-2 .8-2s.2-1.6.2-3.3v-1.5c0-1.6-.2-3.2-.2-3.2ZM10 14.6V9.4l5.2 2.6L10 14.6Z" />
		</svg>
	),
	linkedin: (
		<svg width="19" height="19" viewBox="0 0 24 24" fill="currentColor" aria-hidden>
			<path d="M6.3 8.6H3.5V21h2.8V8.6ZM4.9 3.5a1.7 1.7 0 1 0 0 3.4 1.7 1.7 0 0 0 0-3.4ZM20.5 14.1c0-3.2-1.7-4.7-4-4.7-1.8 0-2.6 1-3.1 1.7V8.6H10.6V21h2.8v-6.7c0-1.2.9-2.2 2.1-2.2s2.2.9 2.2 2.3V21h2.8v-6.9Z" />
		</svg>
	),
};

/** `[post]`-style code chip inside running text. */
const Code = ({ children }) => <span className="ars-wiz-code">{children}</span>;

/** SEOPress-style info callout: tinted panel with a solid left bar. */
function Callout({ tone = 'info', children }) {
	return <div className={`ars-wiz-callout${tone === 'warning' ? ' is-warning' : ''}${tone === 'success' ? ' is-success' : ''}`}>{children}</div>;
}

/** Small-caps field label, SEOPress-style. */
function FieldLabel({ children }) {
	return (
		<div
			style={{
				fontSize: 11.5,
				fontWeight: 700,
				letterSpacing: '0.07em',
				textTransform: 'uppercase',
				color: 'var(--ars-ink)',
				margin: '22px 0 8px',
			}}
		>
			{children}
		</div>
	);
}

function FieldHint({ children }) {
	return (
		<p style={{ margin: '7px 0 0', fontSize: 12.5, lineHeight: 1.55, color: 'var(--ars-ink-3)', maxWidth: '80ch' }}>{children}</p>
	);
}

/** One exclusion checkbox row: Exclude `[slug]` from search engine. */
function ExcludeRow({ checked, onChange, slug, label, sub, disabled = false, example }) {
	return (
		<div className="ars-wiz-check">
			<label>
				<input type="checkbox" checked={checked} disabled={disabled} onChange={(e) => onChange?.(e.target.checked)} />
				<span>
					{label[0]} <Code>[{slug}]</Code> {label[1]}
				</span>
			</label>
			{(sub || example) && (
				<div className="ars-wiz-check-sub">
					{sub && <div>{sub}</div>}
					{example && (
						<div>
							{__('Example:', 'ai-rank-suite')} {example}
						</div>
					)}
				</div>
			)}
		</div>
	);
}

/**
 * The card frame every sub-step renders in: title, lede, body, and the
 * Back / Skip / primary footer.
 */
function StepCard({ title, lede, children, footer }) {
	return (
		<div className="ars-wiz-card">
			<h1>{title}</h1>
			{lede && <p className="ars-wiz-lede">{lede}</p>}
			<div className="ars-wiz-body">{children}</div>
			{footer && <div className="ars-wiz-footer">{footer}</div>}
		</div>
	);
}

/* ------------------------------------------------------------------ */
/* The site-title template editor: token chips plus free text          */
/* ------------------------------------------------------------------ */

const TITLE_TOKENS = () => ({
	'%sitename%': __('Site title', 'ai-rank-suite'),
	'%sep%': __('Separator', 'ai-rank-suite'),
	'%tagline%': __('Tagline', 'ai-rank-suite'),
	'%currentyear%': __('Current year', 'ai-rank-suite'),
});

const partsFromTemplate = (template) =>
	String(template || '')
		.split(/(%[a-z_]+%)/)
		.map((chunk) => chunk.trim())
		.filter(Boolean)
		.map((chunk) => (/^%[a-z_]+%$/.test(chunk) ? { token: chunk } : { text: chunk }));

const templateFromParts = (parts) => parts.map((p) => (p.token ? p.token : p.text)).join(' ');

function TitleBuilder({ parts, onChange, resolve }) {
	const tokens = TITLE_TOKENS();
	const [draft, setDraft] = useState('');

	const commitDraft = () => {
		const text = draft.trim();
		if (text) onChange([...parts, { text }]);
		setDraft('');
	};

	return (
		<div>
			<div
				style={{
					display: 'flex',
					flexWrap: 'wrap',
					alignItems: 'center',
					gap: 7,
					border: '1px solid var(--ars-border)',
					borderRadius: 8,
					padding: '9px 12px',
					background: 'var(--ars-surface)',
					minHeight: 44,
					boxSizing: 'border-box',
				}}
			>
				{parts.map((p, i) => (
					<span
						key={i}
						style={
							p.token
								? {
										display: 'inline-flex',
										alignItems: 'center',
										gap: 6,
										background: 'var(--ars-brand-tonal)',
										color: 'var(--ars-brand-tonal-ink, var(--ars-brand))',
										borderRadius: 6,
										padding: '3.5px 9px',
										fontSize: 12.5,
										fontWeight: 600,
									}
								: { fontSize: 13, color: 'var(--ars-ink)' }
						}
					>
						{p.token ? tokens[p.token] || p.token : p.text}
						<button
							type="button"
							onClick={() => onChange(parts.filter((_, j) => j !== i))}
							aria-label={__('Remove', 'ai-rank-suite')}
							style={{
								border: 0,
								background: 'none',
								cursor: 'pointer',
								color: 'inherit',
								opacity: 0.65,
								padding: 0,
								marginLeft: p.token ? 0 : 4,
								lineHeight: 1,
								fontSize: 13,
							}}
						>
							×
						</button>
					</span>
				))}
				<input
					type="text"
					value={draft}
					onChange={(e) => setDraft(e.target.value)}
					onBlur={commitDraft}
					onKeyDown={(e) => {
						if (e.key === 'Enter') {
							e.preventDefault();
							commitDraft();
						}
					}}
					placeholder={parts.length ? '' : __('Type text or add a variable below…', 'ai-rank-suite')}
					style={{ flex: '1 1 120px', minWidth: 100, border: 0, outline: 'none', background: 'none', font: 'inherit', fontSize: 13, boxShadow: 'none', padding: 4 }}
				/>
				{parts.length > 0 && (
					<button
						type="button"
						onClick={() => onChange([])}
						aria-label={__('Clear', 'ai-rank-suite')}
						style={{ border: 0, background: 'none', cursor: 'pointer', color: 'var(--ars-ink-3)', fontSize: 15, padding: 2 }}
					>
						×
					</button>
				)}
			</div>
			<div style={{ margin: '8px 0 10px', fontSize: 12.5, color: 'var(--ars-ink-2)' }}>
				<strong>{__('Preview:', 'ai-rank-suite')}</strong>{' '}
				<em>{resolve(templateFromParts(parts)) || __('(empty — the default template applies)', 'ai-rank-suite')}</em>
			</div>
			<div style={{ display: 'flex', flexWrap: 'wrap', gap: 8 }}>
				{Object.entries(tokens).map(([token, label]) => {
					const used = parts.some((p) => p.token === token);
					return (
						<button
							key={token}
							type="button"
							className="ars-btn ars-btn-ghost"
							style={{ fontSize: 12.5, paddingTop: 5, paddingBottom: 5 }}
							onClick={() => !used && onChange([...parts, { token }])}
							disabled={used}
						>
							{used ? '✓' : '+'} {label}
						</button>
					);
				})}
			</div>
		</div>
	);
}

/* ------------------------------------------------------------------ */
/* The wizard                                                          */
/* ------------------------------------------------------------------ */

export default function Wizard({ onDone }) {
	/**
	 * The journey. Groups carry the rail number; subs carry the cards.
	 * Order here is the order of "Save and continue".
	 */
	const GROUPS = useMemo(
		() => [
			{
				id: 'welcome',
				label: __('Welcome', 'ai-rank-suite'),
				subs: [
					{ id: 'start', label: __('Get started', 'ai-rank-suite') },
					{ id: 'import-config', label: __('Import configuration', 'ai-rank-suite') },
					{ id: 'import-meta', label: __('Import metadata', 'ai-rank-suite') },
				],
			},
			{
				id: 'site',
				label: __('Your site', 'ai-rank-suite'),
				subs: [
					{ id: 'general', label: __('General', 'ai-rank-suite') },
					{ id: 'social', label: __('Social accounts', 'ai-rank-suite') },
				],
			},
			{
				id: 'indexing',
				label: __('Indexing', 'ai-rank-suite'),
				subs: [
					{ id: 'idx-types', label: __('Post Types', 'ai-rank-suite') },
					{ id: 'idx-archives', label: __('Archives', 'ai-rank-suite') },
					{ id: 'idx-taxonomies', label: __('Taxonomies', 'ai-rank-suite') },
				],
			},
			{
				id: 'google',
				label: __('Google', 'ai-rank-suite'),
				subs: [{ id: 'google-connect', label: __('Search Console & Analytics', 'ai-rank-suite') }],
			},
			{
				id: 'advanced',
				label: __('Advanced options', 'ai-rank-suite'),
				subs: [
					{ id: 'adv', label: __('Advanced', 'ai-rank-suite') },
					{ id: 'metabox', label: __('SEO Metabox', 'ai-rank-suite') },
				],
			},
			{
				id: 'ready',
				label: __('Ready!', 'ai-rank-suite'),
				subs: [],
			},
		],
		[]
	);

	// Flat list of positions, so next/back are one index step.
	const FLAT = useMemo(() => {
		const out = [];
		GROUPS.forEach((g, gi) => {
			if (!g.subs.length) {
				out.push({ gi, si: -1, group: g, sub: null });
				return;
			}
			g.subs.forEach((s, si) => out.push({ gi, si, group: g, sub: s }));
		});
		return out;
	}, [GROUPS]);

	const [pos, setPos] = useState(0);
	const [busy, setBusy] = useState(false);
	const [error, setError] = useState(null);
	const current = FLAT[pos];

	// What this run actually configured — the Ready! card reads from this,
	// never from "the user clicked through".
	const [log, setLog] = useState({});
	const note = (key, value) => setLog((l) => ({ ...l, [key]: value }));

	const goto = (i) => {
		setError(null);
		setPos(Math.max(0, Math.min(FLAT.length - 1, i)));
	};
	const next = () => goto(pos + 1);
	const back = () => goto(pos - 1);

	const guard = async (fn) => {
		setBusy(true);
		setError(null);
		try {
			await fn();
		} catch (e) {
			setError(e.message);
		} finally {
			setBusy(false);
		}
	};

	/* -------------------------------------------------------------- */
	/* Step state                                                      */
	/* -------------------------------------------------------------- */

	// Import configuration (settings JSON from another install).
	const [configFile, setConfigFile] = useState(null); // {name, payload}
	const [configDone, setConfigDone] = useState(null); // {applied, skipped}
	const [dragOver, setDragOver] = useState(false);

	// Import metadata (migrate from another SEO plugin).
	const [sources, setSources] = useState(null);
	const [sourceChoice, setSourceChoice] = useState(null);
	const [migrated, setMigrated] = useState(null); // {label, total, redirects}

	// Your site.
	const [seo, setSeo] = useState(null); // /settings/seo payload
	const [schema, setSchema] = useState(null); // /settings/schema payload
	const [socialCfg, setSocialCfg] = useState(null); // /settings/social payload
	const [titleParts, setTitleParts] = useState([]);
	const [altTitle, setAltTitle] = useState('');
	const [orgType, setOrgType] = useState('Organization');
	const [profiles, setProfiles] = useState({});
	const [extraAccounts, setExtraAccounts] = useState('');

	// Indexing.
	const [noindexTypes, setNoindexTypes] = useState([]);
	const [noindexTaxes, setNoindexTaxes] = useState([]);
	const [dateNoindex, setDateNoindex] = useState(true);
	const [authorNoindex, setAuthorNoindex] = useState(false);

	// Advanced.
	const [attachRedirect, setAttachRedirect] = useState(true);
	const [emptyTerms, setEmptyTerms] = useState(true);
	const [imageSeo, setImageSeo] = useState(null);
	const [autoAlt, setAutoAlt] = useState(true);
	const [editorCfg, setEditorCfg] = useState(null);
	const [mbEnabled, setMbEnabled] = useState(true);
	const [mbPosition, setMbPosition] = useState('normal');
	const [mbTypes, setMbTypes] = useState(null); // null = all

	// Google: one click connects Search Console and Analytics, and the
	// matching accounts are picked automatically after the sign-in.
	const [gsc, setGsc] = useState(null);
	const [gscPicked, setGscPicked] = useState(null);
	const [gscBusy, setGscBusy] = useState(false);

	// Ready!
	const [status, setStatus] = useState(null);

	useEffect(() => {
		api.get('/import/sources')
			.then((r) => {
				const list = r.sources || [];
				setSources(list);
				const detected = list.find((s) => s.active || s.posts > 0 || s.terms > 0);
				if (detected) setSourceChoice(detected.id);
			})
			.catch(() => setSources([]));

		api.get('/settings/seo')
			.then((r) => {
				setSeo(r);
				setTitleParts(partsFromTemplate(r.settings.home_title));
				setNoindexTypes((r.settings.noindex_types || []).slice());
				setNoindexTaxes((r.settings.noindex_taxonomies || []).slice());
				setDateNoindex(!!r.settings.date_noindex);
				setAuthorNoindex(!!r.settings.author_noindex);
				setAttachRedirect(!!r.settings.attachment_redirect);
				setEmptyTerms(!!r.settings.noindex_empty_terms);
			})
			.catch(() => setSeo({ settings: {}, post_types: [], taxonomies: [] }));

		api.get('/settings/schema')
			.then((r) => {
				setSchema(r);
				setOrgType(r.settings.org_type || 'Organization');
				setAltTitle(r.settings.identity?.alt_name || '');
			})
			.catch(() => setSchema(null));

		api.get('/settings/social')
			.then((r) => {
				setSocialCfg(r);
				setProfiles({ ...(r.settings.profiles || {}) });
			})
			.catch(() => setSocialCfg(null));

		api.get('/settings/image-seo')
			.then((r) => {
				setImageSeo(r);
				setAutoAlt(!!r.settings.auto_alt);
			})
			.catch(() => setImageSeo(null));

		api.get('/settings/editor')
			.then((r) => {
				setEditorCfg(r);
				setMbEnabled(!!r.settings.metabox_enabled);
				setMbPosition(r.settings.metabox_position || 'normal');
				setMbTypes((r.settings.metabox_types || []).length ? r.settings.metabox_types.slice() : null);
			})
			.catch(() => setEditorCfg(null));

		api.get('/setup/status')
			.then(setStatus)
			.catch(() => setStatus({}));

		api.get('/gsc/status')
			.then(setGsc)
			.catch(() => setGsc(null));
	}, []);

	const connectGoogle = async () => {
		setGscBusy(true);
		try {
			// The one-click path first; a build without the connect service
			// falls back to the Search Console screen and its manual setup.
			const r = await api.get('/gsc/signin-url');
			window.location.href = r.url;
		} catch (e) {
			window.location.hash = '/search-console';
		}
	};

	const autoPickGoogle = async () => {
		setGscBusy(true);
		try {
			const picked = await api.post('/gsc/auto-configure', {});
			setGscPicked(picked);
		} catch (e) {
			setGscPicked({ property: '', ga4_property: '', ga4_measurement: '' });
		}
		setGscBusy(false);
	};

	const resolveTemplate = (template) =>
		String(template || '')
			.replace(/%sitename%/g, env.siteName || '')
			.replace(/%sep%/g, seo?.settings?.separator || '–')
			.replace(/%tagline%/g, env.tagline || '')
			.replace(/%currentyear%/g, String(new Date().getFullYear()))
			.replace(/\s{2,}/g, ' ')
			.trim();

	/* -------------------------------------------------------------- */
	/* Actions                                                         */
	/* -------------------------------------------------------------- */

	const finish = (thenAi = false) =>
		guard(async () => {
			await api.post('/core/onboarding', { done: true });
			onDone();
			if (thenAi) window.location.hash = '/ai-wizard';
		});

	const readConfigFile = (file) => {
		setError(null);
		setConfigDone(null);
		const reader = new FileReader();
		reader.onload = () => {
			try {
				const payload = JSON.parse(String(reader.result));
				if (payload?.format !== 'ai-rank-suite/1') {
					setConfigFile(null);
					setError(__('This file is not an AI Rank Suite settings export.', 'ai-rank-suite'));
					return;
				}
				setConfigFile({ name: file.name, payload });
			} catch (e) {
				setConfigFile(null);
				setError(__('This file is not valid JSON.', 'ai-rank-suite'));
			}
		};
		reader.readAsText(file);
	};

	const runConfigImport = () =>
		guard(async () => {
			const r = await api.post('/settings/import', { payload: configFile.payload });
			setConfigDone(r);
			note('config', sprintf(_n('%d settings group applied from your export file', '%d settings groups applied from your export file', r.applied, 'ai-rank-suite'), r.applied));
		});

	const runMetaImport = () =>
		guard(async () => {
			// The same job path the Import screen drives (start + tick until
			// done): keyset paging, kept-counts and the undo journal — the
			// wizard was the last caller of the legacy synchronous /import/run
			// loop (audit: legacy import path). The tick loop is bounded so a
			// server that stops making progress ends the wizard, not hangs it.
			let s = await api.post('/import/start', {
				source: sourceChoice,
				mode: 'fill',
				parts: ['posts', 'terms', 'redirects'],
			});
			for (let i = 0; i < 600 && s && s.state === 'running'; i++) {
				s = await api.post('/import/tick', {});
			}
			const c = (s && s.counts) || {};
			const imported = (c.posts ?? 0) + (c.terms ?? 0);
			const redirects = c.redirects ?? 0;
			const label = (sources || []).find((s2) => s2.id === sourceChoice)?.label || sourceChoice;
			setMigrated({ label, total: imported, redirects });
			if (imported > 0) {
				note('meta', sprintf(_n('%1$d page migrated from %2$s', '%1$d pages migrated from %2$s', imported, 'ai-rank-suite'), imported, label));
			}
		});

	const saveGeneral = () =>
		guard(async () => {
			const template = templateFromParts(titleParts);
			await api.put('/settings/seo', { home_title: template || '%sitename% %sep% %tagline%' });
			if (schema) {
				await api.put('/settings/schema', {
					org_type: orgType,
					identity: { ...(schema.settings.identity || {}), alt_name: altTitle.trim() },
				});
			}
			note('general', __('Home title and site identity are configured', 'ai-rank-suite'));
			next();
		});

	const saveSocial = () =>
		guard(async () => {
			const clean = {};
			Object.entries(profiles).forEach(([k, v]) => {
				const url = String(v || '').trim();
				if (url) clean[k] = url;
			});

			// The free-text lines: recognised hosts become profiles, everything
			// else lands in the Organization's sameAs — nothing typed is lost.
			const rest = [];
			extraAccounts
				.split('\n')
				.map((l) => l.trim())
				.filter(Boolean)
				.forEach((url) => {
					if (/tiktok\.com/i.test(url) && !clean.tiktok) clean.tiktok = url;
					else if (/wikipedia\.org/i.test(url) && !clean.wikipedia) clean.wikipedia = url;
					else rest.push(url);
				});

			const payload = { profiles: clean };
			// The X handle feeds twitter:site; the Facebook page feeds
			// article:publisher — both derived, neither asked twice.
			const xMatch = String(clean.x || '').match(/(?:x|twitter)\.com\/@?([A-Za-z0-9_]{2,})/);
			if (xMatch) payload.tw_site = '@' + xMatch[1];
			if (clean.facebook) payload.fb_page = clean.facebook;
			await api.put('/settings/social', payload);

			if (rest.length && schema) {
				const existing = (schema.settings.identity?.same_as || []).map(String);
				await api.put('/settings/schema', {
					identity: { ...(schema.settings.identity || {}), same_as: [...new Set([...existing, ...rest])] },
				});
			}
			const count = Object.keys(clean).length + rest.length;
			if (count > 0) {
				note('social', sprintf(_n('%d social profile linked for the Knowledge Graph', '%d social profiles linked for the Knowledge Graph', count, 'ai-rank-suite'), count));
			}
			next();
		});

	const saveIndexTypes = () =>
		guard(async () => {
			await api.put('/settings/seo', { noindex_types: noindexTypes });
			next();
		});

	const saveIndexArchives = () =>
		guard(async () => {
			await api.put('/settings/seo', { date_noindex: dateNoindex, author_noindex: authorNoindex });
			next();
		});

	const saveIndexTaxonomies = () =>
		guard(async () => {
			await api.put('/settings/seo', { noindex_taxonomies: noindexTaxes });
			const parts = [];
			if (noindexTypes.length) parts.push(sprintf(_n('%d post type', '%d post types', noindexTypes.length, 'ai-rank-suite'), noindexTypes.length));
			if (noindexTaxes.length) parts.push(sprintf(_n('%d taxonomy', '%d taxonomies', noindexTaxes.length, 'ai-rank-suite'), noindexTaxes.length));
			note(
				'indexing',
				parts.length
					? sprintf(__('Indexing rules set — %s excluded from search results', 'ai-rank-suite'), parts.join(', '))
					: __('Indexing rules reviewed for post types, archives and taxonomies', 'ai-rank-suite')
			);
			next();
		});

	const saveAdvanced = () =>
		guard(async () => {
			await api.put('/settings/seo', { attachment_redirect: attachRedirect, noindex_empty_terms: emptyTerms });
			if (imageSeo) {
				await api.put('/settings/image-seo', { auto_alt: autoAlt });
			}
			note('advanced', __('Advanced options set — attachments, image alt texts, empty terms', 'ai-rank-suite'));
			next();
		});

	const saveMetabox = () =>
		guard(async () => {
			if (editorCfg) {
				await api.put('/settings/editor', {
					metabox_enabled: mbEnabled,
					metabox_position: mbPosition,
					metabox_types: mbTypes === null ? [] : mbTypes,
				});
				note('metabox', mbEnabled ? __('The SEO metabox is active in the editor', 'ai-rank-suite') : __('The SEO metabox is switched off', 'ai-rank-suite'));
			}
			next();
		});

	/* -------------------------------------------------------------- */
	/* Rail                                                            */
	/* -------------------------------------------------------------- */

	const rail = (
		<nav className="ars-wiz-rail" aria-label={__('Setup steps', 'ai-rank-suite')}>
			<h2 className="ars-wiz-rail-title">{current.group.label}</h2>
			<div className="ars-wiz-steps">
				{GROUPS.map((g, gi) => {
					const firstPos = FLAT.findIndex((f) => f.gi === gi);
					const isActive = current.gi === gi;
					const isDone = current.gi > gi;
					return (
						<React.Fragment key={g.id}>
							<button
								type="button"
								className={`ars-wiz-step${isActive ? ' is-active' : ''}${isDone ? ' is-done' : ''}`}
								onClick={() => (gi <= current.gi ? goto(firstPos) : null)}
								disabled={gi > current.gi}
							>
								<span className="ars-wiz-num">{isDone ? <IconCheckSmall /> : gi + 1}</span>
								<span>{g.label}</span>
							</button>
							{isActive && g.subs.length > 0 && (
								<div className="ars-wiz-subs">
									{g.subs.map((s, si) => {
										const p = FLAT.findIndex((f) => f.gi === gi && f.si === si);
										return (
											<button
												type="button"
												key={s.id}
												className={`ars-wiz-sub${current.si === si ? ' is-active' : ''}`}
												onClick={() => (p <= pos ? goto(p) : null)}
											>
												{s.label}
											</button>
										);
									})}
								</div>
							)}
						</React.Fragment>
					);
				})}
			</div>
		</nav>
	);

	/* -------------------------------------------------------------- */
	/* Cards                                                           */
	/* -------------------------------------------------------------- */

	const backBtn = (
		<button type="button" className="ars-wiz-back" onClick={back}>
			<IconChevronLeft />
			{__('Back', 'ai-rank-suite')}
		</button>
	);

	const saveContinue = (onClick) => (
		<Button onClick={onClick} busy={busy}>
			{__('Save and continue', 'ai-rank-suite')}
		</Button>
	);

	let card = null;
	const stepId = current.sub ? current.sub.id : current.group.id;
	const home = String(env.homeUrl || '').replace(/\/$/, '');

	if (stepId === 'start') {
		card = (
			<StepCard
				title={__('Welcome to AI Rank Suite', 'ai-rank-suite')}
				lede={__('This wizard takes about 3 minutes. By the end, your site is set up for AI search — ChatGPT, Claude, Gemini, Perplexity — and classic Google SEO alike.', 'ai-rank-suite')}
				footer={
					<>
						<button type="button" className="ars-wiz-skip-link" style={{ marginRight: 'auto', paddingLeft: 0 }} onClick={() => finish(false)}>
							{__('Skip for now', 'ai-rank-suite')}
						</button>
						<Button onClick={next}>
							{__('Start setup', 'ai-rank-suite')}
							&nbsp;
							<IconArrowRight />
						</Button>
					</>
				}
			>
				<div className="ars-wiz-hero">
					<div className="ars-wiz-hero-card">
						<div className="ars-wiz-quote-mark" aria-hidden>
							<svg width="30" height="24" viewBox="0 0 30 24" fill="currentColor">
								<path d="M0 24V13.7C0 5.9 4.6 1.2 12.2 0l1.4 4.4c-4.3 1-6.4 3.2-6.6 6.9H13V24H0Zm17 0V13.7C17 5.9 21.6 1.2 29.2 0l1.4 4.4c-4.3 1-6.4 3.2-6.6 6.9H30V24H17Z" transform="scale(0.95)" />
							</svg>
						</div>
						<p className="ars-wiz-quote">
							{__('AI assistants answer questions directly instead of showing links. The sites they quote are the sites they can read, understand and verify — that is exactly what this setup makes yours.', 'ai-rank-suite')}
						</p>
						<div className="ars-wiz-quote-by">{__('Why this wizard exists', 'ai-rank-suite')}</div>
						<div className="ars-wiz-quote-role">{__('Generative engine optimization, done once and done right', 'ai-rank-suite')}</div>
					</div>
					<div className="ars-wiz-hero-card ars-wiz-counts">
						{[
							[
								'77',
								__('structured data types, emitted automatically', 'ai-rank-suite'),
								<svg key="i" width="20" height="20" viewBox="0 0 24 24" fill="none"><path d="m8 8-4 4 4 4m8-8 4 4-4 4M14 5l-4 14" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" /></svg>,
							],
							[
								'31',
								__('AI crawlers under your control', 'ai-rank-suite'),
								<svg key="i" width="20" height="20" viewBox="0 0 24 24" fill="none"><rect x="5" y="8" width="14" height="11" rx="2" stroke="currentColor" strokeWidth="2" /><path d="M12 8V4m-7 9H3m18 0h-2M9 13.5h.01M15 13.5h.01" stroke="currentColor" strokeWidth="2" strokeLinecap="round" /></svg>,
							],
							[
								'6',
								__('AI engines tracked for mentions of you', 'ai-rank-suite'),
								<svg key="i" width="20" height="20" viewBox="0 0 24 24" fill="none"><path d="M4 19c2-6 5-9 8-9m0 0c3 0 6 3 8 9M12 10V4m0 0-2.5 2.5M12 4l2.5 2.5" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" /></svg>,
							],
						].map(([n, label, icon]) => (
							<div key={label} className="ars-wiz-count">
								<span className="ars-wiz-count-icon">{icon}</span>
								<span>
									<div className="ars-wiz-count-num">{n}</div>
									<div className="ars-wiz-count-label">{label}</div>
								</span>
							</div>
						))}
					</div>
				</div>
				<div className="ars-wiz-strip">
					<div className="ars-wiz-strip-text">
						<div className="ars-wiz-strip-title">
							<svg width="15" height="15" viewBox="0 0 24 24" fill="none" aria-hidden><path d="M12 2l2.4 7.2H22l-6 4.6 2.3 7.2-6.3-4.5-6.3 4.5L8 13.8 2 9.2h7.6L12 2z" fill="currentColor" /></svg>
							{__('Connect an AI engine after setup', 'ai-rank-suite')}
						</div>
						<div className="ars-wiz-strip-sub">
							{__('Bring an OpenAI, Anthropic or Google key and the suite writes meta descriptions, answers FAQs and simulates AI answers about your site. The last step offers it.', 'ai-rank-suite')}
						</div>
					</div>
				</div>
			</StepCard>
		);
	}

	if (stepId === 'import-config') {
		card = (
			<StepCard
				title={__('Import an existing configuration', 'ai-rank-suite')}
				lede={__('Already configured AI Rank Suite on another site? Upload the JSON file you exported from it to apply the exact same settings here, then skip the rest of the setup.', 'ai-rank-suite')}
				footer={
					<>
						{backBtn}
						<Button variant="ghost" onClick={next}>{__('Skip this step', 'ai-rank-suite')}</Button>
						<Button onClick={runConfigImport} busy={busy} disabled={!configFile || !!configDone}>
							{__('Import configuration', 'ai-rank-suite')}
							&nbsp;
							<IconArrowRight />
						</Button>
					</>
				}
			>
				<div
					className={`ars-wiz-drop${dragOver ? ' is-over' : ''}`}
					onDragOver={(e) => {
						e.preventDefault();
						setDragOver(true);
					}}
					onDragLeave={() => setDragOver(false)}
					onDrop={(e) => {
						e.preventDefault();
						setDragOver(false);
						const f = e.dataTransfer.files?.[0];
						if (f) readConfigFile(f);
					}}
				>
					<span className="ars-wiz-drop-icon"><IconUpload /></span>
					<div className="ars-wiz-drop-title">{__('Drag and drop your configuration file here', 'ai-rank-suite')}</div>
					<div className="ars-wiz-drop-sub">{__('JSON file only — the one Settings → Export writes.', 'ai-rank-suite')}</div>
					<div className="ars-wiz-drop-sub">{__('or select it from your computer:', 'ai-rank-suite')}</div>
					<input
						type="file"
						accept=".json,application/json"
						onChange={(e) => {
							const f = e.target.files?.[0];
							if (f) readConfigFile(f);
						}}
					/>
					{configFile && !configDone && (
						<div className="ars-wiz-drop-file">
							{sprintf(__('Ready to import: %s', 'ai-rank-suite'), configFile.name)}
						</div>
					)}
				</div>
				{configDone ? (
					<Notice tone="good">
						{sprintf(
							_n('%d settings group applied. Continue to review the rest, or skip ahead — your configuration is live.', '%d settings groups applied. Continue to review the rest, or skip ahead — your configuration is live.', configDone.applied, 'ai-rank-suite'),
							configDone.applied
						)}
					</Notice>
				) : (
					<Callout tone="warning">
						<p>
							{__('Importing replaces your current settings (titles, llms.txt, sitemap, schema, advanced options…). Post and term metadata are not affected. API keys and licenses never travel in export files.', 'ai-rank-suite')}
						</p>
					</Callout>
				)}
			</StepCard>
		);
	}

	if (stepId === 'import-meta') {
		const detected = (s) => s.active || s.posts > 0 || s.terms > 0 || s.redirects > 0 || s.settings;
		card = (
			<StepCard
				title={__('Migrate your SEO metadata to AI Rank Suite', 'ai-rank-suite')}
				lede={__('Select your previous SEO plugin to bring its titles, descriptions, canonicals, robots rules and redirects along. Nothing on the other plugin is changed or deleted.', 'ai-rank-suite')}
				footer={
					<>
						{backBtn}
						<Button variant="ghost" onClick={next}>{__('Skip import for now', 'ai-rank-suite')}</Button>
						<Button onClick={runMetaImport} busy={busy} disabled={!sourceChoice || !!migrated}>
							{__('Migrate now', 'ai-rank-suite')}
							&nbsp;
							<IconArrowRight />
						</Button>
					</>
				}
			>
				{sources === null && (
					<div style={{ display: 'grid', placeItems: 'center', minHeight: 200 }}>
						<Spinner />
					</div>
				)}
				{sources !== null && (
					<div className="ars-wiz-sources">
						{sources.map((s) => (
							<button
								key={s.id}
								type="button"
								className={`ars-wiz-source${sourceChoice === s.id ? ' is-selected' : ''}`}
								onClick={() => detected(s) && setSourceChoice(s.id)}
								disabled={!detected(s)}
								aria-pressed={sourceChoice === s.id}
							>
								<VendorIcon id={s.id} label={s.label} />
								<span>
									<span className="ars-wiz-source-name">{s.label}</span>
									<span className="ars-wiz-source-sub" style={{ display: 'block' }}>
										{detected(s)
											? [
													s.posts > 0 ? sprintf(_n('%d page', '%d pages', s.posts, 'ai-rank-suite'), s.posts) : null,
													s.redirects > 0 ? sprintf(_n('%d redirect', '%d redirects', s.redirects, 'ai-rank-suite'), s.redirects) : null,
													s.active ? __('active', 'ai-rank-suite') : null,
												]
													.filter(Boolean)
													.join(' · ') || __('settings found', 'ai-rank-suite')
											: __('not detected', 'ai-rank-suite')}
									</span>
								</span>
							</button>
						))}
					</div>
				)}
				{sources !== null && !sources.some(detected) && (
					<Callout>
						<p>{__('No other SEO plugin left data on this site — nothing to migrate. Skip ahead.', 'ai-rank-suite')}</p>
					</Callout>
				)}
				{migrated && (
					<Notice tone={migrated.total > 0 ? 'good' : 'info'}>
						{migrated.total > 0
							? (migrated.redirects > 0
									? sprintf(_n('Migrated %1$d page and %2$d redirects from %3$s.', 'Migrated %1$d pages and %2$d redirects from %3$s.', migrated.total, 'ai-rank-suite'), migrated.total, migrated.redirects, migrated.label)
									: sprintf(_n('Migrated %1$d page from %2$s.', 'Migrated %1$d pages from %2$s.', migrated.total, 'ai-rank-suite'), migrated.total, migrated.label))
							: __('Nothing left to migrate — every page that had data already carries it here.', 'ai-rank-suite')}
					</Notice>
				)}
				{migrated && migrated.total > 0 && (
					<Callout>
						<p>{__('Once everything looks right, deactivate the old plugin so only one of them writes your SEO tags.', 'ai-rank-suite')}</p>
					</Callout>
				)}
			</StepCard>
		);
	}

	if (stepId === 'general') {
		const typeGroups = schema?.types && typeof schema.types === 'object' ? schema.types : null;
		card = (
			<StepCard
				title={__('Tell us more about your site', 'ai-rank-suite')}
				lede={__('These fields set your home page title and your Google Knowledge Graph — how search engines and AI assistants name your site.', 'ai-rank-suite')}
				footer={
					<>
						{backBtn}
						{saveContinue(saveGeneral)}
					</>
				}
			>
				<FieldLabel>{__('Site title', 'ai-rank-suite')}</FieldLabel>
				<TitleBuilder parts={titleParts} onChange={setTitleParts} resolve={resolveTemplate} />
				<FieldHint>
					{__('This template builds your home page title tag. Variables resolve live — the preview above is exactly what Google shows.', 'ai-rank-suite')}
				</FieldHint>

				<FieldLabel>{__('Alternative site title', 'ai-rank-suite')}</FieldLabel>
				<input
					type="text"
					value={altTitle}
					onChange={(e) => setAltTitle(e.target.value)}
					placeholder={__('e.g. a common acronym or short name', 'ai-rank-suite')}
					style={{ width: '100%', boxSizing: 'border-box' }}
				/>
				<FieldHint>
					{__('The alternate name of the website — for example a commonly recognized acronym or shorter name. It becomes the alternateName in your structured data.', 'ai-rank-suite')}
				</FieldHint>

				<FieldLabel>{__('Person or organization', 'ai-rank-suite')}</FieldLabel>
				<select value={orgType} onChange={(e) => setOrgType(e.target.value)} style={{ width: '100%', boxSizing: 'border-box' }}>
					{typeGroups
						? Object.entries(typeGroups).map(([group, entries]) => (
								<optgroup key={group} label={group}>
									{Object.entries(entries).map(([value, label]) => (
										<option key={value} value={value}>
											{label}
										</option>
									))}
								</optgroup>
							))
						: (
							<>
								<option value="Organization">{__('Organization', 'ai-rank-suite')}</option>
								<option value="Person">{__('Person', 'ai-rank-suite')}</option>
							</>
						)}
				</select>
				<FieldHint>
					{__('Choose "Person / sole trader" for a personal site, or the closest organization type for a company — it decides whether your Knowledge Graph entity is a Person or an Organization.', 'ai-rank-suite')}
				</FieldHint>
			</StepCard>
		);
	}

	if (stepId === 'social') {
		const FIELDS = [
			['facebook', 'Facebook', 'https://facebook.com/your-page'],
			['x', 'X', 'https://x.com/your-handle'],
			['pinterest', 'Pinterest', 'https://pinterest.com/your-profile'],
			['instagram', 'Instagram', 'https://instagram.com/your-profile'],
			['youtube', 'YouTube', 'https://youtube.com/@your-channel'],
			['linkedin', 'LinkedIn', 'https://linkedin.com/company/your-page'],
		];
		card = (
			<StepCard
				title={__('Link your site to your social networks', 'ai-rank-suite')}
				lede={__('These profiles feed the sameAs of your structured data — how search engines and AI assistants confirm which accounts are really yours.', 'ai-rank-suite')}
				footer={
					<>
						{backBtn}
						{saveContinue(saveSocial)}
					</>
				}
			>
				{FIELDS.map(([id, label, placeholder]) => (
					<div key={id}>
						<FieldLabel>{label}</FieldLabel>
						<div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
							<span style={{ color: 'var(--ars-brand)', display: 'grid', placeItems: 'center', width: 24 }} aria-hidden>
								{SOCIAL_MARKS[id]}
							</span>
							<input
								type="url"
								value={profiles[id] || ''}
								onChange={(e) => setProfiles({ ...profiles, [id]: e.target.value })}
								placeholder={placeholder}
								style={{ flex: 1 }}
							/>
						</div>
					</div>
				))}
				<FieldLabel>{__('Additional accounts', 'ai-rank-suite')}</FieldLabel>
				<textarea
					rows={3}
					value={extraAccounts}
					onChange={(e) => setExtraAccounts(e.target.value)}
					placeholder={__('Enter 1 URL per line (e.g. https://example.com/my-profile)', 'ai-rank-suite')}
					style={{ width: '100%', boxSizing: 'border-box' }}
				/>
				<FieldHint>{__('TikTok, Wikipedia, a directory entry, a GitHub org — anything that verifiably belongs to you strengthens the entity.', 'ai-rank-suite')}</FieldHint>
			</StepCard>
		);
	}

	if (stepId === 'idx-types') {
		card = (
			<StepCard
				title={__('Which content types should be excluded from search engine results?', 'ai-rank-suite')}
				lede={__('Check the boxes below to disable indexing for selected content types. They will not appear in search engine results (noindex).', 'ai-rank-suite')}
				footer={
					<>
						{backBtn}
						{saveContinue(saveIndexTypes)}
					</>
				}
			>
				<Callout>
					<p>{__('Custom post types are a content type in WordPress. Post and Page are the default post types.', 'ai-rank-suite')}</p>
					<p>{__('You can create your own type of content like "product" or "business": these are custom post types.', 'ai-rank-suite')}</p>
				</Callout>
				<div className="ars-wiz-checks">
					{(seo?.post_types || []).map((t) => (
						<ExcludeRow
							key={t.name}
							slug={t.name}
							label={[__('Exclude', 'ai-rank-suite'), __('from search engine', 'ai-rank-suite')]}
							checked={noindexTypes.includes(t.name)}
							onChange={(on) =>
								setNoindexTypes(on ? [...noindexTypes, t.name] : noindexTypes.filter((x) => x !== t.name))
							}
						/>
					))}
				</div>
			</StepCard>
		);
	}

	if (stepId === 'idx-archives') {
		card = (
			<StepCard
				title={__('Which archives should be excluded from search engine results?', 'ai-rank-suite')}
				lede={__('Check the boxes below to disable indexing for selected content types. They will not appear in search engine results (noindex).', 'ai-rank-suite')}
				footer={
					<>
						{backBtn}
						{saveContinue(saveIndexArchives)}
					</>
				}
			>
				<Callout>
					<p>{__('Archive pages are automatically generated by WordPress. They group specific content such as your latest articles, a product category or your content by author or date.', 'ai-rank-suite')}</p>
				</Callout>
				<div className="ars-wiz-checks">
					<ExcludeRow
						slug="date"
						label={[__('Exclude', 'ai-rank-suite'), __('archives from search engine', 'ai-rank-suite')]}
						checked={dateNoindex}
						onChange={setDateNoindex}
						example={`${home}/2025/01/01/`}
					/>
					<ExcludeRow
						slug="search"
						label={[__('Exclude', 'ai-rank-suite'), __('results from search engine', 'ai-rank-suite')]}
						checked
						disabled
						sub={__('Always on — internal search results are one query away from infinite duplicate pages.', 'ai-rank-suite')}
						example={`${home}/?s=keyword`}
					/>
					<ExcludeRow
						slug="author"
						label={[__('Exclude', 'ai-rank-suite'), __('archives from search engine', 'ai-rank-suite')]}
						checked={authorNoindex}
						onChange={setAuthorNoindex}
						sub={__('You only have one author on your site? Check this option to avoid duplicate content.', 'ai-rank-suite')}
						example={`${home}/author/jane-doe/`}
					/>
				</div>
			</StepCard>
		);
	}

	if (stepId === 'idx-taxonomies') {
		card = (
			<StepCard
				title={__('Which taxonomies should be excluded from search engine results?', 'ai-rank-suite')}
				lede={__('Check the boxes below to disable indexing for selected content types. They will not appear in search engine results (noindex).', 'ai-rank-suite')}
				footer={
					<>
						{backBtn}
						{saveContinue(saveIndexTaxonomies)}
					</>
				}
			>
				<Callout>
					<p>{__('Taxonomies are the method of classifying content and data in WordPress. When you use a taxonomy you’re grouping similar things together. Categories and Tags are the default taxonomies.', 'ai-rank-suite')}</p>
					<p>{__('You can add your own taxonomies like "product categories": these are called custom taxonomies.', 'ai-rank-suite')}</p>
				</Callout>
				<div className="ars-wiz-checks">
					{(seo?.taxonomies || []).map((t) => (
						<ExcludeRow
							key={t.name}
							slug={t.name}
							label={[__('Exclude', 'ai-rank-suite'), __('from search engine', 'ai-rank-suite')]}
							checked={noindexTaxes.includes(t.name)}
							onChange={(on) => setNoindexTaxes(on ? [...noindexTaxes, t.name] : noindexTaxes.filter((x) => x !== t.name))}
							sub={t.name === 'post_tag' ? __('We recommend excluding tags from search results, as they often create duplicate content.', 'ai-rank-suite') : null}
						/>
					))}
				</div>
			</StepCard>
		);
	}

	if (stepId === 'google-connect') {
		const connected = !!gsc?.connected;
		card = (
			<StepCard
				title={__('Connect Google — one click, no setup', 'ai-rank-suite')}
				lede={__('Google already measures your site: Search Console knows the queries you appear for, Analytics knows your visitors. One sign-in brings both into this dashboard.', 'ai-rank-suite')}
				footer={
					<>
						{backBtn}
						{connected
							? <Button onClick={next}>{__('Continue', 'ai-rank-suite')}</Button>
							: <Button variant="ghost" onClick={next}>{__('Skip — connect later', 'ai-rank-suite')}</Button>}
					</>
				}
			>
				{!connected && (
					<>
						<Callout>
							<p>{__('In plain terms: you click the button, a Google window opens, you choose the Google account you use for this website and press "Allow". That is the whole setup — we find the matching Search Console property and Analytics account for you.', 'ai-rank-suite')}</p>
							<p style={{ marginTop: 8 }}>{__('We can only ever read your statistics — never change anything in your Google account. You can disconnect with one click at any time.', 'ai-rank-suite')}</p>
						</Callout>
						<div style={{ marginTop: 16 }}>
							<Button onClick={connectGoogle} busy={gscBusy}>{__('Sign in with Google', 'ai-rank-suite')}</Button>
						</div>
						<p className="t-caption" style={{ marginTop: 10 }}>{__('No Google account for this site yet? Skip this step — everything else works without it.', 'ai-rank-suite')}</p>
					</>
				)}
				{connected && (
					<>
						<Callout tone="success">
							<p>{__('Google is connected.', 'ai-rank-suite')}</p>
						</Callout>
						{!gscPicked && (
							<div style={{ marginTop: 14 }}>
								<Button onClick={autoPickGoogle} busy={gscBusy}>{__('Find my accounts automatically', 'ai-rank-suite')}</Button>
							</div>
						)}
						{gscPicked && (
							<div style={{ marginTop: 14 }}>
								<p className="m-0">
									{gscPicked.property
										? sprintf(__('Search Console: %s', 'ai-rank-suite'), gscPicked.property)
										: __('Search Console: no property for this domain was found in that account — pick one on the Search Console screen.', 'ai-rank-suite')}
								</p>
								<p className="m-0" style={{ marginTop: 6 }}>
									{gscPicked.ga4_property
										? sprintf(__('Analytics: property %1$s (measurement id %2$s)', 'ai-rank-suite'), gscPicked.ga4_property, gscPicked.ga4_measurement || '—')
										: __('Analytics: no GA4 property points at this site — you can pick one on the Analytics screen, or keep working without it.', 'ai-rank-suite')}
								</p>
							</div>
						)}
					</>
				)}
			</StepCard>
		);
	}

	if (stepId === 'adv') {
		card = (
			<StepCard
				title={__('Advanced SEO options', 'ai-rank-suite')}
				lede={__('Optimize your site’s SEO with these advanced settings.', 'ai-rank-suite')}
				footer={
					<>
						{backBtn}
						{saveContinue(saveAdvanced)}
					</>
				}
			>
				<div className="ars-wiz-checks">
					<div className="ars-wiz-check">
						<label>
							<input type="checkbox" checked={attachRedirect} onChange={(e) => setAttachRedirect(e.target.checked)} />
							<span>{__('Redirect attachment pages to their parent or file URL', 'ai-rank-suite')}</span>
						</label>
						<div className="ars-wiz-check-sub">
							{__('An attachment page is a near-empty page wrapping one image — thin content in the index. This sends visitors and crawlers to the real content instead.', 'ai-rank-suite')}
						</div>
					</div>
					<div className="ars-wiz-check">
						<label>
							<input type="checkbox" checked={autoAlt} onChange={(e) => setAutoAlt(e.target.checked)} disabled={!imageSeo} />
							<span>{__('Automatically set alt text on images that have none', 'ai-rank-suite')}</span>
						</label>
						<div className="ars-wiz-check-sub">
							{__('Images without alt text are invisible to image search and to screen readers. This fills the gap from the file name and title when a page renders.', 'ai-rank-suite')}
						</div>
					</div>
					<div className="ars-wiz-check">
						<label>
							<input type="checkbox" checked={emptyTerms} onChange={(e) => setEmptyTerms(e.target.checked)} />
							<span>{__('Keep empty categories and tags out of the index', 'ai-rank-suite')}</span>
						</label>
						<div className="ars-wiz-check-sub">
							{__('A term with no posts is an empty page. Indexing it is asking search engines to rate a blank.', 'ai-rank-suite')}
						</div>
					</div>
				</div>
			</StepCard>
		);
	}

	if (stepId === 'metabox') {
		const allTypes = editorCfg?.post_types || [];
		const effective = mbTypes === null ? allTypes.map((t) => t.name) : mbTypes;
		card = (
			<StepCard
				title={__('The SEO metabox in your editor', 'ai-rank-suite')}
				lede={__('The metabox is the SEO panel inside the post editor: title, description, score and checks, right where you write. Decide where it lives.', 'ai-rank-suite')}
				footer={
					<>
						{backBtn}
						{saveContinue(saveMetabox)}
					</>
				}
			>
				<div className="ars-wiz-checks">
					<div className="ars-wiz-check">
						<label>
							<input type="checkbox" checked={mbEnabled} onChange={(e) => setMbEnabled(e.target.checked)} disabled={!editorCfg} />
							<span>{__('Show the SEO metabox in the editor', 'ai-rank-suite')}</span>
						</label>
						<div className="ars-wiz-check-sub">
							{__('The block editor also gets the AI Rank sidebar either way — the metabox is the full panel below the content.', 'ai-rank-suite')}
						</div>
					</div>
				</div>
				{mbEnabled && (
					<>
						<FieldLabel>{__('Position', 'ai-rank-suite')}</FieldLabel>
						<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(220px, 1fr))', gap: 12, maxWidth: 560 }}>
							{[
								['normal', __('Below the editor', 'ai-rank-suite'), __('Full width — room for the score, checks and previews.', 'ai-rank-suite')],
								['side', __('Side column', 'ai-rank-suite'), __('Compact, always in view next to the content.', 'ai-rank-suite')],
							].map(([value, label, sub]) => (
								<button
									key={value}
									type="button"
									className={`ars-wiz-source${mbPosition === value ? ' is-selected' : ''}`}
									style={{ minHeight: 0, alignItems: 'flex-start', flexDirection: 'column', gap: 3 }}
									onClick={() => setMbPosition(value)}
									aria-pressed={mbPosition === value}
								>
									<span className="ars-wiz-source-name">{label}</span>
									<span className="ars-wiz-source-sub">{sub}</span>
								</button>
							))}
						</div>
						<FieldLabel>{__('Post types', 'ai-rank-suite')}</FieldLabel>
						<div className="ars-wiz-checks">
							{allTypes.map((t) => (
								<div key={t.name} className="ars-wiz-check" style={{ padding: '5px 4px' }}>
									<label>
										<input
											type="checkbox"
											checked={effective.includes(t.name)}
											onChange={(e) => {
												const on = e.target.checked;
												const nextList = on ? [...new Set([...effective, t.name])] : effective.filter((x) => x !== t.name);
												setMbTypes(nextList.length === allTypes.length ? null : nextList);
											}}
										/>
										<span>
											{t.label} <Code>[{t.name}]</Code>
										</span>
									</label>
								</div>
							))}
						</div>
						<FieldHint>{__('All types checked means new post types are covered automatically.', 'ai-rank-suite')}</FieldHint>
					</>
				)}
			</StepCard>
		);
	}

	if (stepId === 'ready') {
		const rows = [
			[true, __('llms.txt tells AI crawlers what your site is about', 'ai-rank-suite')],
			[true, __('XML sitemaps are published for Google and Bing', 'ai-rank-suite')],
			[true, __('Structured data is emitted for every page — 77 types available', 'ai-rank-suite')],
			[!!status?.indexnow, __('IndexNow pushes new content to search engines within minutes', 'ai-rank-suite')],
			[!!status?.ai, __('An AI engine is connected for writing and answer simulation', 'ai-rank-suite')],
			...Object.values(log).map((v) => [true, v]),
		];
		card = (
			<StepCard
				title={__('Congratulations! Your site is set up for AI search.', 'ai-rank-suite')}
				lede={__('AI Rank Suite has applied its key optimizations. Here is what is now working for you — and the one step that unlocks the rest.', 'ai-rank-suite')}
				footer={
					<>
						{backBtn}
						<Button variant="ghost" onClick={() => finish(false)} busy={busy}>
							{__('Go to dashboard', 'ai-rank-suite')}
						</Button>
					</>
				}
			>
				<div className="ars-wiz-rosette" aria-hidden>
					<span>
						{__('Setup complete', 'ai-rank-suite')}
						<br />
						{__('AI ready', 'ai-rank-suite')}
					</span>
				</div>
				<div className="ars-wiz-donelist">
					{rows.map(([ok, label], i) => (
						<div key={i} className={`ars-wiz-done${ok ? '' : ' is-off'}`}>
							<span className="ars-wiz-done-check">{ok ? <IconDone /> : '·'}</span>
							<span>{label}</span>
						</div>
					))}
				</div>
				{status?.other_seo_plugin && (
					<Callout tone="warning">
						<p>
							{sprintf(__('%s is still active and also writes SEO tags. Deactivate it once the migration looks right — two plugins writing the same tags is the one setup Google punishes.', 'ai-rank-suite'), status.other_seo_plugin)}
						</p>
					</Callout>
				)}
				<div className="ars-wiz-strip">
					<div className="ars-wiz-strip-text">
						<div className="ars-wiz-strip-title">
							<svg width="15" height="15" viewBox="0 0 24 24" fill="none" aria-hidden><path d="M12 2l2.4 7.2H22l-6 4.6 2.3 7.2-6.3-4.5-6.3 4.5L8 13.8 2 9.2h7.6L12 2z" fill="currentColor" /></svg>
							{__('Next step: connect an AI engine', 'ai-rank-suite')}
						</div>
						<div className="ars-wiz-strip-sub">
							{__('One API key unlocks writing, the autopilot and answer simulations — the AI setup reads a description of your business and proposes the rest.', 'ai-rank-suite')}
						</div>
					</div>
					<Button onClick={() => finish(true)} busy={busy}>
						{__('Start AI setup', 'ai-rank-suite')}
						&nbsp;
						<IconArrowRight />
					</Button>
				</div>
			</StepCard>
		);
	}

	return (
		<div className="ars-wiz" style={{ fontFamily: "'Google Sans Text', Roboto, sans-serif" }}>
			{rail}
			<div>
				{card}
				{error && (
					<div style={{ maxWidth: 1160, marginTop: 14 }}>
						<Notice tone="serious">{error}</Notice>
					</div>
				)}
			</div>
		</div>
	);
}
