import React, { useEffect, useMemo, useState } from 'react';
import { __, _n, sprintf } from '@wordpress/i18n';
import { api } from '../api';
import { readTheme, writeTheme } from '../theme.js';
import { Card, Switch, Badge, Button, Notice, MetricGrid, PageState, Field } from '../components/ui';
import Explain from '../components/Explain';
import { IconShield, IconLayers, IconGauge, IconExternal, IconSliders, IconDownload } from '../components/icons';

const MODULE_GROUPS = [
	{ id: 'ai', label: __('AI search', 'ai-rank-suite'), hint: __('Everything that makes assistants find, read and recommend you', 'ai-rank-suite') },
	{ id: 'search', label: __('Google search', 'ai-rank-suite'), hint: __('Classic SEO output, indexing and performance data', 'ai-rank-suite') },
	{ id: 'content', label: __('Content', 'ai-rank-suite'), hint: __('Per-page analysis, bulk work, links and redirects', 'ai-rank-suite') },
	{ id: 'system', label: __('System', 'ai-rank-suite'), hint: __('Housekeeping, integrations and developer tooling', 'ai-rank-suite') },
];

/**
 * Module manager. The promise this screen makes is that switching something
 * off is safe and reversible — so it says so, shows what is actually running,
 * and never silently discards a setting.
 */
export function Modules() {
	const [data, setData] = useState(null);
	const [status, setStatus] = useState(null);
	const [query, setQuery] = useState('');
	const [saved, setSaved] = useState(false);
	const [busy, setBusy] = useState(false);
	const [error, setError] = useState(null);
	const [dirty, setDirty] = useState(false);
	// What the server last confirmed, and whether the person has read the
	// warning for a module that must be switched on knowingly. The server
	// refuses the transition off → on without the acknowledgement, so the
	// screen has to know the saved state to tell a transition from a state.
	const [savedOn, setSavedOn] = useState({});
	const [acknowledged, setAcknowledged] = useState(false);

	const remember = (payload) => {
		setData(payload);
		setSavedOn(Object.fromEntries((payload?.modules || []).map((m) => [m.id, !!m.enabled])));
		setAcknowledged(false);
	};

	useEffect(() => {
		api.get('/settings/general').then(remember).catch((e) => setError(e.message));
		// Optional: the module manager is still fully usable without it.
		api.get('/dev/status').then(setStatus).catch(() => {});
	}, []);

	const optional = useMemo(() => (data?.modules || []).filter((m) => m.optional), [data]);
	const filtered = useMemo(() => {
		const q = query.trim().toLowerCase();
		if (!q) return optional;
		return optional.filter((m) => m.label.toLowerCase().includes(q) || m.desc.toLowerCase().includes(q));
	}, [optional, query]);

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

	const flash = () => {
		setSaved(true);
		setTimeout(() => setSaved(false), 2200);
	};

	const setModules = (fn) => {
		setData({ ...data, modules: data.modules.map(fn) });
		setDirty(true);
	};

	const toggleModule = (id, enabled) => setModules((m) => (m.id === id ? { ...m, enabled } : m));
	const setAll = (enabled) => setModules((m) => (m.optional ? { ...m, enabled } : m));

	// Modules that execute code and are about to go from off to on. Each one
	// needs the acknowledgement flag in the request, or the server refuses.
	const needsAck = (data?.modules || []).filter((m) => m.acknowledge && m.enabled && !savedOn[m.id]);
	const ackMissing = needsAck.length > 0 && !acknowledged;

	const save = async () => {
		if (ackMissing) return;
		setBusy(true);
		setError(null);
		try {
			const disabled = data.modules.filter((m) => !m.enabled).map((m) => m.id);
			const body = { disabled };
			needsAck.forEach((m) => {
				body[`acknowledge_${m.id}`] = true;
			});
			remember(await api.put('/settings/modules', body));
			setDirty(false);
			flash();
		} catch (e) {
			setError(e.message);
		} finally {
			setBusy(false);
		}
	};

	const enabled = optional.filter((m) => m.enabled).length;
	const quarantine = Object.keys(status?.modules?.quarantine || {});

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

			<MetricGrid
				columns={4}
				metrics={[
					{
						id: 'running',
						label: __('Modules running', 'ai-rank-suite'),
						hint: __('Modules loaded on every request right now.', 'ai-rank-suite'),
						value: status?.modules?.active?.length ?? '—',
					},
					{
						id: 'optional',
						label: __('Optional modules on', 'ai-rank-suite'),
						hint: __('A disabled module loads no code, registers no hooks and adds zero overhead.', 'ai-rank-suite'),
						value: `${enabled} / ${optional.length}`,
					},
					{
						id: 'quarantined',
						label: __('Quarantined', 'ai-rank-suite'),
						hint: __('A module that stops with an error is isolated so the rest of the plugin keeps working.', 'ai-rank-suite'),
						value: quarantine.length,
						accent: quarantine.length > 0 ? 'var(--ars-c-red-ink)' : undefined,
						sub: quarantine.length > 0 ? quarantine.join(', ') : __('no module has failed', 'ai-rank-suite'),
					},
					{
						id: 'version',
						label: __('Plugin version', 'ai-rank-suite'),
						hint: __('Settings survive every update — a snapshot is taken automatically before each one.', 'ai-rank-suite'),
						value: status?.environment?.plugin ? `v${status.environment.plugin}` : '—',
						sub: status?.environment?.db_schema ? sprintf(__('database schema %s', 'ai-rank-suite'), status.environment.db_schema) : undefined,
					},
				]}
			/>

			{quarantine.length > 0 && (
				<Notice tone="serious">
					{quarantine.join(', ')} stopped with an error and was isolated so the rest of the plugin keeps working. Open{' '}
					<button
						type="button"
						className="border-0 bg-transparent cursor-pointer p-0 font-semibold"
						style={{ color: 'var(--ars-brand)' }}
						onClick={() => {
							window.location.hash = '/developer';
						}}
					>{__('Developer → Status', 'ai-rank-suite')}</button>{' '}
					to see the error and clear the quarantine.
				</Notice>
			)}

			{/* Rank Math keeps its wizard permanently available as a tab, and it
			    is right: a setup wizard that can only ever run once is a wizard
			    nobody can use after the day they installed the plugin. */}
			<Card
				icon={<IconGauge size={16} />}
				title={__('Setup wizard', 'ai-rank-suite')}
				subtitle={__('Re-run the guided setup any time — it never overwrites a field you have already filled in', 'ai-rank-suite')}
				action={
					<Button variant="ghost" onClick={() => { window.location.hash = '/wizard'; }}>{__('Open the wizard', 'ai-rank-suite')}</Button>
				}
			>
				<p className="m-0 text-[13px]" style={{ color: 'var(--ars-ink-2)' }}>
					{__('Six questions — what the site is, which industry and market it serves, which AI engines matter to you — and it configures the schema type, business defaults, prompts and llms.txt accordingly. Useful after a rebrand, after adding a second market, or simply to see what it would suggest.', 'ai-rank-suite')}
				</p>
			</Card>

			<WhiteLabel data={data} onSaved={setData} />

			<Card
				icon={<IconSliders size={16} />} title={__('Modules', 'ai-rank-suite')}
				subtitle={__('Switch off what you don’t use — a disabled module loads no code, registers no hooks and adds zero overhead', 'ai-rank-suite')}
				action={
					<div className="flex items-center gap-2">
						{saved && <Badge tone="good">✓ {__('Saved', 'ai-rank-suite')}</Badge>}
						{dirty && (
							<Button onClick={save} busy={busy} disabled={ackMissing}>{__('Apply changes', 'ai-rank-suite')}</Button>
						)}
					</div>
				}
			>
				<div className="grid gap-5">
					<div className="flex flex-wrap items-center gap-3">
						<input
							type="text"
							placeholder={__('Search modules…', 'ai-rank-suite')}
							value={query}
							onChange={(e) => setQuery(e.target.value)}
							style={{ maxWidth: 280 }}
							aria-label={__('Search modules', 'ai-rank-suite')}
						/>
						<div className="flex items-center gap-2">
							<Button variant="ghost" onClick={() => setAll(true)}>{__('Enable all', 'ai-rank-suite')}</Button>
							<Button variant="ghost" onClick={() => setAll(false)}>{__('Disable all', 'ai-rank-suite')}</Button>
						</div>
					</div>

					{dirty && <Notice tone="info">{__('Nothing is switched until you press “Apply changes”.', 'ai-rank-suite')}</Notice>}

					{/* The one module that executes code is off on a new install
					    and comes on only through this step. The server refuses the
					    request without the flag, so this is not decoration: without
					    the box ticked, "Apply changes" does nothing. */}
					{needsAck.length > 0 && (
						<Notice tone="warning">
							<div className="grid gap-2">
								<div>
									<strong>{needsAck.map((m) => m.label).join(', ')}</strong>{' '}
									{__('runs PHP that is typed into this admin. Anyone with the “edit plugins” capability can then execute code on this server. A snippet with a mistake is switched off automatically after one failed request; a snippet written to do harm is not.', 'ai-rank-suite')}
								</div>
								<label className="flex items-start gap-2 cursor-pointer" style={{ fontWeight: 500 }}>
									<input
										type="checkbox"
										checked={acknowledged}
										onChange={(e) => setAcknowledged(e.target.checked)}
										style={{ marginTop: 2 }}
									/>
									<span>{__('I understand that this module runs code on the site, and I want to switch it on.', 'ai-rank-suite')}</span>
								</label>
							</div>
						</Notice>
					)}

					{MODULE_GROUPS.map((group) => {
						const modules = filtered.filter((m) => m.group === group.id);
						if (!modules.length) return null;
						const on = modules.filter((m) => m.enabled).length;
						return (
							<div key={group.id} className="grid gap-2">
								<div className="flex items-baseline justify-between gap-3">
									<div className="text-[11px] font-semibold uppercase tracking-wider" style={{ color: 'var(--ars-ink-3)' }}>
										{group.label}
										<span className="ml-2 normal-case tracking-normal font-normal">
											{on} of {modules.length} on
										</span>
									</div>
									<div className="text-[11.5px]" style={{ color: 'var(--ars-ink-3)' }}>
										{group.hint}
									</div>
								</div>
								<div className="ars-cardgrid" style={{ ["--min"]: "320px" }}>
									{modules.map((m) => (
										// A card that is "on" should look solid and a card that is
										// "off" should look switched off. Filling the enabled one
										// with grey did the opposite: the active state was the
										// duller of the two, and forty grey rectangles read as a
										// wireframe rather than a product.
										<div
											key={m.id}
											className="flex items-start justify-between gap-3 ars-r-md px-4 py-3 relative overflow-hidden"
											style={{
												background: 'var(--ars-surface)',
												border: '1px solid var(--ars-border)',
												borderStyle: m.enabled ? 'solid' : 'dashed',
												opacity: m.enabled ? 1 : 0.62,
												boxShadow: m.enabled ? 'var(--ars-e1)' : 'none',
												transition: 'opacity 160ms ease, box-shadow 160ms ease',
											}}
										>
											<div className="min-w-0">
												<div className="font-medium text-[13px] flex items-center gap-2">
													{m.label}
													{m.acknowledge && <Badge tone="warning">{__('runs code', 'ai-rank-suite')}</Badge>}
													{quarantine.includes(m.id) && <Badge tone="serious">failed</Badge>}
												</div>
												<div className="text-[11.5px] mt-0.5" style={{ color: 'var(--ars-ink-3)' }}>
													{m.desc}
												</div>
											</div>
											<Switch checked={m.enabled} onChange={(v) => toggleModule(m.id, v)} />
										</div>
									))}
								</div>
							</div>
						);
					})}

					{filtered.length === 0 && (
						<p className="m-0 text-[13px]" style={{ color: 'var(--ars-ink-3)' }}>
							No module matches “{query}”.
						</p>
					)}

					<p className="m-0 text-[12px]" style={{ color: 'var(--ars-ink-3)' }}>
						{__('Settings of a disabled module are kept untouched — switch it back on and everything is exactly as you left it. Essential modules (core, settings, licensing) are not listed here: turning them off would take this screen down with them.', 'ai-rank-suite')}
					</p>
				</div>
			</Card>
		</div>
	);
}

/**
 * Everything about the customer's data: the snapshot taken before each update,
 * portable configuration, and what happens on uninstall.
 */
export function DataPrivacy() {
	const [data, setData] = useState(null);
	const [backup, setBackup] = useState(null);
	const [busy, setBusy] = useState(false);
	const [saved, setSaved] = useState(false);
	const [restored, setRestored] = useState(false);
	const [importing, setImporting] = useState(false);
	const [imported, setImported] = useState(null);
	const [error, setError] = useState(null);

	const load = () => {
		api.get('/settings/general').then(setData).catch((e) => setError(e.message));
		api.get('/settings/backup').then(setBackup).catch(() => {});
	};
	useEffect(load, []);

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

	const flash = () => {
		setSaved(true);
		setTimeout(() => setSaved(false), 2200);
	};

	const toggleKeepData = async (value) => {
		setError(null);
		try {
			setData(await api.put('/settings/general', { keep_data_on_uninstall: value }));
			flash();
		} catch (e) {
			setError(e.message);
		}
	};

	const restore = async () => {
		setBusy(true);
		setError(null);
		try {
			await api.post('/settings/backup', {});
			setRestored(true);
			setTimeout(() => setRestored(false), 3000);
		} catch (e) {
			setError(e.message);
		} finally {
			setBusy(false);
		}
	};

	const exportConfig = async () => {
		setError(null);
		try {
			const payload = await api.get('/settings/export');
			const blob = new Blob([JSON.stringify(payload, null, 2)], { type: 'application/json' });
			const url = URL.createObjectURL(blob);
			const a = document.createElement('a');
			a.href = url;
			a.download = `ai-rank-suite-config-${new Date().toISOString().slice(0, 10)}.json`;
			document.body.appendChild(a);
			a.click();
			a.remove();
			URL.revokeObjectURL(url);
		} catch (e) {
			setError(e.message);
		}
	};

	const importConfig = async (file) => {
		if (!file) return;
		setImporting(true);
		setError(null);
		setImported(null);
		try {
			const text = await file.text();
			const parsed = JSON.parse(text);
			// The endpoint takes the whole export under `payload` and validates
			// its format marker before touching a single option.
			const r = await api.post('/settings/import', { payload: parsed });
			setImported(r);
			load();
		} catch (e) {
			setError(e instanceof SyntaxError ? __('That file is not valid JSON — pick a config exported from this plugin.', 'ai-rank-suite') : e.message);
		} finally {
			setImporting(false);
		}
	};

	return (
		<div className="ars-page">
			{error && <Notice tone="serious">{error}</Notice>}
			{saved && <Notice tone="good">{__('Saved.', 'ai-rank-suite')}</Notice>}

			<Card icon={<IconShield size={16} />} title={__('Updates & data safety', 'ai-rank-suite')} subtitle={__('Your configuration survives every update — that is a promise, not a hope', 'ai-rank-suite')}>
				<div className="grid gap-3">
					<div
						className="flex items-start justify-between gap-3 ars-r-lg px-4 py-3"
						style={{ background: 'var(--ars-surface-2)' }}
					>
						<div>
							<div className="font-medium text-[13px]">{__('Automatic snapshot', 'ai-rank-suite')}</div>
							<div className="text-[12px] mt-0.5" style={{ color: 'var(--ars-ink-2)' }}>
								{backup?.exists
									? sprintf(__('Taken before the update to v%1$s, on %2$s. Every setting of every module is included.', 'ai-rank-suite'), backup.from, new Date(backup.time * 1000).toLocaleString())
									: __('A snapshot of all settings is taken automatically before every update. None exists yet — the first one appears with your next update.', 'ai-rank-suite')}
							</div>
						</div>
						{backup?.exists && (
							<Button variant="ghost" onClick={restore} busy={busy}>
								{restored ? <>✓ {__('Restored', 'ai-rank-suite')}</> : __('Restore', 'ai-rank-suite')}
							</Button>
						)}
					</div>
					{restored && <Notice tone="good">{__('Settings restored from the backup snapshot.', 'ai-rank-suite')}</Notice>}
				</div>
			</Card>

			<Card
				icon={<IconDownload size={16} />} title={__('Portable configuration', 'ai-rank-suite')}
				subtitle={__('Move a finished setup to another site, or keep a copy outside the database', 'ai-rank-suite')}
			>
				<div className="grid gap-3">
					<div className="flex flex-wrap items-center gap-2">
						<Button variant="ghost" onClick={exportConfig}>{__('Export configuration', 'ai-rank-suite')}</Button>
						<label className="ars-btn ars-btn-tonal" style={{ cursor: 'pointer' }}>
							{importing ? __('Importing…', 'ai-rank-suite') : __('Import configuration', 'ai-rank-suite')}
							<input
								type="file"
								accept="application/json,.json"
								style={{ display: 'none', width: 0 }}
								onChange={(e) => importConfig(e.target.files?.[0])}
							/>
						</label>
					</div>
					{imported && (
						<Notice tone={imported.applied > 0 ? 'good' : 'warning'}>
							{imported.skipped > 0
								? sprintf(__('Applied %1$d settings groups, skipped %2$d that this version does not recognise. Reload the page to see them everywhere.', 'ai-rank-suite'), imported.applied, imported.skipped)
								: sprintf(_n('Applied %d settings group. Reload the page to see them everywhere.', 'Applied %d settings groups. Reload the page to see them everywhere.', imported.applied, 'ai-rank-suite'), imported.applied)}
						</Notice>
					)}
					<p className="m-0 text-[12.5px]" style={{ color: 'var(--ars-ink-2)' }}>
						{__('The export contains settings only — never API keys, license keys or content. That is deliberate: a config file often ends up in a repository or a shared drive, and a secret in it would outlive the site it came from.', 'ai-rank-suite')}
					</p>
				</div>
			</Card>

			<Retention data={data} onSaved={setData} />

			<Card title={__('Uninstall behavior', 'ai-rank-suite')} subtitle={__('What happens if you ever remove the plugin', 'ai-rank-suite')}>
				<div className="grid gap-3">
					<Switch
						checked={!!data.keep_data_on_uninstall}
						onChange={toggleKeepData}
						label={__('Keep all data when the plugin is uninstalled', 'ai-rank-suite')}
					/>
					<p className="m-0 text-[12.5px]" style={{ color: 'var(--ars-ink-2)' }}>
						{__('Recommended. Turn this off only if you want every setting, score, redirect and keyword removed from the database on uninstall — that cannot be undone.', 'ai-rank-suite')}
					</p>
					{!data.keep_data_on_uninstall && (
						<Notice tone="warning">{__('Deletion on uninstall is enabled: removing the plugin will permanently erase all its data.', 'ai-rank-suite')}</Notice>
					)}
				</div>
			</Card>
		</div>
	);
}

export default Modules;

/* -------------------------------------------------------------------------
   Environment

   Before anyone buys an SEO plugin they want three answers: will it fight the
   plugin I already run, does it understand my page builder, and does it work
   in my second language. Those answers lived in four different modules and in
   no screen at all, which meant the honest answer was a support ticket. One
   read-only page, everything it knows about this site.
   ------------------------------------------------------------------------- */

const HEALTH_TONE = { good: 'green', warn: 'yellow', bad: 'red' };
const HEALTH_MARK = { good: '✓', warn: '!', bad: '×' };

function Row({ active, label, children, note }) {
	return (
		<div
			className="flex items-start gap-3 px-3 py-2.5 ars-r-sm"
			style={{
				border: '1px solid var(--ars-border)',
				background: active ? 'var(--ars-surface-2)' : 'transparent',
				opacity: active ? 1 : 0.66,
			}}
		>
			<span
				aria-hidden
				className="shrink-0 mt-0.5"
				style={{ color: active ? 'var(--ars-c-green-ink)' : 'var(--ars-ink-3)', width: 14 }}
			>
				{active ? '✓' : '○'}
			</span>
			<div className="min-w-0 flex-1">
				<div className="font-medium text-[13px] flex items-center gap-2 flex-wrap">
					{label}
					{children}
				</div>
				{note && (
					<div className="text-[12px] mt-0.5" style={{ color: 'var(--ars-ink-2)' }}>
						{note}
					</div>
				)}
			</div>
		</div>
	);
}

export function Environment() {
	const [data, setData] = useState(null);
	const [error, setError] = useState(null);
	const [showAll, setShowAll] = useState(false);

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

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

	const problems = (data.health || []).filter((h) => h.status !== 'good');
	const builders = (data.builders || []).filter((b) => b.active);
	const integrations = data.integrations || [];
	const activeIntegrations = integrations.filter((i) => i.active);
	const shown = showAll ? integrations : activeIntegrations;
	const ml = data.multilingual || {};
	const services = data.services || [];

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

			<MetricGrid
				columns={4}
				metrics={[
					{
						id: 'health',
						label: __('Site health', 'ai-rank-suite'),
						hint: __('What this server lets the plugin do — the table below says what to change if something is holding it back.', 'ai-rank-suite'),
						value: problems.length ? sprintf(__('%d to look at', 'ai-rank-suite'), problems.length) : __('All clear', 'ai-rank-suite'),
						accent: problems.length ? 'var(--ars-accent)' : undefined,
						sub: sprintf(_n('%d check', '%d checks', (data.health || []).length, 'ai-rank-suite'), (data.health || []).length),
					},
					{
						id: 'seo-plugins',
						label: __('Other SEO plugins', 'ai-rank-suite'),
						hint: __('Two SEO plugins normally mean a broken head. Here every surface has exactly one owner — the Compatibility tab shows each decision.', 'ai-rank-suite'),
						value: data.seo_plugins?.length || 0,
						sub: data.seo_plugins?.length ? data.seo_plugins.map((p) => p.label).join(', ') : __('running solo', 'ai-rank-suite'),
					},
					{
						id: 'languages',
						label: __('Languages', 'ai-rank-suite'),
						hint: __('Translation plugins are read directly — no mapping to set up.', 'ai-rank-suite'),
						value: ml.multilingual ? ml.languages.length : 1,
						sub: ml.multilingual ? ml.label : __('single language', 'ai-rank-suite'),
					},
					{
						id: 'integrations',
						label: __('Integrations active', 'ai-rank-suite'),
						hint: __('Plugins read automatically to make the structured data richer — nothing to configure.', 'ai-rank-suite'),
						value: `${activeIntegrations.length} / ${integrations.length}`,
					},
				]}
			/>

			<Card
				icon={<IconGauge size={16} />} title={__('Site health', 'ai-rank-suite')}
				subtitle={__('What this server lets the plugin do — and what to change if something is holding it back', 'ai-rank-suite')}
			>
				<div className="ars-table-wrap">
					<table className="ars-table">
						<thead>
							<tr>
								<th style={{ width: 30 }} />
								<th style={{ width: 210 }}>{__('Check', 'ai-rank-suite')}</th>
								<th className="is-mid">{__('Value', 'ai-rank-suite')}</th>
								<th>{__('What it means', 'ai-rank-suite')}</th>
							</tr>
						</thead>
						<tbody>
							{(data.health || []).map((h) => (
								<tr key={h.id}>
									<td
										style={{
											width: 30,
											color: `var(--ars-c-${HEALTH_TONE[h.status]}-ink)`,
											fontWeight: 700,
										}}
									>
										{HEALTH_MARK[h.status]}
									</td>
									<td style={{ fontWeight: h.status === 'good' ? 400 : 600 }}>{h.label}</td>
									<td className="is-mid ars-mono" style={{ fontSize: 12 }}>
										{h.value}
									</td>
									<td style={{ color: 'var(--ars-ink-2)' }}>{h.note}</td>
								</tr>
							))}
						</tbody>
					</table>
				</div>
			</Card>

			<div className="grid gap-4 md:grid-cols-2">
				<Card
					icon={<IconShield size={16} />} title={__('Other SEO plugins', 'ai-rank-suite')}
					subtitle={__('Detected on this site, with the tags each one normally writes', 'ai-rank-suite')}
				>
					{(data.importable || []).length > 0 && (
						<Notice tone="info">
							{data.importable
								.map(
									(i) =>
										sprintf(_n('%1$s: %2$s page', '%1$s: %2$s pages', i.posts, 'ai-rank-suite'), i.label, i.posts.toLocaleString()) +
										(i.redirects ? ' ' + sprintf(__('and %s redirects', 'ai-rank-suite'), i.redirects.toLocaleString()) : '')
								)
								.join(' · ')}{' '}
							can be brought across without retyping anything.{' '}
							<button
								type="button"
								className="border-0 bg-transparent cursor-pointer p-0 font-semibold"
								style={{ color: 'var(--ars-brand)' }}
								onClick={() => {
									window.location.hash = '/import';
								}}
							>{__('Open the importer →', 'ai-rank-suite')}</button>
						</Notice>
					)}
					{(data.seo_plugins || []).length === 0 ? (
						<p className="m-0 mt-2 text-[13px]" style={{ color: 'var(--ars-ink-2)' }}>
							{__('None found — AI Rank Suite owns the whole head, which is the simplest setup there is. If you install one later, the Compatibility tab decides tag by tag who writes what, so the two never double up.', 'ai-rank-suite')}
						</p>
					) : (
						<div className="grid gap-2">
							{data.seo_plugins.map((p) => (
								<Row
									key={p.key}
									active
									label={p.label}
									note={sprintf(__('Normally writes: %s. The Compatibility tab decides who wins each one.', 'ai-rank-suite'), p.surfaces.join(', '))}
								>
									{p.version && <span className="ars-chip-tag">v{p.version}</span>}
								</Row>
							))}
							<p className="m-0 text-[12.5px]" style={{ color: 'var(--ars-ink-2)' }}>
								{__('Running two SEO plugins is normally a broken head — two titles, two canonicals, a doubled schema graph. Here it is a supported configuration: every surface has exactly one owner, and you can see the decision on the Compatibility tab.', 'ai-rank-suite')}
							</p>
						</div>
					)}
				</Card>

				<Card icon={<IconExternal size={16} />} title={__('Languages', 'ai-rank-suite')} subtitle={__('Translation plugins are read directly — no mapping to set up', 'ai-rank-suite')}>
					<div className="grid gap-2">
						<Row
							active={!!ml.multilingual}
							label={ml.multilingual ? ml.label : __('Single language', 'ai-rank-suite')}
							note={
								ml.multilingual
									? ml.perPost
										? __('Each translation is its own post, so titles, descriptions, schema and llms.txt are written per language and hreflang is emitted automatically.', 'ai-rank-suite')
										: __('Translation happens per request, so one source record is served in every language and hreflang follows the plugin’s own URLs.', 'ai-rank-suite')
									: __('Install WPML, Polylang, TranslatePress or Weglot and this page picks it up on the next load — there is nothing to connect.', 'ai-rank-suite')
							}
						/>
						{ml.multilingual && (
							<div className="flex flex-wrap gap-1.5">
								{(ml.languages || []).map((l) => (
									<span key={l.code || l} className="ars-chip-tag">
										{(l.label || l.name || l.code || l).toString()}
										{l.code && l.code === ml.current ? ` · ${__('current', 'ai-rank-suite')}` : ''}
									</span>
								))}
							</div>
						)}
					</div>
				</Card>
			</div>

			<Card icon={<IconLayers size={16} />} title={__('Page builders', 'ai-rank-suite')} subtitle={__('What the analyser reads when the content is not in the post body', 'ai-rank-suite')}>
				<div className="ars-cardgrid" style={{ '--min': '300px' }}>
					{(data.builders || []).map((b) => (
						<Row key={b.key} active={b.active} label={b.label} note={b.active ? b.note : __('Not installed.', 'ai-rank-suite')} />
					))}
				</div>
				<Explain title={__('Why this matters on a builder site', 'ai-rank-suite')}>
					<p>
						{__('A page built with Elementor or Divi stores almost nothing in the post body — the text a visitor reads lives in meta fields the builder renders later. An SEO plugin that reads the post body sees an empty page, scores it badly, and writes a meta description from nothing. Every check here runs against the rendered output instead, which is why the word count on a builder page matches what you actually see.', 'ai-rank-suite')}
					</p>
				</Explain>
			</Card>

			<Card
				icon={<IconExternal size={16} />} title={__('Connected services', 'ai-rank-suite')}
				subtitle={__('Where real data comes from — all optional, most of them free', 'ai-rank-suite')}
			>
				<div className="grid gap-2">
					{services.map((svc) => (
						<Row
							key={svc.key}
							active={svc.connected}
							label={svc.label}
							note={svc.provides}
						>
							{svc.free && <span className="ars-chip-tag">free</span>}
							{!svc.connected && (
								<button
									type="button"
									className="border-0 bg-transparent cursor-pointer p-0 text-[12px] font-semibold"
									style={{ color: 'var(--ars-brand)' }}
									onClick={() => {
										window.location.hash = '/' + svc.route;
									}}
								>{__('Connect →', 'ai-rank-suite')}</button>
							)}
						</Row>
					))}
				</div>
			</Card>

			<Card
				icon={<IconLayers size={16} />} title={__('Content integrations', 'ai-rank-suite')}
				subtitle={__('Plugins we read from automatically to make the structured data richer', 'ai-rank-suite')}
				action={
					<Button variant="ghost" onClick={() => setShowAll(!showAll)}>
						{showAll ? __('Show active only', 'ai-rank-suite') : sprintf(__('Show all %d', 'ai-rank-suite'), integrations.length)}
					</Button>
				}
			>
				{shown.length === 0 ? (
					<p className="m-0 text-[13px]" style={{ color: 'var(--ars-ink-2)' }}>
						{__('None of the supported plugins is installed. Nothing is missing — this list only ever adds detail to schema that is already being emitted.', 'ai-rank-suite')}
					</p>
				) : (
					<div className="ars-cardgrid" style={{ '--min': '320px' }}>
						{shown.map((i) => (
							<Row key={i.key} active={i.active} label={i.label} note={i.provides} />
						))}
					</div>
				)}
			</Card>
		</div>
	);
}


/**
 * The agency's name on the menu and the header, and no vendor line in the
 * footer. Presentation only: the Plugins screen still says what the plugin
 * is, and every route and hook keeps its name.
 */
function WhiteLabel({ data, onSaved }) {
	const [brand, setBrand] = useState(data.brand_name || '');
	const [busy, setBusy] = useState(false);
	const [error, setError] = useState(null);
	const [saved, setSaved] = useState(false);
	const dirty = brand !== (data.brand_name || '');

	const put = async (patch) => {
		setBusy(true);
		setError(null);
		try {
			onSaved(await api.put('/settings/general', patch));
			setSaved(true);
			setTimeout(() => setSaved(false), 2200);
		} catch (e) {
			setError(e.message);
		} finally {
			setBusy(false);
		}
	};

	const [theme, setTheme] = useState(readTheme);

	return (
		<Card
			icon={<IconLayers size={16} />}
			title={__('Appearance & white label', 'ai-rank-suite')}
			subtitle={__('Put your agency\'s name on the plugin your clients see', 'ai-rank-suite')}
			action={saved && <Badge tone="good">✓ {__('Saved', 'ai-rank-suite')}</Badge>}
		>
			<div className="grid gap-4 md:grid-cols-2">
				<div className="grid gap-3">
					<Field label={__('Name shown in the WordPress menu and the header', 'ai-rank-suite')} hint={__('Empty = AI Rank Suite. Takes effect on the next page load.', 'ai-rank-suite')}>
						<div className="flex gap-2">
							<input type="text" value={brand} maxLength={40} onChange={(e) => setBrand(e.target.value)} placeholder={__('AI Rank Suite', 'ai-rank-suite')} style={{ flex: 1 }} />
							<Button onClick={() => put({ brand_name: brand })} busy={busy} disabled={!dirty}>{__('Save', 'ai-rank-suite')}</Button>
						</div>
					</Field>
					<Switch
						checked={!!data.hide_footer}
						onChange={(v) => put({ hide_footer: v })}
						label={__('Hide the vendor line at the foot of the sidebar', 'ai-rank-suite')}
					/>
					<Switch
						checked={theme === 'dark'}
						onChange={(v) => setTheme(writeTheme(v ? 'dark' : 'light'))}
						label={__('Dark theme for this plugin\'s screens', 'ai-rank-suite')}
					/>
					<p className="m-0 t-caption">{__('Stored in this browser only — it changes nothing for anyone else, and nothing outside these screens.', 'ai-rank-suite')}</p>
					{error && <Notice tone="serious">{error}</Notice>}
				</div>
				<p className="m-0 text-[12.5px]" style={{ color: 'var(--ars-ink-2)' }}>
					{__('For an agency running the plugin on client sites: the client sees your name, your reports, your work. The toolbar menu can be hidden under Advanced → Ghost Mode, and client access is set per role under Advanced → Roles. The Plugins screen keeps the real plugin name — that is a WordPress rule, and the right one.', 'ai-rank-suite')}
				</p>
			</div>
		</Card>
	);
}

/**
 * How long the plugin keeps its own logs. Days; 0 keeps everything. A daily
 * job prunes to these — on a busy site the AI ledger and the 404 monitor are
 * the two tables that otherwise grow forever.
 */
function Retention({ data, onSaved }) {
	const [values, setValues] = useState({
		retention_ai_usage_days: data.retention_ai_usage_days ?? 365,
		retention_404_days: data.retention_404_days ?? 90,
		retention_crawler_days: data.retention_crawler_days ?? 90,
	});
	const [busy, setBusy] = useState(false);
	const [error, setError] = useState(null);
	const [saved, setSaved] = useState(false);
	const dirty = Object.keys(values).some((k) => Number(values[k]) !== Number(data[k] ?? 0));

	const save = async () => {
		setBusy(true);
		setError(null);
		try {
			onSaved(await api.put('/settings/general', values));
			setSaved(true);
			setTimeout(() => setSaved(false), 2200);
		} catch (e) {
			setError(e.message);
		} finally {
			setBusy(false);
		}
	};

	const ROWS = [
		['retention_ai_usage_days', __('AI usage ledger', 'ai-rank-suite'), __('One row per model call — cost, tokens, job. Budgets and the usage screen read the last 30 days; older rows are history.', 'ai-rank-suite')],
		['retention_404_days', __('404 monitor', 'ai-rank-suite'), __('Missing paths and how often they were hit. A path nobody has hit in this many days is dropped.', 'ai-rank-suite')],
		['retention_crawler_days', __('AI crawler visits', 'ai-rank-suite'), __('Per-bot daily counters from the crawler recorder.', 'ai-rank-suite')],
	];

	return (
		<Card
			title={__('Log retention', 'ai-rank-suite')}
			subtitle={__('How long the plugin keeps its own logs — pruned daily; 0 keeps everything', 'ai-rank-suite')}
			action={
				<span className="flex items-center gap-2">
					{saved && <Badge tone="good">✓ {__('Saved', 'ai-rank-suite')}</Badge>}
					<Button onClick={save} busy={busy} disabled={!dirty}>{__('Save', 'ai-rank-suite')}</Button>
				</span>
			}
		>
			<div className="grid gap-3">
				{ROWS.map(([key, label, hint]) => (
					<div key={key} className="grid gap-3 md:grid-cols-[220px_140px_minmax(0,1fr)] items-center">
						<span className="font-medium text-[13px]">{label}</span>
						<span className="flex items-center gap-2">
							<input
								type="number"
								min={0}
								max={3650}
								value={values[key]}
								onChange={(e) => setValues({ ...values, [key]: Math.max(0, Math.min(3650, Number(e.target.value) || 0)) })}
								style={{ width: 90 }}
							/>
							<span className="t-caption">days</span>
						</span>
						<span className="t-caption">{hint}</span>
					</div>
				))}
				{error && <Notice tone="serious">{error}</Notice>}
				<p className="m-0 t-caption">
					{__('Consent-log retention lives with the consent settings (Analytics → Consent), because it is a legal setting rather than a housekeeping one.', 'ai-rank-suite')}
				</p>
			</div>
		</Card>
	);
}
