/**
 * Generic responsive DataList component.
 * ≥1024px → @woocommerce/components Table
 * <1024px  → card layout (2-col on ≥768px, 1-col otherwise)
 */
import React, { useMemo } from 'react';
import { __ } from '@wordpress/i18n';
import {
	Card,
	CardBody,
	Flex,
	FlexBlock,
	FlexItem,
} from '@wordpress/components';
import { Table, TablePlaceholder } from '@woocommerce/components';
import { useWindowSize } from '../hooks';

/* ------------------------------------------------------------------ */
/* Types                                                               */
/* ------------------------------------------------------------------ */

export interface DataListColumn<T> {
	key: string;
	label: React.ReactNode;
	render: (item: T) => React.ReactNode;
	/** Align right in the table header (useful for numbers). */
	isNumeric?: boolean;
	/** Screen-reader-only label for columns with icon-only headers. */
	screenReaderLabel?: string;
}

export interface DataListProps<T> {
	items: T[];
	columns: DataListColumn<T>[];
	getKey: (item: T) => string | number;
	isLoading?: boolean;
	skeletonRows?: number;
	emptyMessage?: string;
	/** Enable row selection. */
	selectable?: boolean;
	selectedIds?: number[];
	onSelectionChange?: (ids: number[]) => void;
	/** Must be provided when selectable=true. */
	getId?: (item: T) => number;
}

/* ------------------------------------------------------------------ */
/* Helpers                                                             */
/* ------------------------------------------------------------------ */

function chunk<A>(arr: A[], size: number): A[][] {
	const result: A[][] = [];
	for (let i = 0; i < arr.length; i += size) {
		result.push(arr.slice(i, i + size));
	}
	return result;
}

/* ------------------------------------------------------------------ */
/* Desktop table view                                                  */
/* ------------------------------------------------------------------ */

function DataListTable<T>({
	items,
	columns,
	getKey,
	isLoading,
	skeletonRows,
	emptyMessage,
	selectable,
	selectedIds,
	onSelectionChange,
	getId,
}: DataListProps<T>) {
	const allSelected = useMemo(
		() =>
			selectable &&
			items.length > 0 &&
			getId != null &&
			items.every((item) => (selectedIds ?? []).includes(getId(item))),
		[items, selectable, selectedIds, getId],
	);

	const someSelected = (selectedIds ?? []).length > 0;

	const handleSelectAll = () => {
		if (!onSelectionChange || !getId) return;
		if (allSelected) {
			onSelectionChange([]);
		} else {
			onSelectionChange(items.map((item) => getId(item)));
		}
	};

	const headers = useMemo(() => {
		const base = columns.map((col) => ({
			key: col.key,
			label: col.label,
			required: true as const,
			isNumeric: col.isNumeric,
			screenReaderLabel: col.screenReaderLabel,
		}));

		if (!selectable) return base;

		const selectAllCheckbox = (
			<input
				type="checkbox"
				checked={!!allSelected}
				ref={(el) => {
					if (el) el.indeterminate = someSelected && !allSelected;
				}}
				onChange={handleSelectAll}
				aria-label={__('Select all', 'parcel2go-shipping')}
			/>
		);

		return [
			{
				key: '_select',
				label: selectAllCheckbox,
				required: true as const,
				screenReaderLabel: __('Select', 'parcel2go-shipping'),
			},
			...base,
		];
	}, [columns, selectable, allSelected, someSelected]);

	const rows = useMemo(
		() =>
			items.map((item) => {
				const dataCells = columns.map((col) => ({
					display: col.render(item),
					value: 0 as string | number,
				}));

				if (!selectable || !getId || !onSelectionChange) return dataCells;

				const id = getId(item);
				const isSelected = (selectedIds ?? []).includes(id);
				const toggle = () =>
					onSelectionChange(
						isSelected
							? (selectedIds ?? []).filter((sid) => sid !== id)
							: [...(selectedIds ?? []), id],
					);

				return [
					{
						display: (
							<input
								type="checkbox"
								checked={isSelected}
								onChange={toggle}
								aria-label={__('Select row', 'parcel2go-shipping')}
							/>
						),
						value: 0 as string | number,
					},
					...dataCells,
				];
			}),
		[items, columns, selectable, selectedIds, getId, onSelectionChange],
	);

	return (
		<Card style={{ overflow: 'hidden' }}>
			<CardBody size={null}>
				{isLoading ? (
					<TablePlaceholder
						numberOfRows={skeletonRows ?? 5}
						headers={headers}
						rowHeader={selectable ? 1 : 0}
					/>
				) : (
					<Table
						headers={headers}
						rows={rows}
						rowKey={(_row: unknown, index: number) => {
							const item = items[index];
							return item != null ? getKey(item) : index;
						}}
						rowHeader={selectable ? 1 : 0}
						emptyMessage={emptyMessage ?? __('No items found.', 'parcel2go-shipping')}
					/>
				)}
			</CardBody>
		</Card>
	);
}

/* ------------------------------------------------------------------ */
/* Card view (mobile / tablet)                                         */
/* ------------------------------------------------------------------ */

function DataListCardItem<T>({
	item,
	columns,
	selectable,
	selectedIds,
	onSelectionChange,
	getId,
}: {
	item: T;
	columns: DataListColumn<T>[];
	selectable?: boolean;
	selectedIds?: number[];
	onSelectionChange?: (ids: number[]) => void;
	getId?: (item: T) => number;
}) {
	const id = getId ? getId(item) : null;
	const isSelected = id != null && (selectedIds ?? []).includes(id);

	const toggle = () => {
		if (!onSelectionChange || id == null) return;
		onSelectionChange(
			isSelected
				? (selectedIds ?? []).filter((sid) => sid !== id)
				: [...(selectedIds ?? []), id],
		);
	};

	return (
		<Card>
			<CardBody>
				<Flex direction="column" gap={3}>
					{/* First column as header row */}
					<Flex
						justify="space-between"
						align="center"
						style={{ paddingBottom: 12, borderBottom: '1px solid #ddd' }}
					>
						<FlexItem style={{ fontWeight: 600, display: 'flex', alignItems: 'center', gap: 8 }}>
							{selectable && (
								<input
									type="checkbox"
									checked={isSelected}
									onChange={toggle}
									aria-label={__('Select', 'parcel2go-shipping')}
								/>
							)}
							{columns[0] && columns[0].render(item)}
						</FlexItem>
						{columns[1] && (
							<FlexItem>
								{columns[1].render(item)}
							</FlexItem>
						)}
					</Flex>

					{/* Remaining columns as key-value rows */}
					{columns.slice(2).map((col) => (
						<Flex key={col.key} justify="space-between" align="center">
							<FlexItem style={{ color: '#50575e', fontSize: '0.9em' }}>
								{col.label}
							</FlexItem>
							<FlexItem>{col.render(item)}</FlexItem>
						</Flex>
					))}
				</Flex>
			</CardBody>
		</Card>
	);
}

function DataListCards<T>({
	items,
	columns,
	getKey,
	isLoading,
	skeletonRows,
	selectable,
	selectedIds,
	onSelectionChange,
	getId,
}: DataListProps<T> & { cardColumns: 1 | 2 }) {
	const cardColumns = useWindowSize().isSmOrUp ? 2 : 1;

	if (isLoading) {
		const count = cardColumns === 2 ? 4 : (skeletonRows ?? 3);
		const rows = chunk(
			Array.from({ length: count }, (_, i) => i),
			cardColumns,
		);
		return (
			<Flex direction="column" gap={4}>
				{rows.map((row, rowIdx) => (
					<Flex key={rowIdx} direction="row" gap={3} wrap>
						{row.map((i) => (
							<FlexBlock key={i}>
								<Card>
									<CardBody>
										<Flex direction="column" gap={3}>
											{[1, 2, 3, 4, 5].map((j) => (
												<Flex key={j} justify="space-between" align="center">
													<FlexItem style={{ width: 80, height: 14, background: '#eee', borderRadius: 2 }} />
													<FlexItem style={{ width: 100, height: 14, background: '#eee', borderRadius: 2 }} />
												</Flex>
											))}
										</Flex>
									</CardBody>
								</Card>
							</FlexBlock>
						))}
					</Flex>
				))}
			</Flex>
		);
	}

	if (!items.length) return null;

	const rows = chunk(items, cardColumns);

	return (
		<Flex direction="column" gap={4}>
			{rows.map((row, rowIdx) => (
				<Flex key={rowIdx} direction="row" gap={3} wrap>
					{row.map((item) => (
						<FlexBlock key={getKey(item)}>
							<DataListCardItem
								item={item}
								columns={columns}
								selectable={selectable}
								selectedIds={selectedIds}
								onSelectionChange={onSelectionChange}
								getId={getId}
							/>
						</FlexBlock>
					))}
				</Flex>
			))}
		</Flex>
	);
}

/* ------------------------------------------------------------------ */
/* DataList (public)                                                   */
/* ------------------------------------------------------------------ */

function DataListInner<T>(props: DataListProps<T>) {
	const { isMdOrUp, isSmOrUp } = useWindowSize();

	if (isMdOrUp) {
		return <DataListTable {...props} />;
	}

	return <DataListCards {...props} cardColumns={isSmOrUp ? 2 : 1} />;
}

// Wrap in React.memo — generic memo needs a cast
const DataList = React.memo(DataListInner) as typeof DataListInner;
export default DataList;
