import React, { useEffect, useState } from 'react';
import { __, sprintf } from '@wordpress/i18n';
import { api } from '../api';
import KeywordSuggest from '../components/KeywordSuggest';
import { Card, Button, Badge, Notice, MetricGrid, LineChart, PosBadge, Sparkline, ColumnChart, Delta, PageState, Field } from '../components/ui';
import { IconRefresh, IconX } from '../components/icons';

/**
 * The shared Delta, with the one thing it cannot know: absence.
 *
 * `change` is null until a keyword has two snapshots far enough apart, and
 * ±0 would claim "measured, no movement" — an em-dash is the honest value.
 * The number itself is already signed so that positive means climbed
 * (position 12 → 9 is +3), so no invert.
 */
function Change({ value }) {
	if (value === null || value === undefined) {
		return <span style={{ color: 'var(--ars-ink-3)' }}>—</span>;
	}
	return <Delta value={value} />;
}

function KeywordPlanner({ tracked, onTrack }) {
	const [status, setStatus] = useState(null);
	const [form, setForm] = useState({ client_id: '', client_secret: '', developer_token: '', customer_id: '', login_customer_id: '', language_id: '1000', geo_target_ids: '2784' });
	const [seeds, setSeeds] = useState('');
	const [pageUrl, setPageUrl] = useState('');
	const [ideas, setIdeas] = useState([]);
	const [busy, setBusy] = useState(false);
	const [error, setError] = useState(null);
	const [serp, setSerp] = useState(null);
	const [serpReport, setSerpReport] = useState(null);

	useEffect(() => {
		api.get('/rank/serp').then(setSerp).catch(() => setSerp(false));
		api.get('/rank/planner/status').then((next) => {
			setStatus(next);
			setForm((old) => ({ ...old, ...next, client_secret: '', developer_token: '', geo_target_ids: (next.geo_target_ids || []).join(', ') }));
		}).catch(() => setStatus(false));
	}, []);

	if (status === null || status === false) return null;
	const set = (key) => (e) => setForm((old) => ({ ...old, [key]: e.target.value }));
	const act = async (fn) => {
		setBusy(true); setError(null);
		try { await fn(); } catch (e) { setError(e.message); } finally { setBusy(false); }
	};

	/**
	 * Ask a search engine where the tracked keywords actually stand. Search
	 * Console answers a different question — the average position of pages it
	 * already shows — so a keyword the site does not rank for yet is simply
	 * absent from it.
	 */
	const checkPositions = (force) =>
		act(async () => {
			const r = await api.post('/rank/serp', { limit: 25, force });
			setSerpReport(r);
			setSerp(await api.get('/rank/serp'));
		});

	return (
		<>
		<Card
			title={__('Live positions', 'ai-rank-suite')}
			subtitle={__('Where this site stands today for the keywords you track, asked of a search engine rather than averaged out of Search Console.', 'ai-rank-suite')}
		>
			{serp && serp.provider ? (
				<div className="grid gap-2">
					<div className="flex flex-wrap items-center gap-2">
						<Button busy={busy} onClick={() => checkPositions(false)}>{__('Check positions now', 'ai-rank-suite')}</Button>
						<Button variant="ghost" busy={busy} onClick={() => checkPositions(true)}>{__('Check again today', 'ai-rank-suite')}</Button>
						<span className="t-caption">
							{sprintf(
								/* translators: %s: the provider being asked, e.g. Serper. */
								__('Asking %s — each check spends one lookup from your quota.', 'ai-rank-suite'),
								serp.provider
							)}
						</span>
					</div>
					{serpReport && (
						<div className="t-caption">
							{sprintf(
								/* translators: 1: keywords checked, 2: how many were found in the results, 3: keywords skipped, 4: failures. */
								__('%1$s checked, %2$s found in the results, %3$s already done today, %4$s failed.', 'ai-rank-suite'),
								Number(serpReport.checked || 0).toLocaleString(),
								Number(serpReport.found || 0).toLocaleString(),
								Number(serpReport.skipped || 0).toLocaleString(),
								Number(serpReport.failed || 0).toLocaleString()
							)}
							{(serpReport.errors || []).length > 0 && (
								<ul className="m-0 mt-1 pl-4">
									{serpReport.errors.slice(0, 5).map((e, i) => (
										<li key={i}>
											<code>{e.keyword}</code> — {e.message}
										</li>
									))}
								</ul>
							)}
						</div>
					)}
				</div>
			) : (
				<p className="m-0 text-[13px]" style={{ color: 'var(--ars-ink-2)' }}>
					{__('No SERP provider is set up yet. Add one under SEO data providers and this becomes a daily position check for every keyword you track — including the ones you do not rank for yet, which Search Console cannot show you at all.', 'ai-rank-suite')}
				</p>
			)}
		</Card>
		<Card title={__('Google Keyword Planner', 'ai-rank-suite')} subtitle={__('Live search-volume ideas from your own Google Ads account; suggestions are never saved until you choose Track', 'ai-rank-suite')}>
			{error && <Notice tone="serious">{error}</Notice>}
			{!status.configured ? (
				<div className="grid gap-3 md:grid-cols-2">
					<Field label={__('OAuth client ID', 'ai-rank-suite')}><input value={form.client_id} onChange={set('client_id')} /></Field>
					<Field label={__('OAuth client secret', 'ai-rank-suite')} hint={status.has_client_secret ? 'Saved — leave blank to keep it' : ''}><input type="password" value={form.client_secret} onChange={set('client_secret')} /></Field>
					<Field label={__('Developer token', 'ai-rank-suite')} hint={status.has_developer_token ? 'Saved — leave blank to keep it' : 'From Google Ads API Center'}><input type="password" value={form.developer_token} onChange={set('developer_token')} /></Field>
					<Field label={__('Customer ID', 'ai-rank-suite')} hint={__('Digits only; hyphens are accepted', 'ai-rank-suite')}><input value={form.customer_id} onChange={set('customer_id')} /></Field>
					<Field label={__('Manager customer ID', 'ai-rank-suite')} hint={__('Optional MCC login customer', 'ai-rank-suite')}><input value={form.login_customer_id} onChange={set('login_customer_id')} /></Field>
					<Field label={__('Language constant ID', 'ai-rank-suite')}><input value={form.language_id} onChange={set('language_id')} /></Field>
					<Field label={__('Geo target IDs', 'ai-rank-suite')} hint={__('Comma-separated; 2784 is UAE', 'ai-rank-suite')}><input value={form.geo_target_ids} onChange={set('geo_target_ids')} /></Field>
					<div className="flex items-end"><Button busy={busy} onClick={() => act(async () => setStatus(await api.put('/rank/planner/settings', form)))}>{__('Save Google Ads settings', 'ai-rank-suite')}</Button></div>
					<p className="m-0 md:col-span-2 text-[12px]" style={{ color: 'var(--ars-ink-3)' }}>{__('OAuth redirect URI:', 'ai-rank-suite')}<code>{status.redirect_uri}</code></p>
				</div>
			) : !status.connected ? (
				<div className="flex items-center gap-3 flex-wrap">
					<Badge tone="warning">{__('Not connected', 'ai-rank-suite')}</Badge>
					<Button busy={busy} onClick={() => act(async () => { const r = await api.get('/rank/planner/connect-url'); window.location.assign(r.url); })}>{__('Connect Google Ads', 'ai-rank-suite')}</Button>
					<Button variant="ghost" onClick={() => setStatus({ ...status, configured: false })}>{__('Edit settings', 'ai-rank-suite')}</Button>
				</div>
			) : (
				<>
					<div className="grid gap-3 md:grid-cols-2">
						<Field label={__('Seed keywords', 'ai-rank-suite')} hint={__('Up to 20, one per line or comma-separated', 'ai-rank-suite')}><textarea rows={3} value={seeds} onChange={(e) => setSeeds(e.target.value)} /></Field>
						<Field label={__('Page URL', 'ai-rank-suite')} hint={__('Optional; must be on this site', 'ai-rank-suite')}><input type="url" value={pageUrl} onChange={(e) => setPageUrl(e.target.value)} placeholder={window.location.origin + '/page/'} /></Field>
					</div>
					<div className="flex gap-2 mt-3 flex-wrap">
						<Button busy={busy} disabled={!seeds.trim() && !pageUrl.trim()} onClick={() => act(async () => { const r = await api.post('/rank/planner/ideas', { keywords: seeds.split(/[\n,]+/).map((v) => v.trim()).filter(Boolean).slice(0, 20), page_url: pageUrl, page_size: 100 }); setIdeas(r.ideas || []); })}>{__('Find keyword ideas', 'ai-rank-suite')}</Button>
						<Button variant="ghost" onClick={() => act(async () => setStatus(await api.post('/rank/planner/disconnect')))}>{__('Disconnect', 'ai-rank-suite')}</Button>
					</div>
					{ideas.length > 0 && <div className="ars-table-wrap mt-4" style={{ maxHeight: 480 }}><div className="ars-table-wrap"><table className="ars-table"><thead><tr><th>{__('Keyword', 'ai-rank-suite')}</th><th className="is-num">{__('Monthly searches', 'ai-rank-suite')}</th><th>{__('Competition', 'ai-rank-suite')}</th><th className="is-num">{__('Top bid range', 'ai-rank-suite')}</th><th /></tr></thead><tbody>{ideas.map((idea) => <tr key={idea.keyword}><td className="font-medium">{idea.keyword}</td><td className="is-num">{idea.avg_monthly.toLocaleString()}</td><td>{idea.competition.replaceAll('_', ' ')}</td><td className="is-num">{(idea.low_bid_micros / 1000000).toFixed(2)}–{(idea.high_bid_micros / 1000000).toFixed(2)}</td><td><Button variant="ghost" disabled={tracked.includes(idea.keyword)} onClick={() => onTrack(idea.keyword)}>{tracked.includes(idea.keyword) ? 'Tracked' : 'Track'}</Button></td></tr>)}</tbody></table></div></div>}
				</>
			)}
		</Card>
		</>
	);
}

export default function RankTracker() {
	const [data, setData] = useState(null);
	const [keyword, setKeyword] = useState('');
	const [busy, setBusy] = useState(false);
	const [error, setError] = useState(null);
	const [open, setOpen] = useState(null);
	const [history, setHistory] = useState(null);
	const [trend, setTrend] = useState(null);
	const flash = new URLSearchParams((window.location.hash.split('?')[1] || ''));

	useEffect(() => {
		api.get('/rank/keywords').then(setData).catch((e) => setError(e.message));
		api.get('/rank/trend?days=90').then((r) => setTrend(r.days || [])).catch(() => setTrend([]));
	}, []);

	useEffect(() => {
		if (!open) return;
		setHistory(null);
		api.get(`/rank/history/${open.id}?days=90`).then((r) => setHistory(r.history)).catch(() => setHistory([]));
	}, [open]);

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

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

	const tracked = data.keywords.length;
	const ranked = data.keywords.filter((k) => k.position !== null);
	const top10 = ranked.filter((k) => k.position <= 10).length;
	// Em-dash, not zero: with nothing ranked there is no average to claim.
	const avg = ranked.length ? (ranked.reduce((s, k) => s + k.position, 0) / ranked.length).toFixed(1) : '—';

	return (
		<div className="ars-page">
			{flash.get('ads_connected') && <Notice tone="good">{__('Google Ads is connected. Keyword Planner is ready.', 'ai-rank-suite')}</Notice>}
			{flash.get('ads_error') && <Notice tone="serious">{flash.get('ads_error')}</Notice>}
			{error && <Notice tone="serious">{error}</Notice>}
			{!data.connected && (
				<Notice>
					{__('This tracks your average position from your own Google Search Console data — not independent SERP rankings. Connect a property under “Search Console” to start collecting positions.', 'ai-rank-suite')}
				</Notice>
			)}

			<KeywordPlanner
				tracked={(data.keywords || []).map((item) => item.keyword)}
				onTrack={(next) => run(() => api.post('/rank/keywords', { keyword: next }))}
			/>

			{/* The four headline numbers in one bordered grid — density is the
			    feature, and the explanation lives in the `?` hover. */}
			<MetricGrid
				columns={4}
				metrics={[
					{
						id: 'tracked',
						label: __('Tracked keywords', 'ai-rank-suite'),
						hint: __('Everything a snapshot is stored for daily — added by hand or imported from your top queries.', 'ai-rank-suite'),
						value: tracked,
					},
					{
						id: 'ranked',
						label: __('With position data', 'ai-rank-suite'),
						hint: __('Keywords Google has reported a position for. The rest are waiting on their first snapshot.', 'ai-rank-suite'),
						value: ranked.length,
					},
					{
						id: 'top10',
						label: __('In top 10', 'ai-rank-suite'),
						hint: __('Ranking on page one — the only page most people ever see.', 'ai-rank-suite'),
						value: top10,
					},
					{
						id: 'avg',
						label: __('Avg. position', 'ai-rank-suite'),
						hint: __('Averaged across every keyword with a position. Lower is better.', 'ai-rank-suite'),
						value: avg,
					},
				]}
			/>

			{/* Where the site is going, not just where it stands: the average
			    position across every tracked keyword, one point per daily
			    snapshot. Inverted, because on this axis lower is better. */}
			{trend && trend.length > 1 && (
				<Card
					title={__('Average position over time', 'ai-rank-suite')}
					subtitle={__('Across every tracked keyword that had a position that day — lower is better', 'ai-rank-suite')}
					action={
						<Delta
							value={Number((trend[0].value - trend[trend.length - 1].value).toFixed(1))}
							suffix={` ${__('since the first snapshot', 'ai-rank-suite')}`}
						/>
					}
				>
					<LineChart
						data={trend}
						label={__('Average position', 'ai-rank-suite')}
						color="var(--ars-series-1)"
						height={150}
						invert
						formatValue={(v) => `#${v}`}
					/>
					<div className="grid gap-3 sm:grid-cols-3 mt-3">
						{[
							['top3', __('In the top 3', 'ai-rank-suite')],
							['top10', __('On page one', 'ai-rank-suite')],
							['clicks', __('Clicks that day', 'ai-rank-suite')],
						].map(([key, label]) => {
							const now = trend[trend.length - 1]?.[key] ?? 0;
							const was = trend[0]?.[key] ?? 0;
							return (
								<div key={key} className="ars-r-md px-3 py-2.5" style={{ background: 'var(--ars-surface-2)' }}>
									<div className="flex items-center justify-between gap-2">
										<span className="text-[12px] font-semibold" style={{ color: 'var(--ars-ink-2)' }}>
											{label}
										</span>
										<Delta value={now - was} />
									</div>
									<div className="t-num text-[18px] font-bold mt-0.5">{now.toLocaleString()}</div>
								</div>
							);
						})}
					</div>
				</Card>
			)}

			{/*
			 * The distribution, stacked — screenshot 4's chart.
			 *
			 * Each column is one daily snapshot: every tracked keyword that had
			 * a position that day, bucketed. The column's height is the total,
			 * so a site gaining rankings grows taller and a site trading
			 * page-one spots for page-five spots changes colour without
			 * changing height — two different stories a grouped chart would
			 * make the reader assemble themselves.
			 */}
			{(data.distribution || []).filter((d) => d.top3 + d.mid + d.low > 0).length >= 2 && (
				<Card
					title={__('Position distribution', 'ai-rank-suite')}
					subtitle={__('Every tracked keyword with a position, per daily snapshot — Top 3, page one, and pages two to five', 'ai-rank-suite')}
				>
					<ColumnChart
						stacked
						height={130}
						data={data.distribution.map((d) => ({
							label: d.d.slice(5),
							top3: d.top3,
							mid: d.mid,
							low: d.low,
						}))}
						series={[
							{ key: 'top3', label: __('Top 3', 'ai-rank-suite'), color: 'var(--ars-c-green-ink)' },
							{ key: 'mid', label: '4–10', color: 'var(--ars-brand)' },
							{ key: 'low', label: '11–50', color: 'var(--ars-accent)' },
						]}
					/>
				</Card>
			)}

			<Card
				title={__('Keywords', 'ai-rank-suite')}
				subtitle={__('A snapshot is stored daily; the top 25 queries are picked up automatically', 'ai-rank-suite')}
				action={
					<div className="flex items-center gap-2">
						<Button variant="ghost" busy={busy} onClick={() => run(() => api.post('/rank/sync'))} disabled={!data.connected}>
							<IconRefresh size={13} />{__('Sync now', 'ai-rank-suite')}</Button>
					</div>
				}
			>
				<form
					className="flex gap-2 mb-4"
					onSubmit={(e) => {
						e.preventDefault();
						if (!keyword.trim()) return;
						run(async () => {
							const next = await api.post('/rank/keywords', { keyword });
							setKeyword('');
							return next;
						});
					}}
				>
					<input
						type="text"
						placeholder={__('Add a keyword to track…', 'ai-rank-suite')}
						value={keyword}
						onChange={(e) => setKeyword(e.target.value)}
					/>
					<Button type="submit" busy={busy} disabled={!keyword.trim()}>{__('Track', 'ai-rank-suite')}</Button>
				</form>

				{/* The empty box is where people stall. The engine already reads
				    this site for meta descriptions; it can propose what to track. */}
				<div className="mb-4">
					<KeywordSuggest
						taken={(data.keywords || []).map((k) => k.keyword)}
						onPick={(k) => run(() => api.post('/rank/keywords', { keyword: k }))}
					/>
				</div>

				{tracked === 0 ? (
					<p className="m-0 text-[13px]" style={{ color: 'var(--ars-ink-2)' }}>{__('No keywords yet. Add one above, or hit “Sync now” to import your top queries from Search Console.', 'ai-rank-suite')}</p>
				) : (
					<div className="ars-table-wrap"><div className="ars-table-wrap"><table className="ars-table">
						<thead>
							<tr>
								<th>{__('Keyword', 'ai-rank-suite')}</th>
								<th className="is-num">{__('Position', 'ai-rank-suite')}</th>
								<th className="is-num">{__('7-day change', 'ai-rank-suite')}</th>
								<th className="is-mid" style={{ width: 110 }}>
									{__('30 days', 'ai-rank-suite')}
								</th>
								<th className="is-num">{__('Clicks', 'ai-rank-suite')}</th>
								<th className="is-num">{__('Impressions', 'ai-rank-suite')}</th>
								<th />
							</tr>
						</thead>
						<tbody>
							{data.keywords.map((k) => (
								<tr key={k.id} onClick={() => setOpen(k)} style={{ cursor: 'pointer' }}>
									<td className="font-medium">
										{k.keyword}
										{k.source === 'auto' && (
											<span className="ml-2">
												<Badge tone="neutral">auto</Badge>
											</span>
										)}
									</td>
									<td className="is-num">
										<PosBadge value={k.position} />
									</td>
									<td className="is-num">
										<Change value={k.change} />
									</td>
									{/* Position negated so climbing towards #1 draws as a
									    rising line — the same flip the detail chart makes,
									    because a sparkline that falls as you improve would
									    be read wrong by everyone, every time. */}
									<td className="is-mid">
										{(k.history || []).length >= 3 ? (
											<span style={{ display: 'inline-block', width: 96, color: 'var(--ars-brand)' }}>
												<Sparkline data={k.history.map((h) => -h.position)} height={22} />
											</span>
										) : (
											<span style={{ color: 'var(--ars-ink-3)' }}>—</span>
										)}
									</td>
									<td className="is-num">{k.clicks}</td>
									<td className="is-num">{k.impressions}</td>
									<td className="is-num">
										<button
											type="button"
											aria-label={__('Stop tracking', 'ai-rank-suite')}
											className="border-0 bg-transparent cursor-pointer"
											style={{ color: 'var(--ars-ink-3)' }}
											onClick={(e) => {
												e.stopPropagation();
												run(() => api.del(`/rank/keywords/${k.id}`));
											}}
										>
											<IconX size={14} />
										</button>
									</td>
								</tr>
							))}
						</tbody>
					</table></div></div>
				)}
				{data.synced_at > 0 && (
					<p className="m-0 mt-3 text-[12px]" style={{ color: 'var(--ars-ink-3)' }}>
						Last sync: {new Date(data.synced_at * 1000).toLocaleString()}
					</p>
				)}
			</Card>

			{open && (
				<Card
					title={sprintf(/* translators: %s: the tracked keyword. */ __('Position history — %s', 'ai-rank-suite'), open.keyword)}
					subtitle={__('The axis is flipped: a rising line means you are climbing towards position 1', 'ai-rank-suite')}
					action={
						<Button variant="ghost" onClick={() => setOpen(null)}>{__('Close', 'ai-rank-suite')}</Button>
					}
				>
					{history === null ? (
						<p className="m-0">{__('Loading…', 'ai-rank-suite')}</p>
					) : history.length < 2 ? (
						<p className="m-0 text-[13px]" style={{ color: 'var(--ars-ink-2)' }}>{__('Not enough snapshots yet — history builds up one day at a time.', 'ai-rank-suite')}</p>
					) : (
						<LineChart
							label={__('Google position', 'ai-rank-suite')}
							color="var(--ars-series-1)"
							invert
							data={history.map((h) => ({ date: h.date, value: h.position }))}
							formatValue={(v) => `#${Number(v).toFixed(1)}`}
						/>
					)}
				</Card>
			)}

			<div className="grid gap-4 lg:grid-cols-2">
				{[
					{ title: __('Gaining positions', 'ai-rank-suite'), rows: data.movers.winners, up: true },
					{ title: __('Losing positions', 'ai-rank-suite'), rows: data.movers.losers, up: false },
				].map((box) => (
					<Card key={box.title} title={box.title}>
						{box.rows.length === 0 ? (
							<p className="m-0 text-[13px]" style={{ color: 'var(--ars-ink-2)' }}>{__('Nothing to report yet.', 'ai-rank-suite')}</p>
						) : (
							<div className="ars-table-wrap"><table className="ars-table">
								<tbody>
									{box.rows.map((r) => (
										<tr key={r.id}>
											<td className="font-medium">{r.keyword}</td>
											<td className="is-num" style={{ color: 'var(--ars-ink-3)' }}>
												#{r.position?.toFixed(1)}
											</td>
											<td className="is-num">
												<Change value={r.change} />
											</td>
										</tr>
									))}
								</tbody>
							</table></div>
						)}
					</Card>
				))}
			</div>
		</div>
	);
}
