import {
  MagnifyingGlassIcon,
  PlusIcon,
  XMarkIcon,
} from "@heroicons/react/24/outline";
import { CheckCircleIcon } from "@heroicons/react/24/solid";
import { useEffect, useRef, useState } from "react";
import cn from "../utils/cn";
import LoadingSpinner from "./LoadingSpinner";

const AsyncMultiSelect = ({
  onChange,
  queryHook,
  selected = [],
  placeHolder,
  endpoint = "",
  widthClass = "ino:w-96",
  invalid = false,
}) => {
  const searchRef = useRef();
  const dropdownRef = useRef();

  const [skip, setSkip] = useState(true);
  const [searchQuery, setSearchQuery] = useState("");
  const [showSearchResult, setShowSearchResult] = useState(false);
  const [selectedItems, setSelectedItems] = useState(selected);

  const { data, isError, isLoading, isFetching } = queryHook(
    { endpoint, query: { search: searchQuery } },
    {
      skip,
    },
  );

  // handle dropdown outside click
  useEffect(() => {
    const handler = (e) => {
      if (!dropdownRef.current.contains(e.target)) {
        setShowSearchResult(false);
        searchRef.current.value = "";
      }
    };
    document.addEventListener("mousedown", handler);
    return () => {
      document.removeEventListener("mousedown", handler);
    };
  }, []);

  useEffect(() => {
    onChange(selectedItems);
  }, [selectedItems]);

  // handle item select
  const handleSelect = (item) => {
    if (selectedItems.find((_item) => _item.id === item.id)) {
      setSelectedItems(selectedItems.filter((_item) => _item.id !== item.id));
    } else {
      setSelectedItems((prevState) => [...prevState, item]);
    }
  };

  const handleRemoveItem = (id) => {
    setSelectedItems(selectedItems.filter((_item) => _item.id !== id));
  };

  // debounce functionality start
  const debounceHandler = (fn, delay) => {
    let timeoutId;
    return (...args) => {
      clearTimeout(timeoutId);
      timeoutId = setTimeout(() => {
        fn(...args);
      }, delay);
    };
  };

  const doSearch = (e) => {
    const query = e.target.value;
    setSearchQuery(query);
    if (query.trim().length === 0) {
      setShowSearchResult(false);
      setSkip(true);
      return;
    }
    setSkip(false);
    setShowSearchResult(true);
  };
  const handleSearch = debounceHandler(doSearch, 500);
  // debounce functionality end

  const isSelected = (item) => selected.some((_item) => _item.id === item.id);
  const results = !isError && Array.isArray(data) ? data : [];

  // set content for different state
  let content = null;

  if (isLoading || (isFetching && isError)) {
    content = (
      <div className="ino:flex ino:items-center ino:gap-2 ino:px-3 ino:py-4 ino:text-sm ino:text-muted-foreground">
        <LoadingSpinner className="ino:size-4" />
        Searching...
      </div>
    );
  } else if (isError || results.length === 0) {
    // The search API answers "no matches" with an error response.
    content = (
      <div className="ino:flex ino:flex-col ino:items-center ino:gap-1 ino:px-3 ino:py-5 ino:text-center">
        <MagnifyingGlassIcon className="ino:size-5 ino:text-muted-foreground/60" />
        <p className="ino:m-0 ino:text-sm ino:font-medium ino:text-foreground">
          No results found
        </p>
        <p className="ino:m-0 ino:text-xs ino:text-muted-foreground">
          Try a different search term.
        </p>
      </div>
    );
  } else {
    content = (
      <>
        <p className="ino:m-0 ino:border-b ino:border-border ino:px-3 ino:py-2 ino:text-[11px] ino:font-semibold ino:tracking-wider ino:text-muted-foreground ino:uppercase">
          {results.length} {results.length === 1 ? "result" : "results"}
        </p>
        <ul
          role="listbox"
          aria-multiselectable="true"
          className="ino:custom-scrollbar ino:m-0 ino:max-h-72 ino:overflow-y-auto ino:p-1"
        >
          {results.map((item) => {
            const checked = isSelected(item);

            return (
              <li
                key={item.id}
                role="option"
                aria-selected={checked}
                onClick={() => handleSelect(item)}
                className={cn(
                  "ino:group ino:m-0 ino:flex ino:cursor-pointer ino:items-center ino:gap-2.5 ino:rounded-md ino:px-2 ino:py-1.5 ino:transition-colors",
                  checked ? "ino:bg-primary/5" : "ino:hover:bg-muted",
                )}
              >
                {item.image ? (
                  <img
                    className="ino:size-9 ino:shrink-0 ino:rounded-md ino:border ino:border-border ino:object-cover"
                    src={item.image}
                    alt=""
                    loading="lazy"
                  />
                ) : null}
                <div className="ino:min-w-0 ino:flex-1">
                  <p
                    className={cn(
                      "ino:m-0 ino:truncate ino:text-sm ino:font-medium ino:select-none",
                      checked ? "ino:text-primary" : "ino:text-foreground",
                    )}
                  >
                    {item.name}
                  </p>
                  <p className="ino:m-0 ino:truncate ino:text-xs ino:text-muted-foreground">
                    ID: {item.id}
                    {item.sku ? ` · SKU: ${item.sku}` : ""}
                  </p>
                </div>
                {checked ? (
                  <CheckCircleIcon className="ino:size-5 ino:shrink-0 ino:text-primary" />
                ) : (
                  <span className="ino:flex ino:size-6 ino:shrink-0 ino:items-center ino:justify-center ino:rounded-md ino:text-muted-foreground ino:transition-colors ino:group-hover:bg-primary/10 ino:group-hover:text-primary">
                    <PlusIcon className="ino:size-4" />
                  </span>
                )}
              </li>
            );
          })}
        </ul>
      </>
    );
  }

  return (
    <div ref={dropdownRef} className={`ino:relative ${widthClass}`}>
      <div
        className={`ino:relative ino:rounded-md ino:border ino:border-border ino:bg-card ino:shadow-xs ino:transition-colors ino:focus-within:border-primary ino:hover:border-primary/40 ${invalid ? "ino:border-red-500! ino:ring-2 ino:ring-red-500/15" : ""}`}
      >
        <div
          onClick={() => {
            searchRef.current.focus();
          }}
          className="ino:flex ino:min-h-8.5 ino:cursor-text ino:flex-wrap ino:items-center ino:gap-1.5 ino:px-1.5 ino:py-0.5 ino:text-sm ino:outline-none"
        >
          {selected.length > 0 &&
            selected.map((item) => (
              <div
                className="ino:inline-flex ino:items-center ino:gap-1 ino:rounded-full ino:border ino:border-border ino:bg-muted ino:py-0.5 ino:pr-1 ino:pl-2.5 ino:text-xs ino:font-medium ino:text-foreground"
                key={item.id}
              >
                <span>
                  {item.name}{" "}
                  <span className="ino:text-muted-foreground">#{item.id}</span>
                </span>

                <XMarkIcon
                  role="button"
                  onClick={() => handleRemoveItem(item.id)}
                  className="ino:size-4 ino:cursor-pointer ino:rounded-full ino:p-0.5 ino:text-muted-foreground ino:transition-colors ino:hover:bg-red-500/10 ino:hover:text-red-600"
                />
              </div>
            ))}
          <input
            onFocus={(e) => {
              if (e.target.value.length > 0) {
                setShowSearchResult(true);
              }
            }}
            ref={searchRef}
            onChange={handleSearch}
            className="ino:h-7 ino:min-h-0! ino:min-w-24 ino:flex-1 ino:border-0! ino:bg-transparent! ino:px-1! ino:py-0! ino:text-sm ino:text-foreground! ino:shadow-none! ino:outline-none!"
            type="text"
            placeholder={placeHolder}
          />
          {isFetching && !skip ? (
            <LoadingSpinner className="ino:mr-1 ino:size-4 ino:shrink-0" />
          ) : null}
        </div>
      </div>
      {showSearchResult && (
        <div
          className={`ino:absolute ino:z-50 ino:mt-1.5 ${widthClass} ino:rounded-lg ino:shadow-lg`}
        >
          <div className="ino:overflow-hidden ino:rounded-lg ino:border ino:border-border ino:bg-popover">
            {content}
          </div>
        </div>
      )}
    </div>
  );
};
export default AsyncMultiSelect;
