import React, { useMemo, isValidElement, cloneElement, Children, PropsWithChildren } from "react";
import {
    useReactTable,
    getCoreRowModel,
    type ColumnDef
} from "@tanstack/react-table";
import { useTranslations } from "next-intl";
import { Checkbox } from "@/components/ui/checkbox";
import { DatasetTable, DatasetTableProps } from "@/components/ui/dataset/dataset-table";
import { DatasetColProps } from "@/components/ui/dataset/dataset-col";
import { DatasetContext, DatasetContextType, DatasetItem } from "@/components/ui/dataset/dataset-provider";
import { cn } from "@/lib/utils/cn";

type Props<T = DatasetItem> = PropsWithChildren<{
    className?: string;
    selection?: boolean;
    view?: "table" | "grid";
    data: T[];
    total: number;
    page: number;
    perPage: number;
    orderBy?: string;
    orderDir?: string;
    onOrderByChange?: (orderBy?: string) => void;
    onOrderDirChange?: (orderDir?: "asc" | "desc") => void;
    onPerPageChange?: (perPage: number) => void;
    onPageChange?: (page: number) => void;
}>;

export const Dataset = <T extends object = object>(props: Props<T>) => {
    const t = useTranslations("dashboard.general.dataset.selection");
    const {
        className,
        selection,
        view = "table",
        data,
        total,
        page,
        perPage,
        orderBy,
        orderDir,
        children,
        onOrderByChange,
        onOrderDirChange,
        onPageChange,
        onPerPageChange
    } = props;

    const columns = useMemo<ColumnDef<T>[]>(() => {
        const columns: ColumnDef<T>[] = [];

        if(selection) {
            columns.push({
                id: "__select__",
                header({ table }) {
                    return (
                        <Checkbox
                          className="translate-y-0.5"
                          aria-label={t("selectAll")}
                          checked={table.getIsAllPageRowsSelected() || (table.getIsSomePageRowsSelected() && "indeterminate")}
                          onCheckedChange={(value) => table.toggleAllPageRowsSelected(!!value)} />
                    );
                },
                cell: ({ row }) => {
                    return (
                        <Checkbox
                          className={cn("translate-y-0.5", { "absolute top-1 left-1 z-1": view === "grid" })}
                          aria-label={t("selectRow")}
                          checked={row.getIsSelected()}
                          onCheckedChange={(value) => row.toggleSelected(!!value)} />
                    );
                },
                enableSorting: false,
                enableHiding: false,
                size: 40
            });
        }

        Children.map(children, (child) => {
            if(!isValidElement(child) || child.type !== DatasetTable) {
                return;
            }

            if(child.type === DatasetTable && child.props) {
                Children.map((child.props as DatasetTableProps).children, (child, index) => {
                    if(!isValidElement(child)) {
                        return;
                    }

                    const {
                        colSpan,
                        enableSorting = false,
                        name,
                        label
                    } = child.props as DatasetColProps<T, keyof T>;

                    columns.push({
                        id: name ? String(name) : `col-${index}`,
                        enableSorting,
                        size: colSpan,
                        accessorKey: name ? String(name) : undefined,
                        header: label ?? (name ? String(name) : undefined) ?? "",
                        cell: () => {
                            return cloneElement(child);
                        }
                    });
                });
            }
        });

        return columns;
    }, [children, view, t, selection]);

    const sorting = useMemo(() => {
        return orderBy && orderDir ? [
            {
                id: orderBy,
                desc: orderDir === "desc"
            }
        ] : [];
    }, [orderBy, orderDir]);

    const pagination = useMemo(() => {
        return {
            pageIndex: page,
            pageSize: perPage
        };
    }, [page, perPage]);
    const table = useReactTable<T>({
        columns,
        data,
        state: {
            pagination,
            sorting
        },
        pageCount: total > 0 ? Math.ceil(total / perPage) : 0,
        getCoreRowModel: getCoreRowModel(),
        enableRowSelection: true,
        manualSorting: true,
        manualPagination: true,
        manualFiltering: true,
        getRowId: (row) => (row as unknown as { id: string }).id,
        onSortingChange(updater) {
            const [sort] = typeof updater === "function" ? updater(sorting) : updater;

            if(sort) {
                if(sort.id !== orderBy && onOrderByChange) {
                    onOrderByChange(sort.id);
                }

                if(orderDir !== (sort.desc ? "desc" : "asc") && onOrderDirChange) {
                    onOrderDirChange(sort.desc ? "desc" : "asc");
                }
            }
            else {
                if(onOrderByChange) {
                    onOrderByChange("");
                }

                if(onOrderDirChange) {
                    onOrderDirChange("asc");
                }
            }
        },
        onPaginationChange(updater) {
            const {
                pageIndex,
                pageSize
            } = typeof updater === "function" ? updater(pagination) : updater;

            if(pagination.pageIndex !== pageIndex && onPageChange) {
                onPageChange(pageIndex);
            }

            if(pagination.pageSize !== pageSize && onPerPageChange) {
                onPerPageChange(pageSize);
            }
        }
    });

    return (
        <DatasetContext.Provider
          value={{
            selectable: selection,
            view,
            data,
            total,
            table
          } as DatasetContextType}>
            <div className={cn("flex w-full flex-col gap-2.5 py-1", className)}>
                {children}
            </div>
        </DatasetContext.Provider>
    );
};
