import {
    flexRender,
    getCoreRowModel,
    getFilteredRowModel,
    getPaginationRowModel,
    getSortedRowModel,
    createColumnHelper,
    useReactTable,
} from '@tanstack/react-table';
import { ArrowDown, ArrowUp, ArrowUpDown } from 'lucide-react';
import { useEffect, useMemo, useState } from 'react';
import { useLanguage } from '@/contexts/language-context';

import { Badge } from '@/components/ui/badge';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import {
    Table,
    TableBody,
    TableCell,
    TableHead,
    TableHeader,
    TableRow,
} from '@/components/ui/table';
import { cn } from '@/lib/utils';
import { formatPermissionLabel } from '@/lib/permission-labels';
import { createHiddenIdColumn, newestFirstSorting } from '@/lib/table-defaults';

const columnHelper = createColumnHelper();

function SortButton({ column, children }) {
    const sorted = column.getIsSorted();

    return (
        <Button
            type="button"
            variant="ghost"
            size="sm"
            className="-ms-2 h-8 font-medium"
            onClick={column.getToggleSortingHandler()}
        >
            {children}
            {sorted === 'asc' ? (
                <ArrowUp className="ms-1 size-4 opacity-70" />
            ) : sorted === 'desc' ? (
                <ArrowDown className="ms-1 size-4 opacity-70" />
            ) : (
                <ArrowUpDown className="text-muted-foreground ms-1 size-4 opacity-50" />
            )}
        </Button>
    );
}

function RolePermissionsCell({ role, allPermissions, labels, locale }) {
    const assigned = useMemo(
        () => new Set(Array.isArray(role.permissions) ? role.permissions : []),
        [role.permissions],
    );

    if (!allPermissions?.length) {
        if (assigned.size === 0) {
            return (
                <span className="text-muted-foreground text-sm">
                    {labels.noPermissions}
                </span>
            );
        }

        return (
            <div className="flex flex-wrap gap-1.5 py-0.5">
                {[...assigned].sort((a, b) => a.localeCompare(b)).map((name) => (
                    <Badge
                        key={name}
                        variant="secondary"
                        className="max-w-full whitespace-normal font-normal"
                        title={formatPermissionLabel(name, locale)}
                    >
                        {formatPermissionLabel(name, locale)}
                    </Badge>
                ))}
            </div>
        );
    }

    return (
        <div className="flex flex-wrap gap-1.5 py-0.5">
            {allPermissions.map((name) => {
                const granted = assigned.has(name);
                const label = formatPermissionLabel(name, locale);

                return (
                    <Badge
                        key={name}
                        variant={granted ? 'secondary' : 'outline'}
                        className={cn(
                            'max-w-full whitespace-normal font-normal',
                            !granted &&
                                'text-muted-foreground/70 border-dashed opacity-60',
                        )}
                        title={
                            granted
                                ? `${label} (${labels.permissionGranted})`
                                : `${label} (${labels.permissionNotGranted})`
                        }
                    >
                        {label}
                    </Badge>
                );
            })}
        </div>
    );
}

export function RolesDataTable({
    roles,
    allPermissions = [],
    pendingId,
    onStatusChange,
    labels,
    allowStatusToggle = true,
}) {
    const { locale } = useLanguage();
    const [sorting, setSorting] = useState(newestFirstSorting);
    const [columnVisibility, setColumnVisibility] = useState({ id: false });
    const [globalFilter, setGlobalFilter] = useState('');
    const [pagination, setPagination] = useState({
        pageIndex: 0,
        pageSize: 10,
    });

    useEffect(() => {
        setPagination((prev) => ({ ...prev, pageIndex: 0 }));
    }, [globalFilter, sorting]);

    const columns = useMemo(
        () => [
            createHiddenIdColumn(columnHelper),
            columnHelper.display({
                id: 'serial',
                header: labels.colSerial,
                cell: ({ row, table }) => {
                    const { pageIndex, pageSize } = table.getState().pagination;
                    const serial = pageIndex * pageSize + row.index + 1;

                    return (
                        <span className="text-muted-foreground tabular-nums">
                            {serial}
                        </span>
                    );
                },
                enableSorting: false,
                enableGlobalFilter: false,
            }),
            columnHelper.accessor('name', {
                header: ({ column }) => (
                    <SortButton column={column}>{labels.colName}</SortButton>
                ),
                cell: (info) => (
                    <span className="font-medium">{info.getValue()}</span>
                ),
            }),
            columnHelper.accessor('permissions_label', {
                id: 'permissions',
                header: ({ column }) => (
                    <SortButton column={column}>
                        {labels.colPermissions}
                    </SortButton>
                ),
                cell: (info) => (
                    <RolePermissionsCell
                        role={info.row.original}
                        allPermissions={allPermissions}
                        labels={labels}
                        locale={locale}
                    />
                ),
                sortingFn: 'alphanumeric',
            }),
            columnHelper.accessor('is_active', {
                header: ({ column }) => (
                    <SortButton column={column}>{labels.colStatus}</SortButton>
                ),
                cell: (info) => {
                    const active = info.getValue();

                    return (
                        <span
                            className={cn(
                                'inline-flex rounded-full px-2 py-0.5 text-xs font-medium',
                                active
                                    ? 'bg-emerald-500/15 text-emerald-700 dark:text-emerald-400'
                                    : 'bg-muted text-muted-foreground',
                            )}
                        >
                            {active
                                ? labels.statusActive
                                : labels.statusInactive}
                        </span>
                    );
                },
                sortingFn: (rowA, rowB) => {
                    const a = rowA.original.is_active ? 1 : 0;
                    const b = rowB.original.is_active ? 1 : 0;

                    return a - b;
                },
            }),
            columnHelper.display({
                id: 'action',
                header: labels.colAction,
                cell: ({ row }) => {
                    const r = row.original;

                    if (!r.can_toggle_status) {
                        return (
                            <span className="text-muted-foreground text-sm">—</span>
                        );
                    }

                    if (!allowStatusToggle) {
                        return (
                            <span className="text-muted-foreground text-sm">—</span>
                        );
                    }

                    return (
                        <Button
                            type="button"
                            variant="outline"
                            size="sm"
                            disabled={pendingId === r.id}
                            onClick={() => onStatusChange(r)}
                        >
                            {r.is_active
                                ? labels.deactivate
                                : labels.activate}
                        </Button>
                    );
                },
                enableSorting: false,
                enableGlobalFilter: false,
            }),
        ],
        [allPermissions, labels, locale, pendingId, onStatusChange, allowStatusToggle],
    );

    // eslint-disable-next-line react-hooks/incompatible-library -- TanStack Table API is not memoization-safe
    const table = useReactTable({
        data: roles,
        columns,
        state: {
            sorting,
            columnVisibility,
            globalFilter,
            pagination,
        },
        onSortingChange: setSorting,
        onColumnVisibilityChange: setColumnVisibility,
        onGlobalFilterChange: setGlobalFilter,
        onPaginationChange: setPagination,
        getCoreRowModel: getCoreRowModel(),
        getSortedRowModel: getSortedRowModel(),
        getFilteredRowModel: getFilteredRowModel(),
        getPaginationRowModel: getPaginationRowModel(),
        globalFilterFn: (row, _columnId, filterValue) => {
            const q = String(filterValue ?? '').toLowerCase().trim();

            if (!q) {
                return true;
            }

            const r = row.original;

            if (String(r.name).toLowerCase().includes(q)) {
                return true;
            }

            if (String(r.permissions_label).toLowerCase().includes(q)) {
                return true;
            }

            if (
                Array.isArray(r.permissions) &&
                r.permissions.some((p) =>
                    String(p).toLowerCase().includes(q),
                )
            ) {
                return true;
            }

            return false;
        },
    });

    const pageCount = table.getPageCount();
    const pageIndex = table.getState().pagination.pageIndex;
    const pageSize = table.getState().pagination.pageSize;
    const summaryText = labels.paginationSummary
        .replace('{page}', String(pageIndex + 1))
        .replace('{pages}', String(Math.max(pageCount, 1)));

    return (
        <div className="space-y-4">
            <div className="flex max-w-sm flex-col gap-2">
                <Input
                    type="search"
                    value={globalFilter ?? ''}
                    onChange={(e) => setGlobalFilter(e.target.value)}
                    placeholder={labels.searchPlaceholder}
                    className="h-9"
                />
            </div>

            <Table>
                <TableHeader>
                    {table.getHeaderGroups().map((headerGroup) => (
                        <TableRow key={headerGroup.id}>
                            {headerGroup.headers.map((header) => (
                                <TableHead key={header.id}>
                                    {header.isPlaceholder
                                        ? null
                                        : flexRender(
                                              header.column.columnDef.header,
                                              header.getContext(),
                                          )}
                                </TableHead>
                            ))}
                        </TableRow>
                    ))}
                </TableHeader>
                <TableBody>
                    {table.getRowModel().rows.length === 0 ? (
                        <TableRow>
                            <TableCell
                                colSpan={columns.length}
                                className="text-muted-foreground h-24 text-center"
                            >
                                {labels.empty}
                            </TableCell>
                        </TableRow>
                    ) : (
                        table.getRowModel().rows.map((row) => (
                            <TableRow key={row.id}>
                                {row.getVisibleCells().map((cell) => (
                                    <TableCell
                                        key={cell.id}
                                        className="whitespace-normal align-middle"
                                    >
                                        {flexRender(
                                            cell.column.columnDef.cell,
                                            cell.getContext(),
                                        )}
                                    </TableCell>
                                ))}
                            </TableRow>
                        ))
                    )}
                </TableBody>
            </Table>

            {roles.length > 0 ? (
                <div className="flex flex-col gap-4 border-t border-border pt-4 sm:flex-row sm:items-center sm:justify-between">
                    <div className="flex flex-wrap items-center gap-2">
                        <Label
                            htmlFor="roles-page-size"
                            className="text-muted-foreground whitespace-nowrap text-sm"
                        >
                            {labels.rowsPerPage}
                        </Label>
                        <select
                            id="roles-page-size"
                            className="border-input bg-background h-9 rounded-md border px-2 text-sm shadow-xs outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50"
                            value={pageSize}
                            onChange={(e) => {
                                const next = Number(e.target.value);
                                setPagination({
                                    pageIndex: 0,
                                    pageSize: next,
                                });
                            }}
                        >
                            {[5, 10, 25, 50].map((n) => (
                                <option key={n} value={n}>
                                    {n}
                                </option>
                            ))}
                        </select>
                    </div>
                    <div className="flex flex-wrap items-center gap-2">
                        <Button
                            type="button"
                            variant="outline"
                            size="sm"
                            onClick={() => table.previousPage()}
                            disabled={!table.getCanPreviousPage()}
                        >
                            {labels.paginationPrevious}
                        </Button>
                        <span className="text-muted-foreground min-w-[8rem] text-center text-sm tabular-nums">
                            {summaryText}
                        </span>
                        <Button
                            type="button"
                            variant="outline"
                            size="sm"
                            onClick={() => table.nextPage()}
                            disabled={!table.getCanNextPage()}
                        >
                            {labels.paginationNext}
                        </Button>
                    </div>
                </div>
            ) : null}
        </div>
    );
}
