"use client";

import React, { useCallback, useEffect, useMemo, useState } from "react";

import styles from "@/components/administrator/Table/table.module.css";
import SafeImage from "@/components/ui/SafeImage/safeImage";
import { generateRange, getByKeys } from "@/app/library/library";

import { VscSearchStop } from "react-icons/vsc";
import { BiSort } from "react-icons/bi";
import { usePathname, useRouter, useSearchParams } from "next/navigation";
import Loader from "@/components/web/Loader/loader";

/**
 *
 * if preview image, use mediaPreview prop, and must be preview key
 * @type {React.FC <{
 *   data: any[];
 *   limit: number;
 *   key: string;
 *   showEmptyStatus: boolean;
 *   role: {
 *     key: string
 *   }; // key as key on objectdata, value as display header table
 *   style: {
 *     key: React.CSSProperties
 *   }; // key as role key pair when is image media, value as key on objectdata
 *   headerColor: string;
 *   evenColor: Boolean;
 *   mediaPreview?: {
 *     key: string
 *   }; // key as role key pair when is image media, value as key on objectdata
 *   itemClick?: {
 *     key: Function
 *   }
 *   onClick?: Function;
 *   onSelected?: Function;
 *   selected?: any[];
 *   sortRole: {
 *     canSorting: boolean;
 *     excludeSorting: string[];
 *     sortingFn: Function; // must have argument ({page, limit}, sort)
 *   }
 * }>}
 * @returns {JSX.Element}
 */
const Table = (props) => {
  const [checkedItems, setCheckedItems] = useState([]);
  const [isLoading, setIsLoading] = useState({});

  // const router = useRouter();
  const pathname = usePathname();
  const searchParams = useSearchParams();

  const updateQuery = useCallback(
    (key, value) => {
      const params = new URLSearchParams(searchParams);
      if (value) {
        params.set(key, value);
      } else {
        params.delete(key);
      }

      // delete page param when change sort
      if (key === "sort" && params.get("page")) params.delete("page");

      const newUrl = `${pathname}?${params.toString()}`;
      window.history.replaceState({}, "", newUrl);

      // if using router like below, page will soft reload the page
      // router.replace(`${pathname}?${params.toString()}`, { scroll: false });
    },
    [searchParams, pathname]
  );

  const indexKey = useMemo(() => (props.key ? props.key : "_id"), [props.key]);
  const sort = useMemo(() => {
    try {
      const SORT = Object.fromEntries(searchParams.entries())?.sort;
      return JSON.parse(SORT);
    } catch (error) {
      return null;
    }
  }, [searchParams]);
  const header = useMemo(() => {
    return {
      display: Object.values(props.role),
      keys: Object.keys(props.role).map((key) =>
        key.match(/\./gi) ? key.split(".").slice(-1)[0] : key
      ),
    };
  }, [props]);
  const data = useMemo(
    () =>
      props.data.map((item) =>
        getByKeys(item, [indexKey, ...Object.keys(props.role)])
      ),
    [props, indexKey]
  );

  const setChecked = useCallback(
    (data) => {
      const onSelectedAvailable =
        props.onSelected && typeof props.onSelected === "function";
      if (data && typeof data === "function") {
        const newData = data(checkedItems);
        setCheckedItems(newData);
        if (onSelectedAvailable) props.onSelected(newData);
        return;
      }

      if (onSelectedAvailable) props.onSelected(data);
      setCheckedItems(data);
    },
    [checkedItems, props]
  );

  const handleCheckBoxClick = (e, currentIndex) => {
    e.stopPropagation();
    if (!e.shiftKey) return;

    const indexes = checkedItems.map((item) =>
      data.findIndex((_item) => _item[indexKey] === item[indexKey])
    );
    const lastCheckedIndex = data.findIndex(
      (item) =>
        item[indexKey] === checkedItems[checkedItems.length - 1][indexKey]
    );

    const newCheckedIndex = [
      ...new Set([
        ...indexes,
        ...generateRange(lastCheckedIndex, currentIndex),
      ]),
    ];

    const newData = data.filter((_, index) => newCheckedIndex.includes(index));
    setTimeout(() => setChecked(newData), 10);
  };

  const getSortData = useCallback(
    async (key, value) => {
      if (
        props.sortRole.sortingFn &&
        typeof props.sortRole.sortingFn === "function"
      ) {
        await props.sortRole.sortingFn(
          { page: 1, limit: props.limit },
          { [key]: value }
        );
      }
    },
    [props]
  );

  useEffect(() => {
    if (!props?.selected) return;
    setCheckedItems(props.selected);
  }, [props.selected]);

  return (
    <div className={styles.table_container}>
      {data.length === 0 &&
        (props.showEmptyStatus === undefined ||
          props.showEmptyStatus === true) && (
          <div className={styles.empty_container}>
            <div className={styles.empty}>
              <VscSearchStop
                size={60}
                color="#3d3d3d"
              />
              <p>Empty</p>
            </div>
          </div>
        )}
      {data.length > 0 && (
        <table
          className={styles.table}
          cellSpacing="0">
          <thead
            style={{ backgroundColor: props?.headerColor || "transparent" }}>
            <tr>
              {props?.onSelected && typeof props.onSelected === "function" && (
                <th className={styles.checkbox}>
                  <input
                    type="checkbox"
                    checked={checkedItems.length === props.data.length}
                    onChange={(e) => {
                      if (e.target.checked) {
                        setChecked(props.data);
                      } else {
                        setChecked([]);
                      }
                    }}
                  />
                </th>
              )}
              {header.display.map((item, index) => (
                <th key={index}>
                  <div className={styles.header_item}>
                    {item}
                    {props?.sortRole?.canSorting &&
                      !props?.sortRole?.excludeSorting?.includes(
                        header.keys[index]
                      ) && (
                        <div
                          className={`${styles.order_button} ${
                            isLoading[header.keys[index]] && styles.disabled
                          }`}
                          onClick={async () => {
                            if (isLoading[header.keys[index]]) return;
                            const isSame = sort
                              ? Object.keys(sort)[0] === header.keys[index]
                              : false;
                            const sortOrder = isSame
                              ? Object.values(sort)[0] * -1
                              : -1;
                            updateQuery(
                              "sort",
                              JSON.stringify({
                                [header.keys[index]]: sortOrder,
                              })
                            );

                            setIsLoading({ [header.keys[index]]: true });
                            await getSortData(header.keys[index], sortOrder);
                            setIsLoading({});
                          }}>
                          <BiSort size={10} />
                        </div>
                      )}
                    {isLoading && isLoading[header.keys[index]] && <Loader />}
                  </div>
                </th>
              ))}
            </tr>
          </thead>
          <tbody
            className={`${
              props?.evenColor ||
              (props?.evenColor === undefined && styles.evenColor)
            }`}>
            {data.map((item, index) => (
              <tr
                key={index}
                className={`${styles.row} ${
                  checkedItems
                    .map((item) => item?.[indexKey])
                    .includes(item?.[indexKey]) && styles.selected
                }`}
                onClick={(e) => {
                  const currentData = props.data.find(
                    (_item) => _item?.[indexKey] === item?.[indexKey]
                  );
                  if (props.onClick && typeof props.onClick === "function")
                    props.onClick(currentData, e);

                  setChecked([]);
                }}
                onMouseEnter={(e) => {
                  const currentData = props.data.find(
                    (_item) => _item?.[indexKey] === item?.[indexKey]
                  );
                  if (
                    props.onMouseEnter &&
                    typeof props.onMouseEnter === "function"
                  )
                    props.onMouseEnter(currentData, e);
                }}>
                {props?.onSelected &&
                  typeof props.onSelected === "function" && (
                    <td
                      className={styles.checkbox}
                      onClick={(e) => e.stopPropagation()}>
                      <span>
                        <input
                          type="checkbox"
                          checked={checkedItems
                            .map((data) => data?.[indexKey])
                            .includes(item?.[indexKey])}
                          onClick={(e) => handleCheckBoxClick(e, index)}
                          onChange={(e) => {
                            if (e.target.checked) {
                              setChecked((prev) => [
                                ...prev,
                                props.data.find(
                                  (_item) =>
                                    _item?.[indexKey] === item?.[indexKey]
                                ),
                              ]);
                            } else {
                              setChecked((prev) =>
                                prev.filter(
                                  (data) =>
                                    data?.[indexKey] !== item?.[indexKey]
                                )
                              );
                            }
                          }}
                        />
                      </span>
                    </td>
                  )}
                {header.keys.map((key, index) => (
                  <td
                    key={index}
                    style={props?.style?.[key] ? props.style[key] : {}}
                    onClick={(e) => {
                      if (
                        props?.itemClick?.[key] &&
                        typeof props.itemClick[key] === "function"
                      ) {
                        e.stopPropagation();
                        props.itemClick[key](
                          props.data.find(
                            (_item) => _item?.[indexKey] === item?.[indexKey]
                          ),
                          item,
                          e
                        );
                      }
                    }}
                    className={`${key === "preview" && styles.preview}`}>
                    {key === "preview" ? (
                      <SafeImage
                        src={item[props.mediaPreview[key]]}
                        alt={item["alt"] || "preview"}
                        width={100}
                        height={50}
                        fallbackSrc="https://placehold.co/300x200/f0f0f0/999/png?text=NotFound"
                      />
                    ) : (
                      <span>
                        {item[key]
                          ? item[key]
                          : `<No ${props.role[key] || header.display[index]}>`}
                      </span>
                    )}
                  </td>
                ))}
              </tr>
            ))}
          </tbody>
        </table>
      )}
    </div>
  );
};

export default Table;
