import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { __, _n, sprintf } from '@wordpress/i18n';
import { api } from '../api';

/**
 * The enterprise data grid: one table for every large dataset.
 *
 * Everything the server can do better, the server does — searching, sorting,
 * filtering, counting, paging. The browser only ever holds one page. This is
 * the design difference between this product and the WP-List-Tables the
 * incumbents still ship: a toolbar that reads like a sentence (search, filter
 * chips, live total), a header that sorts on click and says so, skeleton rows
 * while loading, honest empty/error states, and a footer that always tells you
 * where you are in the dataset.
 *
 * Drawn entirely from design tokens: correct in light, dark and RTL.
 */

/** Server list state — mirrors the ListQuery contract on the PHP side. */
export function useServerQuery(endpoint, { perPage = 50, orderby = '', order = 'desc', filters = {} } = {}) {
	const [state, setState] = useState({
		page: 1,
		perPage,
		search: '',
		orderby,
		order,
		filters,
	});
	const [rows, setRows] = useState(null); // null = loading, [] = empty
	const [total, setTotal] = useState(0);
	const [pages, setPages] = useState(0);
	const [error, setError] = useState(null);
	// One in-flight request wins: a stale slow response must not overwrite a
	// fresh fast one after rapid filter clicks.
	const seq = useRef(0);
	// Debounce only the search; page/sort/filter changes fetch immediately.
	const searchTimer = useRef(null);
	const [liveSearch, setLiveSearch] = useState('');

	const fetchPage = useCallback(() => {
		const mine = ++seq.current;
		setError(null);
		const params = new URLSearchParams();
		params.set('page', String(state.page));
		params.set('per_page', String(state.perPage));
		if (state.search) params.set('search', state.search);
		if (state.orderby) {
			params.set('orderby', state.orderby);
			params.set('order', state.order);
		}
		Object.entries(state.filters).forEach(([k, v]) => {
			if (v !== '' && v !== null && v !== undefined) params.set(k, String(v));
		});
		api
			.get(`${endpoint}?${params.toString()}`)
			.then((r) => {
				if (mine !== seq.current) return;
				setRows(r.items || []);
				setTotal(r.pagination?.total_items ?? (r.items || []).length);
				setPages(r.pagination?.total_pages ?? 1);
			})
			.catch((e) => {
				if (mine !== seq.current) return;
				setError(e.message || __('Request failed', 'ai-rank-suite'));
				setRows([]);
			});
	}, [endpoint, state]);

	useEffect(() => {
		fetchPage();
	}, [fetchPage]);

	const setSearch = (value) => {
		setLiveSearch(value);
		window.clearTimeout(searchTimer.current);
		searchTimer.current = window.setTimeout(() => {
			setState((s) => ({ ...s, search: value, page: 1 }));
		}, 350);
	};

	return {
		rows,
		total,
		pages,
		error,
		state,
		liveSearch,
		setSearch,
		setPage: (page) => setState((s) => ({ ...s, page })),
		setPerPage: (pp) => setState((s) => ({ ...s, perPage: pp, page: 1 })),
		sortBy: (col) =>
			setState((s) => ({
				...s,
				orderby: col,
				order: s.orderby === col && s.order === 'desc' ? 'asc' : 'desc',
				page: 1,
			})),
		setFilter: (key, value) => setState((s) => ({ ...s, filters: { ...s.filters, [key]: value }, page: 1 })),
		resetFilters: () =>
			setState((s) => ({ ...s, search: '', filters: {}, page: 1 })) || setLiveSearch(''),
		refresh: fetchPage,
	};
}

function SortCaret({ dir }) {
	return (
		<svg className="ars-grid-caret" viewBox="0 0 24 24" width="12" height="12" fill="none" aria-hidden>
			<path
				d={dir === 'asc' ? 'm6 14 6-6 6 6' : 'm6 10 6 6 6-6'}
				stroke="currentColor"
				strokeWidth="2.4"
				strokeLinecap="round"
				strokeLinejoin="round"
			/>
		</svg>
	);
}

/** Shimmering placeholder rows while the server answers. */
function Skeleton({ columns, count = 8 }) {
	return Array.from({ length: count }, (_, i) => (
		<tr key={i} className="ars-grid-skeleton" aria-hidden>
			{columns.map((c) => (
				<td key={c.id}>
					<span style={{ width: `${45 + ((i * 17 + c.id.length * 13) % 40)}%` }} />
				</td>
			))}
		</tr>
	));
}

/**
 * @param {object} props
 * @param {Array<{id:string,label:string,sortable?:boolean,num?:boolean,render?:Function,narrow?:'hide'}>} props.columns
 * @param {ReturnType<typeof useServerQuery>} props.query  The server query hook.
 * @param {Function} [props.rowKey]
 * @param {Function} [props.rowActions]  Renders trailing action cell per row.
 * @param {object} [props.empty]  { title, children, action, onAction }
 * @param {string} [props.searchPlaceholder]
 * @param {React.ReactNode} [props.toolbar]  Extra toolbar content (filter chips).
 */
export default function DataGrid({ columns, query, rowKey = (r) => r.id, rowActions, empty, searchPlaceholder = 'Search…', toolbar }) {
	const { rows, total, pages, error, state, liveSearch, setSearch, setPage, setPerPage, sortBy, resetFilters, refresh } = query;

	const activeFilters = useMemo(
		() => Object.entries(state.filters).filter(([, v]) => v !== '' && v !== null && v !== undefined),
		[state.filters]
	);
	const loading = rows === null;
	const from = total === 0 ? 0 : (state.page - 1) * state.perPage + 1;
	const to = Math.min(total, state.page * state.perPage);

	return (
		<div className="ars-grid">
			<div className="ars-grid-toolbar">
				<div className="ars-grid-search">
					<svg viewBox="0 0 24 24" width="14" height="14" fill="none" aria-hidden>
						<circle cx="11" cy="11" r="7" stroke="currentColor" strokeWidth="2" />
						<path d="m20 20-3.5-3.5" stroke="currentColor" strokeWidth="2" strokeLinecap="round" />
					</svg>
					<input
						type="search"
						value={liveSearch}
						placeholder={searchPlaceholder}
						onChange={(e) => setSearch(e.target.value)}
						aria-label={searchPlaceholder}
					/>
				</div>
				{toolbar}
				{(activeFilters.length > 0 || state.search) && (
					<button type="button" className="ars-grid-reset" onClick={resetFilters}>{__('Reset filters', 'ai-rank-suite')}</button>
				)}
				<span className="ars-grid-total" aria-live="polite">
					{loading ? '…' : sprintf(__('%s total', 'ai-rank-suite'), total.toLocaleString())}
				</span>
			</div>

			<div className="ars-grid-scroll">
				<table className="ars-table ars-grid-table">
					<thead>
						<tr>
							{columns.map((c) => (
								<th key={c.id} className={c.num ? 'num' : ''} aria-sort={state.orderby === c.id ? (state.order === 'asc' ? 'ascending' : 'descending') : undefined}>
									{c.sortable ? (
										<button type="button" className="ars-grid-sort" onClick={() => sortBy(c.id)}>
											{c.label}
											{state.orderby === c.id ? <SortCaret dir={state.order} /> : null}
										</button>
									) : (
										c.label
									)}
								</th>
							))}
							{rowActions && <th aria-label={__('Actions', 'ai-rank-suite')} />}
						</tr>
					</thead>
					<tbody>
						{loading ? (
							<Skeleton columns={columns} />
						) : error ? (
							<tr>
								<td colSpan={columns.length + (rowActions ? 1 : 0)}>
									<div className="ars-grid-state">
										<strong>{error}</strong>
										<button type="button" className="ars-linkbtn" onClick={refresh}>{__('Try again', 'ai-rank-suite')}</button>
									</div>
								</td>
							</tr>
						) : rows.length === 0 ? (
							<tr>
								<td colSpan={columns.length + (rowActions ? 1 : 0)}>
									<div className="ars-grid-state">
										<strong>{empty?.title || __('Nothing here', 'ai-rank-suite')}</strong>
										{empty?.children && <span>{empty.children}</span>}
										{(activeFilters.length > 0 || state.search) && (
											<button type="button" className="ars-linkbtn" onClick={resetFilters}>{__('Clear filters', 'ai-rank-suite')}</button>
										)}
									</div>
								</td>
							</tr>
						) : (
							rows.map((row) => (
								<tr key={rowKey(row)}>
									{columns.map((c) => (
										<td key={c.id} className={`${c.num ? 'num' : ''}${c.id === columns[0].id ? ' name' : ''}`}>
											{c.render ? c.render(row) : row[c.id]}
										</td>
									))}
									{rowActions && <td className="ars-grid-actions">{rowActions(row)}</td>}
								</tr>
							))
						)}
					</tbody>
				</table>
			</div>

			{!loading && !error && total > 0 && (
				<div className="ars-grid-footer">
					<label className="ars-grid-perpage">{__('Rows', 'ai-rank-suite')}<select value={state.perPage} onChange={(e) => setPerPage(Number(e.target.value))}>
							{[25, 50, 100].map((n) => (
								<option key={n} value={n}>
									{n}
								</option>
							))}
						</select>
					</label>
					<span className="ars-grid-range">
						{from.toLocaleString()}–{to.toLocaleString()} of {total.toLocaleString()}
					</span>
					<div className="ars-grid-pager">
						<button type="button" disabled={state.page <= 1} onClick={() => setPage(state.page - 1)} aria-label={__('Previous page', 'ai-rank-suite')}>
							‹
						</button>
						<span>
							{state.page} / {Math.max(1, pages)}
						</span>
						<button type="button" disabled={state.page >= pages} onClick={() => setPage(state.page + 1)} aria-label={__('Next page', 'ai-rank-suite')}>
							›
						</button>
					</div>
				</div>
			)}
		</div>
	);
}
