import '@/Styles/datatable.style.css';
import { Delete, Search } from 'lucide-react';
import { CSSProperties, ReactNode, useCallback, useEffect, useMemo, useState } from 'react';
import { useNavigate } from 'react-router';

export interface Column<T> {
    label: ReactNode;
    key?: keyof T | string;
    align?: CSSProperties['textAlign'];
    value?: (data: T, index?: number) => ReactNode;
    width?: number;
    style?: CSSProperties;
    class?: string;
}

interface DataTableProps<T> {
    data: T[];
    column: Column<T>[] | Column<T>;
    item_per_page?: number;
    history?: boolean;
    filter?: boolean;
    search?: boolean;
    filterText?: string;
    filterKategori?: string;
    loading?: boolean;
    currentPage?: number;
    buttons?: ReactNode;
    height?: string | number;
    containerStyle?: CSSProperties;
    style?: CSSProperties;
    headerStyle?: CSSProperties;
    size?: 'xs' | 'sm' | 'md' | 'lg';
    wrap?: boolean;
    row_number?: boolean;
    selectable?: boolean;
    costumRow?: (data: T) => boolean;
    costumRowStyle?: CSSProperties;
    onDoubleClick?: (data: T) => void;
    onSelected?: (selectedData: T[]) => void;
    onSearch?: (query: string) => void;
    pagination?: boolean;
}

interface DisplayedDataItem extends Record<string, unknown> {
    index?: number;
}

export default function DataTable<T extends Record<string, unknown>>(props: DataTableProps<T>) {
    const {
        data: originalData = [],
        column: propColumn,
        item_per_page,
        history = true,
        filter = false,
        search = false,
        filterText = '',
        filterKategori = '',
        loading: propLoading = false,
        // currentPage: propCurrentPage = 1,
        currentPage,
        buttons,
        height,
        containerStyle,
        style,
        headerStyle,
        size,
        wrap = false,
        row_number = true,
        selectable = false,
        costumRow,
        costumRowStyle,
        onDoubleClick,
        onSelected,
        pagination = true,
    } = props;

    const navigate = useNavigate();
    const existingParams = useMemo(() => new URLSearchParams(window.location.search), []);

    const [page, setPage] = useState<number>(() => {
        const pageFromUrl = existingParams.get('page');
        return pageFromUrl ? parseInt(pageFromUrl) : (currentPage ?? 1);
    });
    const [selectedRows, setSelectedRows] = useState<number[]>([]);
    const [currentFilterText, setCurrentFilterText] = useState<string>(filterText);
    const [currentColumnFilter, setCurrentColumnFilter] = useState<string>(filterKategori);
    const [searchQuery, setSearchQuery] = useState<string>(existingParams.get('filter') ?? '');
    const [loading, setLoading] = useState<boolean>(propLoading);

    const getParameter = useCallback(
        (parameter: string) => {
            return existingParams.get(parameter);
        },
        [existingParams],
    );

    // Set parameter di URL
    const setParameter = useCallback(
        (parameter: string, value: string | number) => {
            if (!history) return;
            existingParams.set(parameter, String(value));
            const newUrl = `${window.location.pathname}?${existingParams.toString()}`;
            navigate(newUrl);
        },
        [existingParams, navigate, history],
    );

    useEffect(() => setLoading(propLoading), [propLoading]);

    useEffect(() => {
        const pageParam = getParameter('page');
        setPage(pageParam ? parseInt(pageParam) : page);
    }, [getParameter]);

    // Gabungkan filter dan search
    const processedData = useMemo(() => {
        let data = originalData;
        if (filter && currentFilterText && currentColumnFilter) {
            data = data.filter((d) => {
                const value = d[currentColumnFilter];
                return value != null && value.toString().toLowerCase().includes(currentFilterText.toLowerCase());
            });
        }
        if (search && searchQuery) {
            data = data.filter((item) => {
                const values = Object.values(item);
                for (let i = 0; i < values.length; i++) {
                    const val = values[i];
                    if (val != null && val.toString().toLowerCase().includes(searchQuery.toLowerCase())) {
                        return true;
                    }
                }

                // return Object.values(item).some((val) => {
                //     return val != null && val.toString().toLowerCase().includes(searchQuery.toLowerCase());
                // });
            });
        }
        return data;
    }, [originalData, filter, currentFilterText, currentColumnFilter, search, searchQuery]);

    const totalItems = processedData.length;
    const pageCount = (item_per_page ?? 10) > 0 ? Math.ceil(totalItems / (item_per_page ?? 10)) : 1;

    // Validasi page
    useEffect(() => {
        if (page < 1 && pageCount > 0) setPage(1);
        else if (page > pageCount && pageCount > 0) setPage(pageCount);
    }, [page, pageCount]);

    // Data yang ditampilkan
    const displayedData: DisplayedDataItem[] = useMemo(() => {
        if (props.pagination ?? true) {
            const startIndex = (page - 1) * (item_per_page ?? 10);
            const endIndex = startIndex + (item_per_page ?? 10);
            const sliced = processedData.slice(startIndex, endIndex).map((d, i) => ({ ...d, index: startIndex + i }));
            // Padding jika kurang dari item_per_page
            while (sliced.length < (item_per_page ?? 10)) {
                sliced.push({
                    ...({} as T),
                    index: sliced.length > 0 ? (sliced[sliced.length - 1].index ?? 0) + 1 : originalData.length + sliced.length,
                });
            }
            return sliced;
        }
        return processedData.map((d, i) => ({ ...d, index: i }));
    }, [props.pagination, processedData, page, item_per_page, originalData.length]);

    // Kolom
    const columns = useMemo(() => {
        const columnArray = Array.isArray(propColumn) ? propColumn : [propColumn];
        return row_number
            ? [
                  {
                      label: <div className="text-center">#</div>,
                      align: 'center',
                      value: (_: unknown, index = 0) => <span>{index + 1}</span>,
                      width: 45,
                      style: {},
                  },
                  ...columnArray,
              ]
            : columnArray;
    }, [propColumn, row_number]);

    // Ukuran tabel
    const getTableSizeClass = useCallback(() => {
        switch (size) {
            case 'xs':
                return 'table-xs';
            case 'sm':
                return 'table-sm';
            case 'md':
                return 'table-md';
            case 'lg':
                return 'table-lg';
            default:
                return 'table-sm';
        }
    }, [size]);

    // Navigasi halaman
    const goToPage = useCallback(
        (pageNumber: number) => {
            setPage(pageNumber);
            setParameter('page', pageNumber);
        },
        [setParameter],
    );

    const previousPage = useCallback(() => {
        if (page > 1) goToPage(page - 1);
    }, [page, goToPage]);

    const nextPage = useCallback(() => {
        if (page < pageCount) goToPage(page + 1);
    }, [page, pageCount, goToPage]);

    // Pagination builder
    const paginationBuilder = useCallback(() => {
        const pages = Array.from({ length: pageCount }, (_, i) => i + 1);
        if (pageCount <= 9) {
            return (
                <>
                    <li className={`page-item ${page === 1 ? 'disabled' : ''}`}>
                        <button className="page-link" onClick={previousPage} disabled={page === 1 || totalItems === 0}>
                            Previous
                        </button>
                    </li>
                    {pages.map((item) => (
                        <li key={item} className={`page-item ${page === item ? 'active' : ''}`}>
                            <button className="page-link" onClick={() => goToPage(item)}>
                                {item}
                            </button>
                        </li>
                    ))}
                    <li className={`page-item ${page === pageCount ? 'disabled' : ''}`}>
                        <button className="page-link" onClick={nextPage} disabled={page === pageCount}>
                            Next
                        </button>
                    </li>
                </>
            );
        }
        // ... (logika >9 halaman tetap sama)
        return (
            <>
                <li className={`page-item ${page === 1 ? 'disabled' : ''}`}>
                    <button className="page-link" onClick={previousPage}>
                        Previous
                    </button>
                </li>
                <li className={`page-item ${page === 1 ? 'active' : ''}`}>
                    <button className="page-link" onClick={() => goToPage(1)}>
                        1
                    </button>
                </li>
                {page <= 3 && pageCount > 2 && (
                    <li className={`page-item ${page === 2 ? 'active' : ''}`}>
                        <button className="page-link" onClick={() => goToPage(2)}>
                            2
                        </button>
                    </li>
                )}
                {page <= 3 && pageCount > 3 && (
                    <li className={`page-item ${page === 3 ? 'active' : ''}`}>
                        <button className="page-link" onClick={() => goToPage(3)}>
                            3
                        </button>
                    </li>
                )}
                {page > 3 && (
                    <li className="page-item">
                        <button className="page-link" onClick={previousPage}>
                            ...
                        </button>
                    </li>
                )}
                {page > 3 && page <= pageCount - 3 && (
                    <li className={`page-item active`}>
                        <button className="page-link" onClick={() => goToPage(page)}>
                            {page}
                        </button>
                    </li>
                )}
                {page <= pageCount - 3 && (
                    <li className="page-item">
                        <button className="page-link" onClick={nextPage}>
                            ...
                        </button>
                    </li>
                )}
                {page >= pageCount - 2 && (
                    <>
                        <li className={`page-item ${page === pageCount - 2 ? 'active' : ''}`}>
                            <button className="page-link" onClick={() => goToPage(pageCount - 2)}>
                                {pageCount - 2}
                            </button>
                        </li>
                        <li className={`page-item ${page === pageCount - 1 ? 'active' : ''}`}>
                            <button className="page-link" onClick={() => goToPage(pageCount - 1)}>
                                {pageCount - 1}
                            </button>
                        </li>
                    </>
                )}
                <li className={`page-item ${page === pageCount ? 'active' : ''}`}>
                    <button className="page-link" onClick={() => goToPage(pageCount)}>
                        {pageCount}
                    </button>
                </li>
                <li className={`page-item ${page === pageCount ? 'disabled' : ''}`}>
                    <button className="page-link" onClick={nextPage}>
                        Next
                    </button>
                </li>
            </>
        );
    }, [page, pageCount, goToPage, previousPage, nextPage, totalItems]);

    const handleRowClick = useCallback(
        (tableData: DisplayedDataItem) => {
            if (!selectable) return;
            setSelectedRows((prevSelected) => {
                const isSelected = prevSelected.includes(tableData.index!);
                const newSelected = isSelected ? prevSelected.filter((index) => index !== tableData.index) : [...prevSelected, tableData.index!];
                if (onSelected) {
                    const actualSelectedData = originalData.filter((_, idx) => newSelected.includes(idx));
                    onSelected(actualSelectedData);
                }
                return newSelected;
            });
        },
        [selectable, onSelected, originalData],
    );

    return (
        <>
            {!columns || columns.length === 0 ? (
                <span className="text-center">Tidak ada data</span>
            ) : (
                <>
                    {filter && (
                        <div className="mb-2 gap-3 d-flex" style={{ width: '500px' }}>
                            <div className="input-group">
                                <span className="input-group-text">Filter</span>
                                <input
                                    className="form-control"
                                    placeholder="kata pencarian"
                                    value={currentFilterText}
                                    onChange={(e) => {
                                        setParameter('filter', e.target.value);
                                        setCurrentFilterText(e.target.value);
                                    }}
                                />
                                <span className="input-group-text">Kolom</span>
                                <select
                                    className="form-select"
                                    value={currentColumnFilter}
                                    onChange={(e) => {
                                        setParameter('kategori', e.target.value);
                                        setCurrentColumnFilter(e.target.value);
                                    }}
                                >
                                    <option value="" disabled>
                                        Pilih Kolom
                                    </option>
                                    {columns.map((col, index) =>
                                        'key' in col && col.key ? (
                                            <option key={index} value={String(col.key)}>
                                                {col.label}
                                            </option>
                                        ) : null,
                                    )}
                                </select>
                            </div>
                            <button
                                className="btn btn-danger"
                                onClick={() => {
                                    setCurrentColumnFilter('');
                                    setCurrentFilterText('');
                                    if (history) {
                                        existingParams.delete('filter');
                                        existingParams.delete('kategori');
                                        const newUrl = `${window.location.pathname}?${existingParams.toString()}`;
                                        navigate(newUrl);
                                    }
                                }}
                            >
                                Reset
                            </button>
                        </div>
                    )}

                    <div className="d-flex justify-content-between">
                        {search && (
                            <div className="mb-3 gap-2 d-flex" style={{ width: '300px' }}>
                                <div className="input-group">
                                    <span className="input-group-text bg-white gap-3 d-flex align-items-center">
                                        <Search size={18} />
                                    </span>
                                    <input
                                        className={`form-control border-start-0 ${searchQuery ? 'border-end-0' : ''}`}
                                        placeholder="Filter"
                                        value={searchQuery}
                                        onChange={(e) => {
                                            // if (props.onSearch) props.onSearch(e.target.value);
                                            setSearchQuery(e.target.value);
                                            setParameter('filter', e.target.value);
                                        }}
                                    />
                                    {searchQuery && (
                                        <span
                                            className="input-group-text bg-white gap-3 d-flex align-items-center c-pointer"
                                            onClick={() => {
                                                setSearchQuery('');
                                                setParameter('filter', '');
                                            }}
                                        >
                                            <Delete size={18} />
                                        </span>
                                    )}
                                </div>
                            </div>
                        )}
                        <div className="mb-2">{buttons}</div>
                    </div>

                    <div style={{ overflowX: 'auto', borderRadius: 6, border: '1px solid #bbb', height, ...containerStyle }}>
                        <table
                            className={`table-responsive table-hover table ${getTableSizeClass()} datatable`}
                            style={{
                                ...style,
                                overflow: 'scroll',
                                textWrap: wrap ? 'wrap' : 'nowrap',
                                margin: 0,
                                tableLayout: 'fixed',
                                height: displayedData.length == 0 ? '-webkit-fill-available' : '',
                            }}
                        >
                            <thead style={headerStyle}>
                                <tr className="fixed">
                                    {columns.map((col, index) => (
                                        <th
                                            key={index}
                                            className="px-2 py-2"
                                            style={{
                                                width: col?.width,
                                                overflow: 'hidden',
                                                textOverflow: 'ellipsis',
                                                backgroundColor: '#E5E9F2',
                                            }}
                                        >
                                            {col?.label}
                                        </th>
                                    ))}
                                </tr>
                            </thead>
                            <tbody style={{ position: 'relative' }}>
                                {/* {loading && (
                                    <tr>
                                        <td
                                            colSpan={columns.length}
                                            style={{
                                                position: 'absolute',
                                                top: 0,
                                                left: 0,
                                                right: 0,
                                                bottom: 0,
                                                display: 'flex',
                                                alignItems: 'center',
                                                justifyContent: 'center',
                                                backgroundColor: 'rgba(128, 128, 128, 0.1)',
                                                zIndex: 1,
                                                fontSize: '1.2rem',
                                                color: '#333',
                                                fontFamily: 'Arial, sans-serif',
                                            }}
                                            className="d-flex gap-2"
                                        >
                                            <span className="spinner-border spinner-border-sm"></span>
                                            Loading...
                                        </td>
                                    </tr>
                                )} */}
                                {displayedData.length === 0 ? (
                                    <tr>
                                        <td colSpan={columns.length} className="text-center" valign="middle">
                                            Tidak ada Data
                                        </td>
                                    </tr>
                                ) : (
                                    displayedData.map((tableData, index) => {
                                        const isSelected =
                                            selectable && selectedRows.includes(tableData.index!) && tableData.index! < originalData.length;
                                        const isCostumRow =
                                            costumRow &&
                                            tableData.index !== undefined &&
                                            tableData.index < originalData.length &&
                                            costumRow(originalData[tableData.index]);

                                        return (
                                            <tr
                                                key={index}
                                                style={{ height: 31.86, ...(isCostumRow && costumRowStyle) }}
                                                className={`${onDoubleClick ? 'disable-selection' : ''} ${isSelected ? 'selected' : ''}`}
                                                onDoubleClick={() =>
                                                    onDoubleClick &&
                                                    tableData.index !== undefined &&
                                                    tableData.index < originalData.length &&
                                                    onDoubleClick(originalData[tableData.index])
                                                }
                                                onClick={() => handleRowClick(tableData)}
                                            >
                                                {columns.map((col, colIndex) => {
                                                    return (
                                                        <td
                                                            key={colIndex}
                                                            className="px-2 py-1"
                                                            style={{
                                                                textAlign: col?.align as CSSProperties['textAlign'],
                                                                textOverflow: wrap ? 'inherit' : 'ellipsis',
                                                                overflow: wrap ? 'inherit' : 'hidden',
                                                                verticalAlign: 'middle',
                                                                ...col?.style,
                                                            }}
                                                        >
                                                            {tableData && tableData.index !== undefined && tableData.index < originalData.length ? (
                                                                col?.value ? (
                                                                    col.value(tableData, tableData.index)
                                                                ) : 'key' in col && col.key ? (
                                                                    (tableData[String(col.key)] as ReactNode)
                                                                ) : null
                                                            ) : (
                                                                <div style={{ height: 15 }}></div>
                                                            )}
                                                        </td>
                                                    );
                                                })}
                                            </tr>
                                        );
                                    })
                                )}
                            </tbody>
                        </table>
                    </div>

                    {pagination && (
                        <div
                            className="pagination pagination-space d-flex align-items-center justify-content-between"
                            style={{ flexWrap: 'wrap', fontFamily: 'inherit', margin: '12px 0px 0px 0px' }}
                        >
                            {loading ? (
                                <div className="align-items-center d-flex gap-2">
                                    <span className="spinner-border spinner-border-sm mr-2"></span>
                                    <span>Memuat Data...</span>
                                </div>
                            ) : (
                                <>
                                    {totalItems === 0 || pageCount === 0 ? (
                                        <div style={{ fontFamily: 'arial' }}>Tidak ada data</div>
                                    ) : (
                                        <div style={{ fontFamily: 'arial' }}>
                                            Menampilkan {displayedData[0]?.index !== undefined ? displayedData[0].index + 1 : 0} -{' '}
                                            {page === pageCount
                                                ? totalItems
                                                : Math.min(totalItems, (displayedData[displayedData.length - 1]?.index ?? 0) + 1)}{' '}
                                            dari {totalItems}
                                        </div>
                                    )}
                                </>
                            )}
                            <div className="gap-3 d-flex">
                                <div className="input-group mb-3">
                                    <span className="input-group-text">Halaman</span>
                                    <select className="form-select" value={page} onChange={(e) => goToPage(parseInt(e.target.value))}>
                                        {Array.from({ length: pageCount }).map((_, index) => (
                                            <option key={index} value={index + 1}>
                                                {index + 1}
                                            </option>
                                        ))}
                                    </select>
                                </div>
                                <ul style={{ listStyle: 'none', fontFamily: 'arial' }} className="disable-selection pagination flex">
                                    {paginationBuilder()}
                                </ul>
                            </div>
                        </div>
                    )}
                </>
            )}
        </>
    );
}
