import React, {
  forwardRef,
  useEffect,
  useImperativeHandle,
  useRef,
  useState,
} from "react";
import { IoIosArrowDown } from "react-icons/io";

import { SelectComponentProps } from "../types/SelectComponent.types";

const generateRandomCharacters = (length: number = 8): string => {
  let randomCharacters = "";
  const characters =
    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
  const charactersLength = characters.length;

  for (let i = 0; i < length; i += 1) {
    randomCharacters += characters.charAt(
      Math.floor(Math.random() * charactersLength),
    );
  }

  return randomCharacters;
};

const SelectComponent = (
  {
    id,
    style,
    className,
    classNameLabel = ` text-xs mb-[12px] font-semibold block text-[#1a1f28] text-left`,
    classNameOpened,
    classNameOption,
    classNamePlaceholder,
    classNameSearchInput,
    label,
    placeholder,
    value,
    setValue,
    items,
    labelKey = "label",
    searchKey = "name",
    icon = <IoIosArrowDown size={16} />,
    iconTransitionSpeed,
    scrollToTopOnClose = true,
    maxHeightOpened = 150,
    disabled = false,
    styleOpened = {
      overflow: "auto",
      position: "absolute",
      opacity: 1,
      zIndex: 5,
      transition: "all 0.3s",
      visibility: "visible",
    },
    styleClosed = {
      overflow: "hidden",
      position: "absolute",
      visibility: "hidden",
      transition: "all 0.2s",
      zIndex: 3,
      opacity: 0,
    },
    onSelectOpen = () => {},
    onSelectClose = () => {},
    onSelectChange = () => {},
    renderOption = (item) => item,
    renderPlaceholder = (item) => item,
  }: SelectComponentProps,
  ref: any,
) => {
  // States
  const [open, setOpen] = useState(false);
  const [position, setPosition] = useState("");
  const [searchQuery, setSearchQuery] = useState("");

  // Const
  const computedItems = items || [];
  const isObject = computedItems.some((el) => typeof el === "object");
  const transitionMatch = styleClosed?.transition?.match(/(\d+(?:\.\d+)?)s/);
  const seconds = transitionMatch ? parseFloat(transitionMatch[1]) : 0;
  const computedId: string = id || generateRandomCharacters();

  // Refs
  const defaultRef = useRef<HTMLDivElement>(null);
  const selectRef = ref || defaultRef;
  const childRef = useRef<HTMLDivElement>(null);
  const selectOpenedRef = useRef<HTMLDivElement>(null);
  const searchInputRef = useRef<HTMLInputElement>(null);

  // Functions
  const checkPosition = (
    refOpened: React.RefObject<HTMLDivElement>,
    state: boolean,
  ) => {
    const selectOpened = refOpened?.current;
    if (selectOpened) {
      const selectTop = selectOpened.getBoundingClientRect().top;
      const windowHeight = window.innerHeight;

      if (!state) {
        if (windowHeight - selectTop < maxHeightOpened && childRef?.current) {
          setPosition("top");
          selectOpened.style.bottom = `${childRef?.current?.clientHeight}px`;
          selectOpened.style.top = `auto`;
        }
        if (windowHeight - selectTop >= maxHeightOpened && childRef?.current) {
          setPosition("bottom");
          selectOpened.style.top = `${childRef?.current.clientHeight}px`;
          selectOpened.style.bottom = "auto";
        }
      }
    }
  };

  // Function to filter items based on search query
  const filterItems = (query: string) => {
    if (searchKey) {
      return computedItems.filter((item) =>
        item[searchKey]?.toLowerCase().includes(query?.toLowerCase()),
      );
    } else {
      return computedItems;
    }
  };

  const handleSelectClick = () => {
    if (!disabled) {
      selectRef?.current?.toggle();
    }
  };

  // Methods
  useImperativeHandle(selectRef, () => ({
    open: () => {
      checkPosition(selectOpenedRef, open);
      setOpen(true);
      onSelectOpen();
    },
    close: () => {
      if (selectOpenedRef.current) {
        position === "top"
          ? (selectOpenedRef.current.style.bottom = "0")
          : (selectOpenedRef.current.style.top = "0");
      }
      checkPosition(selectOpenedRef, open);
      setOpen(false);
      onSelectClose();
    },
    toggle: () => {
      checkPosition(selectOpenedRef, open);
      setOpen((OldState) => {
        if (OldState) {
          onSelectClose();
        } else {
          onSelectOpen();
        }
        return !OldState;
      });
    },
    getValue: () => value,
    updateValue: (tempValue: any) => {
      setValue(tempValue);
      onSelectChange(tempValue);
    },
  }));

  // Theme Classes
  const computedClassName =
    className ||
    `p-4 min-w-[150px] rounded-[9px] text-xs bg-[var(--light)] border border-[rgba(230,230,230,0.93)]`;

  const computedClassNameOpened =
    classNameOpened ||
    `bg-white text-xs w-[calc(100%)] top-[-10px] left-[-0.95px] rounded-tl-none rounded-tr-none rounded-[8px] shadow-[0_4px_4px_0px_rgba(0,0,0,0.25)]`;

  const computedClassNameOption = classNameOption || `py-5 px-4`;

  const computedClassNamePlaceholder =
    classNamePlaceholder || `text-[#5c5c5c] opacity-50 text-s font-normal`;

  const computedClassNameSearchInput =
    classNameSearchInput || `border-0 outline-none`;

  const filteredItems = filterItems(searchQuery);

  useEffect(() => {
    if (open && searchInputRef.current) {
      searchInputRef.current.focus();
    }
  }, [open]);

  return (
    <div>
      {label && (
        <label className={classNameLabel} htmlFor={computedId}>
          {label}
        </label>
      )}
      <div ref={selectRef} style={style} id={computedId}>
        <div
          ref={childRef}
          style={{
            position: "relative",
            ...(open
              ? position === "top"
                ? {
                    borderTopLeftRadius: 0,
                    borderTopRightRadius: 0,
                  }
                : {
                    borderBottomLeftRadius: 0,
                    borderBottomRightRadius: 0,
                  }
              : {}),
            cursor: disabled ? "not-allowed" : "",
            backgroundColor: disabled ? "##fafafa" : "",
          }}
          className={`${computedClassName} outline-none relative justify-between`}
          onClick={handleSelectClick}
          onMouseLeave={() => {
            selectRef?.current.close();

            if (scrollToTopOnClose) {
              setTimeout(() => {
                if (selectOpenedRef?.current) {
                  selectOpenedRef.current.scrollTop = 0;
                }
              }, seconds * 1000);
            }
          }}
        >
          <div
            className={`selected w-full text-xs flex justify-between relative z-[4]`}
          >
            {/* Add search input */}
            {open && (
              <input
                ref={searchInputRef}
                type="text"
                placeholder="Search..."
                value={searchQuery}
                onChange={(e) => {
                  selectRef?.current.open();
                  setSearchQuery(e.target.value);
                }}
                className={computedClassNameSearchInput}
              />
            )}
            {!open && (
              <span className="flex items-center text-xs">
                {value ? (
                  isObject ? (
                    renderOption(value[labelKey], value)
                  ) : (
                    renderOption(value, {})
                  )
                ) : (
                  <span className={computedClassNamePlaceholder}>
                    {renderPlaceholder(placeholder)}
                  </span>
                )}
              </span>
            )}

            {!disabled && (
              <span
                className={open ? `transform rotate-180 mr-0` : `mr-0`}
                style={{
                  transition: `all ${iconTransitionSpeed || seconds}s`,
                }}
              >
                {icon}
              </span>
            )}
          </div>

          <div
            ref={selectOpenedRef}
            className={computedClassNameOpened}
            style={
              open
                ? position === "top"
                  ? {
                      ...styleOpened,
                      borderBottomLeftRadius: 0,
                      borderBottomRightRadius: 0,
                      maxHeight: maxHeightOpened,
                    }
                  : {
                      ...styleOpened,
                      borderTopLeftRadius: 0,
                      borderTopRightRadius: 0,
                      maxHeight: maxHeightOpened,
                    }
                : { ...styleClosed, maxHeight: 0 }
            }
          >
            {isObject
              ? filteredItems
                  .filter((item) => item.searchable !== false)
                  .map((option, i) => {
                    return (
                      <div
                        key={i}
                        className={computedClassNameOption}
                        onClick={() => {
                          selectRef?.current.updateValue(option);
                        }}
                      >
                        {renderOption(option[labelKey], option)}
                      </div>
                    );
                  })
              : filteredItems.map((option, i) => (
                  <div
                    key={i}
                    className={computedClassNameOption}
                    onClick={() => {
                      selectRef?.current.updateValue(option);
                    }}
                  >
                    {renderOption(option, {})}
                  </div>
                ))}
          </div>
        </div>
      </div>
    </div>
  );
};

export const SelectWithSearch = forwardRef(SelectComponent);
