import {
    getCoreRowModel,
    getFacetedMinMaxValues,
    getFacetedRowModel,
    getFacetedUniqueValues,
    getFilteredRowModel,
    getPaginationRowModel,
    getSortedRowModel,
    useReactTable,
} from '@tanstack/react-table';
import { useEffect, useRef, useState } from 'react';

// needed for table body level scope DnD setup
import DraggableTableHead from '@/components/data-table/DraggableTableHead';
import {
    DndContext,
    KeyboardSensor,
    MouseSensor,
    TouchSensor,
    closestCenter,
    useSensor,
    useSensors,
} from '@dnd-kit/core';
import { restrictToHorizontalAxis } from '@dnd-kit/modifiers';
import {
    SortableContext,
    arrayMove,
    horizontalListSortingStrategy,
} from '@dnd-kit/sortable';

import Cell from '@/components/data-table/Cell';
import ExportFile from '@/components/data-table/ExportFile';
import TableFooter from '@/components/data-table/TableFooter';
import TableGlobalSearch from '@/components/data-table/TableGlobalSearch';
import { fuzzyFilter } from '@/components/data-table/fuzzyFilter';

function DataTable({
    data,
    columns,
    exportVisible = true,
    searchVisible = true,
    footerVisible = true,
    data_key = [],
    t_row,
    loading,
    extraButton = <div></div>,
    style,
    manualPagination = false,
    enableGlobalFilter = true,
    manualSearchField = null,
    manualSearchText = '',
    setManualSearchText = () => {},
}) {
    const [columnFilters, setColumnFilters] = useState([]);
    const [globalFilter, setGlobalFilter] = useState('');

    const [columnOrder, setColumnOrder] = useState(() =>
        columns.map(c => c.id),
    );
    const [innerWidth, setInnerWidth] = useState(0);
    const divRef = useRef(null);

    // get the inner size to make table scrollable while overflowed out of screen
    useEffect(() => {
        const updateInnerWidth = () => {
            if (divRef.current) {
                const width = window.innerWidth;
                setInnerWidth(width);
            }
        };

        // Call the update function initially and whenever the window is resized
        updateInnerWidth();
        window.addEventListener('resize', updateInnerWidth);

        // Clean up the event listener when component unmounts
        return () => {
            window.removeEventListener('resize', updateInnerWidth);
        };
    }, []);

    // table instance
    const table = useReactTable({
        data,
        columns,
        filterFns: {
            fuzzy: fuzzyFilter,
        },
        state: {
            columnFilters,
            globalFilter,
            columnOrder,
        },
        onColumnFiltersChange: setColumnFilters,
        onColumnOrderChange: setColumnOrder,
        onGlobalFilterChange: setGlobalFilter,
        globalFilterFn: fuzzyFilter,
        getCoreRowModel: getCoreRowModel(),
        getFilteredRowModel: getFilteredRowModel(),
        getSortedRowModel: getSortedRowModel(),
        getPaginationRowModel: getPaginationRowModel(),
        getFacetedRowModel: getFacetedRowModel(),
        getFacetedUniqueValues: getFacetedUniqueValues(),
        getFacetedMinMaxValues: getFacetedMinMaxValues(),
        enableColumnResizing: true,
        manualPagination: manualPagination,
        enableGlobalFilter: enableGlobalFilter,
        columnResizeMode: 'onChange',
        debugTable: false,
        debugHeaders: false,
        debugColumns: false,
    });

    // get the filtered data
    const filteredData = table.getSortedRowModel().rows.map(d => d.original);

    // get the specific fields to export
    const exportData = filteredData?.map(item => {
        const filteredItem = {};
        data_key.forEach(k => {
            if (item?.hasOwnProperty(k)) {
                filteredItem[k] = item[k];
            }
        });
        return filteredItem;
    });

    // reorder columns after drag & drop
    function handleDragEnd(event) {
        const { active, over } = event;
        if (active && over && active.id !== over.id) {
            setColumnOrder(columnOrder => {
                const oldIndex = columnOrder.indexOf(active.id);
                const newIndex = columnOrder.indexOf(over.id);
                return arrayMove(columnOrder, oldIndex, newIndex); // this is just a splice util
            });
        }
    }

    // used for drag and drop
    const sensors = useSensors(
        useSensor(MouseSensor, {}),
        useSensor(TouchSensor, {}),
        useSensor(KeyboardSensor, {}),
    );

    // used for drag and drop
    useEffect(() => {
        if (table.getState().columnFilters[0]?.id === 'fullName') {
            if (table.getState().sorting[0]?.id !== 'fullName') {
                table.setSorting([{ id: 'fullName', desc: false }]);
            }
        }
    }, [table.getState().columnFilters[0]?.id]);

    return (
        <div ref={divRef} className="p-2">
            {/* global_search & export file */}
            <div className="flex justify-between gap-x-2">
                {/* file export  */}
                <div className="flex sm:flex-row flex-col sm:items-center items-start gap-2">
                    {exportVisible ? (
                        <ExportFile
                            data={exportData}
                            columnOrder={columns}
                            table={table}
                        />
                    ) : (
                        <div></div>
                    )}
                    {extraButton}
                </div>

                {/* global_search_box  */}
                <div className="flex sm:flex-row flex-col sm:items-center items-start gap-2">
                    {manualSearchField && (
                        <TableGlobalSearch
                            value={manualSearchText ?? ''}
                            onChange={value =>
                                setManualSearchText(String(value))
                            }
                            className="max-w-[250px] w-full"
                            placeholder="Search Globally ..."
                        />
                    )}
                    {searchVisible && (
                        <TableGlobalSearch
                            value={globalFilter ?? ''}
                            onChange={value => setGlobalFilter(String(value))}
                            className="max-w-[250px] w-full"
                            placeholder="Search Globally ..."
                        />
                    )}
                </div>
            </div>
            <div
                className="overflow-hidden rounded-lg my-4"
                style={{
                    width: style
                        ? style
                        : innerWidth > 992
                          ? `${innerWidth - 350}px`
                          : '100%',
                }}>
                <div className="w-full overflow-auto rounded-lg border">
                    <DndContext
                        collisionDetection={closestCenter}
                        modifiers={[restrictToHorizontalAxis]}
                        // eslint-disable-next-line react/jsx-no-bind
                        onDragEnd={handleDragEnd}
                        sensors={sensors}>
                        <table className="w-full rounded-lg" id="table">
                            <thead className="w-full bg-secondary">
                                {table.getHeaderGroups().map(headerGroup => (
                                    <tr key={headerGroup.id}>
                                        <th className="w-1 md:px-2 sm:px-1 px-0.5 hover:bg-base-150 text-base-400 font-semibold sm:text-xs xmd:text-sm text-[8px]">
                                            SL
                                        </th>
                                        <SortableContext
                                            items={columnOrder}
                                            strategy={
                                                horizontalListSortingStrategy
                                            }>
                                            {headerGroup.headers.map(header => (
                                                <DraggableTableHead
                                                    key={header.id}
                                                    header={header}
                                                    table={table}
                                                />
                                            ))}
                                        </SortableContext>
                                    </tr>
                                ))}
                            </thead>
                            {!loading && data?.length > 0 ? (
                                <tbody>
                                    {filteredData?.length > 0 ? (
                                        table.getRowModel().rows.map(row => {
                                            return (
                                                <tr
                                                    className={`border-t border-base-200 hover:bg-secondary/30`}
                                                    key={row.id}>
                                                    <td className="w-fit text-center py-2 text-base-400 3xl:text-base xmd:text-sm sm:text-xs text-[8px]">
                                                        {row?.index + 1}
                                                    </td>
                                                    {row
                                                        .getVisibleCells()
                                                        .map(cell => {
                                                            return (
                                                                <SortableContext
                                                                    key={
                                                                        cell.id
                                                                    }
                                                                    items={
                                                                        columnOrder
                                                                    }
                                                                    strategy={
                                                                        horizontalListSortingStrategy
                                                                    }>
                                                                    <Cell
                                                                        cell={
                                                                            cell
                                                                        }
                                                                        globalFilter={
                                                                            globalFilter
                                                                        }
                                                                    />
                                                                </SortableContext>
                                                            );
                                                        })}
                                                </tr>
                                            );
                                        })
                                    ) : (
                                        <tr>
                                            <td
                                                colSpan={
                                                    table?.getAllColumns()
                                                        ?.length + 1
                                                }
                                                className="py-2.5 text-center text-base-400">
                                                No Data Found!
                                            </td>
                                        </tr>
                                    )}
                                    {t_row}
                                </tbody>
                            ) : !loading ? (
                                <tbody>
                                    <tr>
                                        <td
                                            colSpan={
                                                table?.getAllColumns()?.length +
                                                1
                                            }
                                            className="py-2.5 text-center text-base-400 3xl:text-base xmd:text-sm sm:text-xs text-[8px]">
                                            No Data Found!
                                        </td>
                                    </tr>
                                </tbody>
                            ) : (
                                <tbody className="animate-pulse">
                                    <tr className="border-b">
                                        <td className="py-3 text-left pl-2 xmd:pr-5 sm:pr-3 pr-0 text-base-400 3xl:text-base xmd:text-sm sm:text-xs text-[8px]">
                                            <div className="xmd:h-6 sm:h-5 h-4 w-5 bg-slate-200 rounded"></div>
                                        </td>
                                        {table.getAllColumns()?.map(d => (
                                            <td
                                                key={d.id}
                                                className="py-3 text-left pl-2 xmd:pr-5 sm:pr-3 pr-0 text-base-400 3xl:text-base xmd:text-sm sm:text-xs text-[8px]">
                                                <div className="xmd:h-6 sm:h-5 h-4 bg-slate-200 rounded"></div>
                                            </td>
                                        ))}
                                    </tr>
                                    <tr className="border-b">
                                        <td className="py-3 text-left pl-2 xmd:pr-5 sm:pr-3 pr-0 text-base-400 3xl:text-base xmd:text-sm sm:text-xs text-[8px]">
                                            <div className="xmd:h-6 sm:h-5 h-4 w-5 bg-slate-200 rounded"></div>
                                        </td>
                                        {table.getAllColumns()?.map(d => (
                                            <td
                                                key={d.id}
                                                className="py-3 text-left pl-2 xmd:pr-5 sm:pr-3 pr-0 text-base-400 3xl:text-base xmd:text-sm sm:text-xs text-[8px]">
                                                <div className="xmd:h-6 sm:h-5 h-4 bg-slate-200 rounded"></div>
                                            </td>
                                        ))}
                                    </tr>
                                    <tr className="border-b">
                                        <td className="py-3 text-left pl-2 xmd:pr-5 sm:pr-3 pr-0 text-base-400 3xl:text-base xmd:text-sm sm:text-xs text-[8px]">
                                            <div className="xmd:h-6 sm:h-5 h-4 w-5 bg-slate-200 rounded"></div>
                                        </td>
                                        {table.getAllColumns()?.map(d => (
                                            <td
                                                key={d.id}
                                                className="py-3 text-left pl-2 xmd:pr-5 sm:pr-3 pr-0 text-base-400 3xl:text-base xmd:text-sm sm:text-xs text-[8px]">
                                                <div className="xmd:h-6 sm:h-5 h-4 bg-slate-200 rounded"></div>
                                            </td>
                                        ))}
                                    </tr>
                                    <tr className="border-b">
                                        <td className="py-3 text-left pl-2 xmd:pr-5 sm:pr-3 pr-0 text-base-400 3xl:text-base xmd:text-sm sm:text-xs text-[8px]">
                                            <div className="xmd:h-6 sm:h-5 h-4 w-5 bg-slate-200 rounded"></div>
                                        </td>
                                        {table.getAllColumns()?.map(d => (
                                            <td
                                                key={d.id}
                                                className="py-3 text-left pl-2 xmd:pr-5 sm:pr-3 pr-0 text-base-400 3xl:text-base xmd:text-sm sm:text-xs text-[8px]">
                                                <div className="xmd:h-6 sm:h-5 h-4 bg-slate-200 rounded"></div>
                                            </td>
                                        ))}
                                    </tr>
                                    <tr>
                                        <td className="py-3 text-left pl-2 xmd:pr-5 sm:pr-3 pr-0 text-base-400 3xl:text-base xmd:text-sm sm:text-xs text-[8px]">
                                            <div className="xmd:h-6 sm:h-5 h-4 w-5 bg-slate-200 rounded"></div>
                                        </td>
                                        {table.getAllColumns()?.map(d => (
                                            <td
                                                key={d.id}
                                                className="py-3 text-left pl-2 xmd:pr-5 sm:pr-3 pr-0 text-base-400 3xl:text-base xmd:text-sm sm:text-xs text-[8px]">
                                                <div className="xmd:h-6 sm:h-5 h-4 bg-slate-200 rounded"></div>
                                            </td>
                                        ))}
                                    </tr>
                                </tbody>
                            )}
                        </table>
                    </DndContext>
                </div>
            </div>
            {/* table footer */}
            {footerVisible && <TableFooter table={table} data={data} />}
        </div>
    );
}

export default DataTable;
