"use client";

import { Fragment, useEffect, useMemo, useRef, useState } from "react";
import clsx from "clsx";
import {
  ChevronUp,
  ArrowDownUp,
  ArrowDownWideNarrow,
  ArrowDownNarrowWide,
} from "lucide-react";

/* ================= TYPES ================= */
interface ColumnType<T> {
  title: string | React.ReactNode;
  render: (row: T, index: number, path?: number[]) => React.ReactNode;
  className?: string;
  checkBox?: boolean;
  fixedLeft?: boolean;
  fixedRight?: boolean;
  maxWidth?: number | string;
  sortTable?: string;
}

interface PropsTable<T> {
  data: T[];
  column: ColumnType<T>[];
  fixedHeader?: boolean;
  activeHeader?: boolean;
  handleCheckedAll?: (e: React.ChangeEvent<HTMLInputElement>) => void;
  isCheckedAll?: boolean;
  handleCheckedRow?: (e: React.ChangeEvent<HTMLInputElement>, row: T) => void;
  handleIsCheckedRow?: (row: T) => boolean;
  rowKey: (row: T) => React.Key;
  getChildren?: (row: T) => T[] | undefined;
  useIndexPathAsKey?: boolean;
}

/* ================= COMPONENT ================= */
export default function Table<T>({
  data,
  column,
  fixedHeader,
  activeHeader,
  handleCheckedAll,
  isCheckedAll,
  handleCheckedRow,
  handleIsCheckedRow,
  rowKey,
  getChildren,
  useIndexPathAsKey,
}: PropsTable<T>) {
  const tableRef = useRef<HTMLDivElement>(null);
  const thRefs = useRef<(HTMLTableCellElement | null)[]>([]);

  const [expandedRows, setExpandedRows] = useState<React.Key[]>([]);
  const [sortConfig, setSortConfig] = useState<{
    key: string;
    direction: "asc" | "desc" | null;
  }>({ key: "", direction: null });

  /* ================= SORT ================= */
  const handleSort = (key: string) => {
    setSortConfig((prev) => {
      if (prev.key === key) {
        const next =
          prev.direction === "asc"
            ? "desc"
            : prev.direction === "desc"
              ? null
              : "asc";
        return { key, direction: next };
      }
      return { key, direction: "asc" };
    });
  };

  const sortedData = useMemo(() => {
    if (!sortConfig.key || !sortConfig.direction) return data;

    const copy = [...data];

    copy.sort((a: any, b: any) => {
      const aVal = a?.[sortConfig.key];
      const bVal = b?.[sortConfig.key];

      if (typeof aVal === "number" && typeof bVal === "number") {
        return sortConfig.direction === "asc" ? aVal - bVal : bVal - aVal;
      }

      if (typeof aVal === "string" && typeof bVal === "string") {
        return sortConfig.direction === "asc"
          ? aVal.localeCompare(bVal)
          : bVal.localeCompare(aVal);
      }

      return 0;
    });

    return copy;
  }, [data, sortConfig]);

  /* ================= EXPAND ================= */
  const toggleExpand = (key: React.Key) => {
    setExpandedRows((prev) =>
      prev.includes(key) ? prev.filter((k) => k !== key) : [...prev, key],
    );
  };

  /* ================= ROWS ================= */
  const renderRows = (rows: T[], path: number[] = []): React.ReactNode =>
    rows.map((row, i) => {
      const currentPath = [...path, i];
      const keyPath = useIndexPathAsKey ? currentPath.join(".") : rowKey(row);

      const children = getChildren?.(row);
      const expanded = expandedRows.includes(keyPath);

      return (
        <Fragment key={String(keyPath)}>
          <tr className="bg-white border-b">
            {getChildren && (
              <td className="w-[60px] text-center">
                {!!children?.length && (
                  <button
                    onClick={() => toggleExpand(keyPath)}
                    className={clsx(
                      "transition text-gray-400 hover:text-blue-500",
                      expanded && "rotate-180 text-blue-500",
                    )}
                  >
                    <ChevronUp size={18} />
                  </button>
                )}
              </td>
            )}

            {column.map((col, idx) => (
              <td
                key={idx}
                className={clsx(
                  "px-4 py-3 text-sm font-medium whitespace-nowrap",
                  col.fixedLeft && "sticky left-0 bg-white z-10",
                  col.fixedRight && "sticky right-0 bg-white z-10",
                )}
              >
                <div
                  style={{
                    maxWidth:
                      typeof col.maxWidth === "number"
                        ? `${col.maxWidth}px`
                        : col.maxWidth,
                  }}
                  className={clsx("flex items-center gap-2")}
                >
                  {col.checkBox && (
                    <input
                      type="checkbox"
                      className="w-4 h-4 accent-blue-500 cursor-pointer"
                      onChange={(e) => handleCheckedRow?.(e, row)}
                      checked={handleIsCheckedRow?.(row) ?? false}
                    />
                  )}
                  {col.render(row, i, currentPath)}
                </div>
              </td>
            ))}
          </tr>

          {expanded && children && renderRows(children, currentPath)}
        </Fragment>
      );
    });

  /* ================= UI ================= */
  return (
    <div
      ref={tableRef}
      className={clsx(
        "overflow-auto pb-2",
        fixedHeader && "[&>table>thead]:sticky [&>table>thead]:top-0",
        activeHeader && "[&>table>thead]:bg-[#F4F7FA]",
      )}
    >
      <table className="w-full border-collapse">
        <thead className="bg-white border-b">
          <tr>
            {getChildren && <th className="w-[60px]" />}

            {column.map((col, i) => {
              const isActive = col.sortTable === sortConfig.key;

              return (
                <th
                  key={i}
                  ref={(el) => {
                    thRefs.current[i] = el;
                  }}
                  onClick={() => col.sortTable && handleSort(col.sortTable)}
                  className={clsx(
                    "px-4 py-3 text-left text-[15px] font-semibold whitespace-nowrap",
                    col.sortTable && "cursor-pointer",
                    isActive && "text-blue-600",
                    col.fixedLeft && "sticky left-0 bg-white z-10",
                    col.fixedRight && "sticky right-0 bg-white z-10",
                  )}
                >
                  <div className="flex items-center gap-1">
                    {col.checkBox && (
                      <input
                        type="checkbox"
                        onChange={handleCheckedAll}
                        checked={isCheckedAll}
                      />
                    )}

                    {col.title}

                    {col.sortTable &&
                      (isActive ? (
                        sortConfig.direction === "asc" ? (
                          <ArrowDownNarrowWide size={14} />
                        ) : sortConfig.direction === "desc" ? (
                          <ArrowDownWideNarrow size={14} />
                        ) : (
                          <ArrowDownUp size={14} />
                        )
                      ) : (
                        <ArrowDownUp size={14} />
                      ))}
                  </div>
                </th>
              );
            })}
          </tr>
        </thead>

        <tbody>{renderRows(sortedData)}</tbody>
      </table>
    </div>
  );
}
