import type { ReactNode } from 'react';
import {
    ArrowPathIcon,
    ChevronDownIcon,
    ChevronLeftIcon,
    ChevronRightIcon,
    ChevronUpIcon,
} from '@heroicons/react/24/solid';
import { ArrowsUpDownIcon } from '@heroicons/react/24/outline';
import { Button } from './Button';
import { Dropdown } from './Dropdown';
import { Label } from './Label';
import type { SortState } from '../types/sort';

type HeaderValue<T> = ReactNode | ((row: T) => ReactNode);

export type TableHeader<T> = {
    label: string;
    id: string;
    value: HeaderValue<T>;
    sortable?: boolean;
    sortField?: string;
    headerClassName?: string;
    cellClassName?: string;
};

type TableProps<T> = {
    headers: TableHeader<T>[];
    data: T[];
    rowKey: (row: T, index: number) => string;
    isLoading?: boolean;
    emptyMessage?: string;
    className?: string;
    sorting?: SortState | null;
    onSortChange?: (field: string) => void;
    pagination?: {
        page: number;
        pageSize: number;
        total: number;
        totalPages: number;
        onPageChange: (page: number) => void;
        onPageSizeChange?: (pageSize: number) => void;
    };
};

export function Table<T>({
    headers,
    data,
    rowKey,
    isLoading = false,
    emptyMessage = 'No data to show.',
    className = '',
    sorting = null,
    onSortChange,
    pagination,
}: Readonly<TableProps<T>>) {
    const canGoPrev = pagination != null && pagination.page > 1;
    const canGoNext = pagination != null && pagination.page < pagination.totalPages;

    return (
        <div className={`table-shell ${className}`.trim()}>
            <div className="table-scroll">
                <table className="table-root">
                    <thead className="table-head">
                        <tr>
                            {headers.map((header) => {
                                const canSort =
                                    header.sortable === true &&
                                    typeof onSortChange === 'function' &&
                                    typeof header.sortField === 'string' &&
                                    header.sortField.length > 0;
                                const isActiveSort = canSort && sorting?.field === header.sortField;
                                const sortDirection = isActiveSort ? sorting?.direction : null;

                                return (
                                    <th
                                        key={header.id}
                                        className={`table-head-cell ${header.headerClassName ?? ''}`.trim()}
                                    >
                                        {canSort ? (
                                            <button
                                                type="button"
                                                className="table-sort-button"
                                                onClick={() =>
                                                    onSortChange(header.sortField as string)
                                                }
                                                aria-label={`Sort by ${header.label}`}
                                            >
                                                <span>{header.label}</span>
                                                <span
                                                    className="table-sort-icon"
                                                    aria-hidden="true"
                                                    data-sort-state={sortDirection ?? 'none'}
                                                >
                                                    {sortDirection === 'asc' ? (
                                                        <ChevronUpIcon className="h-4 w-4" />
                                                    ) : null}
                                                    {sortDirection === 'desc' ? (
                                                        <ChevronDownIcon className="h-4 w-4" />
                                                    ) : null}
                                                    {sortDirection == null ? (
                                                        <ArrowsUpDownIcon className="h-4 w-4" />
                                                    ) : null}
                                                </span>
                                            </button>
                                        ) : (
                                            header.label
                                        )}
                                    </th>
                                );
                            })}
                        </tr>
                    </thead>
                    <tbody>
                        {isLoading ? (
                            <tr className="table-body-row">
                                <td colSpan={headers.length} className="px-4 py-6 text-center">
                                    <Label
                                        as="span"
                                        variant="body2"
                                        className="inline-flex items-center justify-center ui-loading"
                                    >
                                        <ArrowPathIcon
                                            className="h-5 w-5 animate-spin"
                                            aria-hidden="true"
                                        />
                                    </Label>
                                </td>
                            </tr>
                        ) : null}
                        {!isLoading && data.length === 0 ? (
                            <tr className="table-body-row">
                                <td colSpan={headers.length} className="px-4 py-6 text-center">
                                    <Label variant="body2" className="ui-empty">
                                        {emptyMessage}
                                    </Label>
                                </td>
                            </tr>
                        ) : null}
                        {!isLoading &&
                            data.map((row, index) => (
                                <tr key={rowKey(row, index)} className="table-body-row">
                                    {headers.map((header) => {
                                        const content =
                                            typeof header.value === 'function'
                                                ? (header.value as (item: T) => ReactNode)(row)
                                                : header.value;
                                        return (
                                            <td
                                                key={`${header.id}-${index}`}
                                                className={`table-cell-secondary ${header.cellClassName ?? ''}`.trim()}
                                            >
                                                {content}
                                            </td>
                                        );
                                    })}
                                </tr>
                            ))}
                    </tbody>
                </table>
            </div>
            {pagination ? (
                <div className="flex flex-col gap-3 border-t border-zinc-500/20 px-4 py-3 sm:flex-row sm:items-center sm:justify-between">
                    <Label variant="body2">{pagination.total} results</Label>
                    <div className="flex flex-wrap items-center gap-2">
                        {pagination.onPageSizeChange ? (
                            <Dropdown
                                label="Rows"
                                value={String(pagination.pageSize)}
                                choices={[5, 10, 20, 50, 100].map((size) => ({
                                    id: String(size),
                                    label: String(size),
                                }))}
                                size="sm"
                                layout="inline"
                                className="max-w-none"
                                selectClassName="rounded-lg px-2"
                                disabled={isLoading}
                                onChange={(value) => pagination.onPageSizeChange?.(Number(value))}
                            />
                        ) : null}
                        <Button
                            type="outlined"
                            size="sm"
                            icon={ChevronLeftIcon}
                            ariaLabel="Previous page"
                            disabled={!canGoPrev || isLoading}
                            onClick={() => pagination.onPageChange(pagination.page - 1)}
                        />
                        <Label variant="body2" className="px-1 text-xs ui-body-secondary">
                            Page {pagination.page} of {Math.max(pagination.totalPages, 1)}
                        </Label>
                        <Button
                            type="outlined"
                            size="sm"
                            icon={ChevronRightIcon}
                            ariaLabel="Next page"
                            disabled={!canGoNext || isLoading}
                            onClick={() => pagination.onPageChange(pagination.page + 1)}
                        />
                    </div>
                </div>
            ) : null}
        </div>
    );
}
