import React, { useEffect, useState } from 'react';
import { __, sprintf } from '@wordpress/i18n';
import { api } from '../api';
import { Card, Button, Badge, Notice, Tabs, PageState } from '../components/ui';
import { copyText } from '../copy';
import Webhooks from '../components/Webhooks';

function Status({ data, onMessage }) {
	const [busy, setBusy] = useState(null);

	const run = async (task) => {
		setBusy(task);
		try {
			const r = await api.post('/dev/maintenance', { task });
			onMessage(r.message);
		} catch (e) {
			onMessage(e.message, true);
		} finally {
			setBusy(null);
		}
	};

	const env = data.environment;
	const rows = [
		[__('Plugin', 'ai-rank-suite'), sprintf(__('v%1$s · schema %2$s', 'ai-rank-suite'), env.plugin, env.db_schema)],
		['WordPress', env.multisite ? sprintf(__('%s (multisite)', 'ai-rank-suite'), env.wordpress) : env.wordpress],
		['PHP / MySQL', `${env.php} / ${env.mysql}`],
		[__('Theme', 'ai-rank-suite'), env.theme],
		[__('Memory limit', 'ai-rank-suite'), env.memory],
		[__('Locale', 'ai-rank-suite'), env.locale],
		[__('Pretty permalinks', 'ai-rank-suite'), env.permalinks ? __('✓ on', 'ai-rank-suite') : __('✕ off — required for llms.txt, sitemaps, IndexNow', 'ai-rank-suite')],
		['HTTPS', env.ssl ? '✓' : '✕'],
		['WP-Cron', data.constants.WP_CRON ? __('✓ running', 'ai-rank-suite') : __('✕ disabled — reports & answer simulations need a real cron', 'ai-rank-suite')],
		[__('Object cache', 'ai-rank-suite'), data.constants.OBJECT_CACHE ? __('✓ external', 'ai-rank-suite') : __('database (default)', 'ai-rank-suite')],
	];

	const report = [
		...rows.map(([k, v]) => `${k}: ${v}`),
		sprintf(__('Active modules: %s', 'ai-rank-suite'), data.modules.active.join(', ')),
		sprintf(__('Disabled: %s', 'ai-rank-suite'), data.modules.disabled.join(', ') || '—'),
		...data.tables.map((t) => sprintf(__('%1$s: %2$s rows', 'ai-rank-suite'), t.name, t.rows)),
	].join('\n');

	const MAINTENANCE = [
		{ task: 'flush_transients', label: __('Clear plugin caches', 'ai-rank-suite'), hint: __('Drops every airanksuite_ transient — llms.txt, reports, rate limits', 'ai-rank-suite') },
		{ task: 'flush_rewrites', label: __('Rebuild rewrite rules', 'ai-rank-suite'), hint: __('Re-registers llms.txt, sitemaps and the IndexNow key file', 'ai-rank-suite') },
		{ task: 'rerun_migration', label: __('Re-run database migration', 'ai-rank-suite'), hint: __('Rebuilds tables idempotently — safe, takes a backup first', 'ai-rank-suite') },
		{ task: 'reanalyze_all', label: __('Reset all analyses', 'ai-rank-suite'), hint: __('Every page is re-scored on next access', 'ai-rank-suite') },
		{ task: 'clear_quarantine', label: __('Clear module quarantine', 'ai-rank-suite'), hint: __('Failed modules retry on the next load', 'ai-rank-suite') },
	];

	// System status (~570px) and Maintenance (~480px) are comparable; Data
	// stacked under Maintenance made the right column ~450px taller than the
	// left, so the bottom-left third of the screen was blank. Two cards of
	// similar height in the row, Data full width beneath it in two columns —
	// the table has 9 short rows and does not need 900px of width for a table
	// name and a row count.
	return (
		<div className="grid gap-4">
			<div className="grid gap-4 lg:grid-cols-2 items-start">
				<Card
					title={__('System status', 'ai-rank-suite')}
					subtitle={__('Everything support needs in one place', 'ai-rank-suite')}
					action={
						<Button variant="ghost" onClick={() => copyText(report)}>{__('Copy report', 'ai-rank-suite')}</Button>
					}
				>
					<div className="ars-table-wrap"><table className="ars-table">
						<tbody>
							{rows.map(([k, v]) => (
								<tr key={k}>
									<td className="font-medium" style={{ width: 170 }}>{k}</td>
									<td style={{ color: 'var(--ars-ink-2)' }}>{v}</td>
								</tr>
							))}
						</tbody>
					</table></div>
					{Object.keys(data.modules.quarantine).length > 0 && (
						<div className="mt-3">
							<Notice tone="serious">
								{sprintf(__('Quarantined modules: %s — check the error, then clear the quarantine.', 'ai-rank-suite'), Object.keys(data.modules.quarantine).join(', '))}
							</Notice>
						</div>
					)}
				</Card>

				{/*
				 * Front-end faults.
				 *
				 * These are the failures nobody would otherwise find. A callback
				 * of ours threw on a public page, the guard caught it so the
				 * visitor got a working site, and with WP_DEBUG off on a
				 * production install that is the end of the story — no error,
				 * no white screen, nothing in any log. The page rendered; it
				 * just quietly rendered without our tags on it.
				 */}
				{(data.hook_faults || []).length > 0 && (
					<Card
						title={__('Failing on the public site', 'ai-rank-suite')}
						subtitle={__('Caught, so pages still render — but this output is missing from every page it runs on', 'ai-rank-suite')}
						flush
					>
						<div className="ars-table-wrap">
							<table className="ars-table">
								<thead>
									<tr>
										<th>{__('Where', 'ai-rank-suite')}</th>
										<th>{__('Error', 'ai-rank-suite')}</th>
										<th className="is-num is-narrow">{__('Times', 'ai-rank-suite')}</th>
									</tr>
								</thead>
								<tbody>
									{data.hook_faults.map((f) => (
										<tr key={f.label}>
											<td className="font-medium">{f.label}</td>
											<td style={{ color: 'var(--ars-ink-2)' }}>
												<div>{f.message}</div>
												<div className="t-caption">
													{f.class} · {f.file}:{f.line}
												</div>
											</td>
											<td className="is-num is-narrow tabular-nums">{f.count}</td>
										</tr>
									))}
								</tbody>
							</table>
						</div>
						<div className="p-4 pt-3">
							<Notice tone="serious">
								{__('Send this to support as it is — the file and line are the whole diagnosis. Clearing the quarantine below also empties this list, so fix first and clear second.', 'ai-rank-suite')}
							</Notice>
						</div>
					</Card>
				)}

				<Card title={__('Maintenance', 'ai-rank-suite')} subtitle={__('Safe one-click operations — nothing here deletes settings or content', 'ai-rank-suite')}>
						<div className="grid gap-2.5">
							{MAINTENANCE.map((m) => (
								<div key={m.task} className="flex items-center justify-between gap-3 ars-r-lg px-4 py-2.5" style={{ background: 'var(--ars-surface-2)' }}>
									<div>
										<div className="font-medium text-[13px]">{m.label}</div>
										<div className="text-[11.5px]" style={{ color: 'var(--ars-ink-3)' }}>{m.hint}</div>
									</div>
									<Button variant="ghost" busy={busy === m.task} onClick={() => run(m.task)}>{__('Run', 'ai-rank-suite')}</Button>
								</div>
							))}
						</div>
					</Card>

		</div>

			<Card title={__('Data', 'ai-rank-suite')} subtitle={__('Plugin tables and scheduled jobs', 'ai-rank-suite')}>
				{/* items-start: a grid cell stretches its table, and a 2-row table
					    stretched to a 7-row table's height spreads those two rows 120px
					    apart. */}
				<div className="grid gap-x-8 md:grid-cols-2 items-start">
					<div className="ars-table-wrap"><table className="ars-table">
						<thead>
							<tr>
								<th>{__('Table', 'ai-rank-suite')}</th>
								<th className="is-num is-mid">{__('Rows', 'ai-rank-suite')}</th>
							</tr>
						</thead>
						<tbody>
							{data.tables.map((t) => (
								<tr key={t.name}>
									<td className="ars-mono text-[12px]">{t.name}</td>
									<td className="is-num is-mid">{t.rows}</td>
								</tr>
							))}
						</tbody>
					</table></div>
					<div className="ars-table-wrap"><table className="ars-table">
						<thead>
							<tr>
								<th>{__('Scheduled job', 'ai-rank-suite')}</th>
								<th className="is-num">{__('Next run', 'ai-rank-suite')}</th>
							</tr>
						</thead>
						<tbody>
							{data.crons.map((c) => (
								<tr key={c.hook}>
									<td className="ars-mono text-[12px]">{c.hook}</td>
									<td className="is-num text-[12px]" style={{ color: 'var(--ars-ink-3)' }}>
										{c.next || __('not scheduled', 'ai-rank-suite')}
									</td>
								</tr>
							))}
						</tbody>
					</table></div>
				</div>
			</Card>
		</div>
	);
}

/**
 * Which third-party plugins we can read from, and which are live here.
 * Answering "does this work with my stack?" without a support ticket.
 */
function Integrations() {
	const [rows, setRows] = useState(null);

	useEffect(() => {
		api.get('/integrations').then((r) => setRows(r.integrations)).catch(() => setRows([]));
	}, []);

	if (!rows) return <Card title={__('Plugin integrations', 'ai-rank-suite')}>{__('Loading…', 'ai-rank-suite')}</Card>;
	const active = rows.filter((r) => r.active);

	return (
		<Card
			title={__('Plugin integrations', 'ai-rank-suite')}
			subtitle={sprintf(__('%1$s of %2$s detected on this site — data is read automatically, nothing to configure', 'ai-rank-suite'), active.length, rows.length)}
		>
			<div className="grid gap-2 sm:grid-cols-2">
				{[...rows].sort((a, b) => Number(b.active) - Number(a.active)).map((r) => (
					<div
						key={r.key}
						className="flex items-start justify-between gap-3 ars-r-lg px-4 py-3"
						style={{ background: r.active ? 'var(--ars-good-tonal)' : 'var(--ars-surface-2)' }}
					>
						<div className="min-w-0">
							<div className="font-medium text-[13px] flex items-center gap-1.5">
								<span aria-hidden style={{ color: r.active ? 'var(--ars-good)' : 'var(--ars-ink-3)' }}>
									{r.active ? '✓' : '○'}
								</span>
								{r.label}
							</div>
							<div className="text-[11.5px] mt-0.5" style={{ color: 'var(--ars-ink-2)' }}>
								{r.provides}
							</div>
						</div>
					</div>
				))}
			</div>
		</Card>
	);
}

function Hooks({ data }) {
	/*
	 * Grouped by module, the way SEOPress groups by area — an agency looking
	 * to bend the schema output should not have to scan the autopilot's hooks
	 * to find it. The list itself is generated from the code that fires the
	 * hooks (bin/hooks.php), so it cannot disagree with the plugin it ships in.
	 */
	const groups = [];
	const byModule = new Map();
	(data.hooks || []).forEach((h) => {
		if (!byModule.has(h.module)) {
			byModule.set(h.module, []);
			groups.push(h.module);
		}
		byModule.get(h.module).push(h);
	});

	return (
		<Card
			title={__('Hooks reference', 'ai-rank-suite')}
			subtitle={sprintf(__('%s extension points, generated from the code that fires them — build your own modules, schema types and crawler rules on top', 'ai-rank-suite'), (data.hooks || []).length)}
		>
			<div className="ars-table-wrap"><table className="ars-table">
				<thead>
					<tr>
						<th>{__('Hook', 'ai-rank-suite')}</th>
						<th>{__('Type', 'ai-rank-suite')}</th>
						<th>{__('Arguments', 'ai-rank-suite')}</th>
						<th>{__('What it does', 'ai-rank-suite')}</th>
					</tr>
				</thead>
				<tbody>
					{groups.map((module) => (
						<React.Fragment key={module}>
							<tr>
								<td colSpan={4} className="t-overline" style={{ background: 'var(--ars-surface-2)', paddingTop: 8, paddingBottom: 8 }}>
									{module}
								</td>
							</tr>
							{byModule.get(module).map((h) => (
								<tr key={h.hook}>
									<td className="ars-mono text-[12px]" style={{ whiteSpace: 'nowrap' }} title={(h.sources || []).join('\n')}>
										{h.hook}
									</td>
									<td>
										<Badge tone={h.type === 'filter' ? 'brand' : 'neutral'}>{h.type}</Badge>
									</td>
									<td className="ars-mono text-[11.5px]" style={{ color: 'var(--ars-ink-2)' }}>{h.args}</td>
									<td className="text-[12.5px]" style={{ color: 'var(--ars-ink-2)' }}>{h.desc}</td>
								</tr>
							))}
						</React.Fragment>
					))}
				</tbody>
			</table></div>
			<p className="m-0 mt-3 text-[12px]" style={{ color: 'var(--ars-ink-3)' }}>{__('Example:', 'ai-rank-suite')}<code className="ars-mono">add_filter( 'airanksuite/schema/graph', fn( $graph ) =&gt; [...$graph, $myNode] );</code>
			</p>
		</Card>
	);
}

/**
 * REST console: the route catalogue, plus a "Try" that fires a real GET and
 * shows the live JSON. Only GET — a developer poking at the API wants to see
 * shapes, and a one-click DELETE in a reference table is how data goes
 * missing. Writes are documented, not offered.
 */
function Api({ data }) {
	const [filter, setFilter] = useState('');
	const [live, setLive] = useState(null); // { route, status, ms, body }
	const [busy, setBusy] = useState(null);

	// The namespace root itself is in the list WordPress hands back; it is
	// index metadata, not one of our routes, and printed as an empty row.
	// The namespace root is index metadata WordPress adds, not one of ours.
	const all = data.routes.filter((r) => r.route !== '/airanksuite/v1');
	const routes = all.filter((r) => !filter || r.route.toLowerCase().includes(filter.toLowerCase()));
	const runnable = (r) => r.methods.includes('GET') && !/\(\?P</.test(r.route);

	/*
	 * WordPress stores a route as the regex it matches with, so a path reads
	 * `/ai/describe/(?P<id>\d+)`. Nobody types that, and it is the widest
	 * thing in the column. Show the placeholder the way documentation writes
	 * it — the raw pattern stays in the title attribute for anyone who needs
	 * the real expression.
	 */
	const pretty = (route) =>
		route.replace(/^\/airanksuite\/v1/, '').replace(/\(\?P<([a-z_]+)>[^)]*\)/gi, '{$1}') || '/';

	const tryRoute = async (r) => {
		setBusy(r.route);
		const path = r.route.replace(/^\/airanksuite\/v1/, '');
		const started = performance.now();
		try {
			const body = await api.get(path);
			setLive({ route: r.route, status: 200, ms: Math.round(performance.now() - started), body });
		} catch (e) {
			setLive({ route: r.route, status: e.status || 'error', ms: Math.round(performance.now() - started), body: { error: e.message } });
		} finally {
			setBusy(null);
		}
	};

	/*
	 * Side by side only when both halves can actually be read.
	 *
	 * At the lg breakpoint the admin sidebar leaves so little room that the
	 * routes card was squeezed to about 190px and its subtitle broke one word
	 * per line. Below xl the response panel sits underneath at full width.
	 */
	return (
		<div className="grid gap-4 xl:grid-cols-[minmax(0,1.15fr)_minmax(0,1fr)] items-start">
			<Card
				title={__('REST API', 'ai-rank-suite')}
				subtitle={sprintf(__('%1$s of %2$s routes under airanksuite/v1 — every one requires a capability; authenticate with an application password or the X-WP-Nonce header', 'ai-rank-suite'), routes.length, all.length)}
				action={
					<input
						type="search"
						placeholder={__('Filter routes…', 'ai-rank-suite')}
						value={filter}
						onChange={(e) => setFilter(e.target.value)}
						style={{ minHeight: 30, width: 180 }}
					/>
				}
				flush
			>
				{/* Fits, so nothing scrolls sideways.
				    A floor width on the table made the card wider than its grid
				    column, and the whole page then scrolled horizontally — worse
				    than the clipping it was meant to fix. The action column is
				    now wide enough for the button on its own (96px against a
				    52px button and 16px of padding), so the table can simply be
				    100% of whatever space it gets and the route cell truncates. */}
				<div style={{ maxHeight: 560, overflowY: 'auto', overflowX: 'hidden' }}>
					<div className="ars-table-wrap"><table className="ars-table" style={{ tableLayout: 'fixed', width: '100%' }}>
						{/* 96px, not 68: the cell carries 14px of padding on each
						    side, so a 68px column left 40px for a 52px button and
						    clipped it. The action column also drops its padding. */}
						<colgroup>
							<col />
							<col style={{ width: 104 }} />
							<col style={{ width: 96 }} />
						</colgroup>
						<thead>
							<tr>
								<th>{__('Route', 'ai-rank-suite')}</th>
								<th>{__('Methods', 'ai-rank-suite')}</th>
								<th aria-label={__('Try', 'ai-rank-suite')} />
							</tr>
						</thead>
						<tbody>
							{routes.map((r) => (
								<tr key={r.route} className={live?.route === r.route ? 'is-active' : ''}>
									<td className="ars-mono text-[12px]" style={{ minWidth: 0 }} title={r.route}>
										<span className="ars-url" style={{ display: 'block', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
											{pretty(r.route)}
										</span>
									</td>
									<td>
										<div className="flex gap-1">
											{r.methods.map((m) => (
												<Badge key={m} tone={m === 'GET' ? 'good' : m === 'DELETE' ? 'serious' : 'brand'}>
													{m}
												</Badge>
											))}
										</div>
									</td>
									<td className="is-num" style={{ whiteSpace: 'nowrap', paddingInline: 8 }}>
										{/* A route with an {id} placeholder cannot be called
										    blind, and a write must not be fired from a
										    documentation screen. Say which it is instead of
										    leaving an unexplained gap. */}
										{!runnable(r) && (
											<span className="t-caption" title={/\(\?P</.test(r.route) ? __('Needs an id in the path', 'ai-rank-suite') : __('Not a GET route', 'ai-rank-suite')}>
												{/\(\?P</.test(r.route) ? __('needs id', 'ai-rank-suite') : '—'}
											</span>
										)}
										{runnable(r) && (
											<Button variant="ghost" busy={busy === r.route} onClick={() => tryRoute(r)}>{__('Try', 'ai-rank-suite')}</Button>
										)}
									</td>
								</tr>
							))}
						</tbody>
					</table></div>
				</div>
			</Card>

			<Card
				title={__('Response', 'ai-rank-suite')}
				subtitle={
					live
						? sprintf(__('%1$s · %2$s · %3$s ms', 'ai-rank-suite'), live.route.replace(/^\/airanksuite\/v1/, ''), live.status, live.ms)
						: __('Press Try on any GET route to see its live JSON', 'ai-rank-suite')
				}
				action={
					live && (
						<Button variant="ghost" onClick={() => copyText(JSON.stringify(live.body, null, 2))}>{__('Copy JSON', 'ai-rank-suite')}</Button>
					)
				}
			>
				{live ? (
					<pre
						className="ars-mono m-0"
						style={{
							fontSize: 11.5,
							lineHeight: 1.5,
							maxHeight: 520,
							overflow: 'auto',
							padding: 12,
							borderRadius: 'var(--ars-r-md)',
							background: 'var(--ars-surface-2)',
							color: 'var(--ars-ink)',
							whiteSpace: 'pre-wrap',
							wordBreak: 'break-word',
						}}
					>
						{JSON.stringify(live.body, null, 2)}
					</pre>
				) : (
					<div className="t-caption">{__('Nothing requested yet.', 'ai-rank-suite')}</div>
				)}
			</Card>
		</div>
	);
}

export default function DevTools({ tab = 'status', onTab }) {
	const [data, setData] = useState(null);
	const [message, setMessage] = useState(null);
	const [error, setError] = useState(null);

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

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

	const onMessage = (msg, isError) => {
		setMessage({ msg, isError });
		setTimeout(() => setMessage(null), 4000);
		if (!isError) load();
	};

	return (
		<div className="ars-page">
			<Tabs
				tabs={[
					{ id: 'status', label: __('Status', 'ai-rank-suite') },
					{ id: 'integrations', label: __('Integrations', 'ai-rank-suite') },
					{ id: 'hooks', label: __('Hooks', 'ai-rank-suite') },
					{ id: 'webhooks', label: __('Webhooks', 'ai-rank-suite') },
					...(window.airanksuiteData?.debug ? [{ id: 'api', label: __('API', 'ai-rank-suite') }] : []),
				]}
				active={tab}
				onChange={onTab}
			/>
			{message && <Notice tone={message.isError ? 'serious' : 'good'}>{message.msg}</Notice>}
			{tab === 'status' && <Status data={data} onMessage={onMessage} />}
			{tab === 'integrations' && <Integrations />}
			{tab === 'hooks' && <Hooks data={data} />}
			{tab === 'webhooks' && <Webhooks />}
			{tab === 'api' && window.airanksuiteData?.debug && <Api data={data} />}
		</div>
	);
}
