import React, {
  useState,
  useEffect,
  useRef,
  useId,
  useMemo,
  useCallback,
} from "react";
import { ChevronDownIcon, CheckIcon } from "@heroicons/react/24/outline";
import { getSizeStyles } from "../../data/getInputSizes";
import Textfield from "../Textfeild/Textfeild";
import { motion } from "framer-motion";
import { Tooltip } from "../Tooltip/Tooltip";
import { createPortal } from "react-dom";
import { checkIsMobile } from "../../utils/checkIsMobile";

interface DataItem {
  key: number | string;
  value: string;
  disabled?: boolean;
  isGroupHeader?: boolean;
  originalGroupKey?: string | number;
}

interface AutoCompleteProps {
  data: DataItem[];
  multiselect?: boolean;
  inPage?: boolean;
  disabled?: boolean;
  selectedKeys: (number | string)[];
  onChange: (keys: (number | string)[]) => void | [];
  width?: string;
  buttonHeight?: number | string;
  title?: string;
  error?: boolean;
  size?: "small" | "medium" | "large";
  helperText?: string;
  onChangeValue?: (data: { value: string; key: number | string }) => void;
  onGroupHeaderClick?: (groupKey: string | number) => void;
  selectField?: boolean;
}

const AutoComplete: React.FC<AutoCompleteProps> = ({
  data,
  multiselect = false,
  selectedKeys,
  onChange,
  disabled = false,
  inPage = false,
  title = "",
  error = false,
  size = "medium",
  helperText,
  onChangeValue,
  onGroupHeaderClick,
  selectField = false,
}) => {
  const [filteredData, setFilteredData] = useState<DataItem[]>(data);
  const [showOptions, setShowOptions] = useState<boolean>(false);
  const [dropdownWidth, setDropdownWidth] = useState<number>(0);
  const [dropdownPosition, setDropdownPosition] = useState<{
    top: number;
    left: number;
  }>({ top: 0, left: 0 });
  const [maxHeight, setMaxHeight] = useState<number>(240);

  const inputRef = useRef<HTMLInputElement>(null);
  const dropdownRef = useRef<HTMLUListElement>(null);
  const uniqueId = useId();
  const selectedKeysRef = useRef<(number | string)[]>(selectedKeys);
  const isInternalChangeRef = useRef<boolean>(false);

  useEffect(() => {
    const updateDropdownDimensions = () => {
      if (inputRef.current) {
        const rect = inputRef.current.getBoundingClientRect();
        const defaultMaxHeight = 240;
        const spaceBelow = window.innerHeight - rect.bottom;
        const availableHeight = Math.max(100, spaceBelow - 10);
        const calculatedMaxHeight =
          spaceBelow < defaultMaxHeight ? availableHeight : defaultMaxHeight;

        setDropdownWidth(rect.width);
        setMaxHeight(calculatedMaxHeight);
        setDropdownPosition({
          top: rect.bottom + window.scrollY,
          left: rect.left + window.scrollX,
        });
      }
    };

    updateDropdownDimensions();

    const resizeObserver = new ResizeObserver(updateDropdownDimensions);
    if (inputRef.current) {
      resizeObserver.observe(inputRef.current);
    }

    window.addEventListener("resize", updateDropdownDimensions);
    window.addEventListener("scroll", updateDropdownDimensions);

    return () => {
      resizeObserver.disconnect();
      window.removeEventListener("resize", updateDropdownDimensions);
      window.removeEventListener("scroll", updateDropdownDimensions);
    };
  }, []);

  useEffect(() => {
    if (!showOptions) return;

    let animationFrameId: number;
    let isActive = true;
    let lastTop = 0;
    let lastLeft = 0;
    let lastWidth = 0;
    let lastMaxHeight = 0;

    const updatePosition = (force = false) => {
      if (!isActive || !inputRef.current) return;

      const rect = inputRef.current.getBoundingClientRect();
      const defaultMaxHeight = 240;
      const viewportHeight =
        window.visualViewport?.height || window.innerHeight;
      const spaceBelow = viewportHeight - rect.bottom;
      const availableHeight = Math.max(100, spaceBelow - 10);
      const calculatedMaxHeight =
        spaceBelow < defaultMaxHeight ? availableHeight : defaultMaxHeight;

      const newTop = rect.bottom + window.scrollY;
      const newLeft = rect.left + window.scrollX;
      const newWidth = rect.width;

      if (
        force ||
        Math.abs(newTop - lastTop) > 0.5 ||
        Math.abs(newLeft - lastLeft) > 0.5 ||
        Math.abs(newWidth - lastWidth) > 0.5 ||
        Math.abs(calculatedMaxHeight - lastMaxHeight) > 1
      ) {
        setDropdownWidth(newWidth);
        setMaxHeight(calculatedMaxHeight - 30);
        setDropdownPosition({
          top: newTop,
          left: newLeft,
        });
        lastTop = newTop;
        lastLeft = newLeft;
        lastWidth = newWidth;
        lastMaxHeight = calculatedMaxHeight;
      }

      if (isActive && showOptions) {
        animationFrameId = requestAnimationFrame(() => updatePosition(false));
      }
    };

    updatePosition();

    const handleResize = () => updatePosition(true);
    const handleScroll = () => updatePosition();
    let lastViewportHeight =
      window.visualViewport?.height || window.innerHeight;
    const handleVisualViewportResize = () => {
      const currentHeight = window.visualViewport?.height || window.innerHeight;
      const heightDiff = Math.abs(currentHeight - lastViewportHeight);
      lastViewportHeight = currentHeight;

      if (heightDiff > 50) {
        setTimeout(() => {
          updatePosition(true);
          setTimeout(() => updatePosition(true), 200);
          setTimeout(() => updatePosition(true), 400);
        }, 50);
      } else {
        setTimeout(() => updatePosition(true), 50);
      }
    };
    const handleVisualViewportScroll = () => updatePosition();
    const handleFocus = () => {
      setTimeout(() => updatePosition(true), 300);
    };
    const handleBlur = () => {
      setTimeout(() => {
        updatePosition(true);
        setTimeout(() => updatePosition(true), 200);
        setTimeout(() => updatePosition(true), 400);
      }, 100);
    };

    window.addEventListener("resize", handleResize);
    window.addEventListener("scroll", handleScroll, true);

    if (checkIsMobile()) {
      if (window.visualViewport) {
        window.visualViewport.addEventListener(
          "resize",
          handleVisualViewportResize
        );
        window.visualViewport.addEventListener(
          "scroll",
          handleVisualViewportScroll
        );
      }
      const inputElement = inputRef.current;
      if (inputElement) {
        inputElement.addEventListener("focus", handleFocus);
        inputElement.addEventListener("blur", handleBlur);
        inputElement.addEventListener("touchstart", handleFocus);
      }
    }

    return () => {
      isActive = false;
      if (animationFrameId) {
        cancelAnimationFrame(animationFrameId);
      }
      window.removeEventListener("resize", handleResize);
      window.removeEventListener("scroll", handleScroll, true);
      if (checkIsMobile()) {
        if (window.visualViewport) {
          window.visualViewport.removeEventListener(
            "resize",
            handleVisualViewportResize
          );
          window.visualViewport.removeEventListener(
            "scroll",
            handleVisualViewportScroll
          );
        }
        const inputElement = inputRef.current;
        if (inputElement) {
          inputElement.removeEventListener("focus", handleFocus);
          inputElement.removeEventListener("blur", handleBlur);
          inputElement.removeEventListener("touchstart", handleFocus);
        }
      }
    };
  }, [showOptions]);

  useEffect(() => {
    const handleClickOutside = (event: MouseEvent) => {
      const target = event.target as HTMLElement;
      const clickedInsideCurrent = target.closest(`.select-group-${uniqueId}`);
      const clickedOnAnotherAutocomplete =
        target.closest(".select-group") && !clickedInsideCurrent;

      const clickedOnPortalDropdown = target.closest(
        `[data-autocomplete-portal="${uniqueId}"]`
      );

      if (clickedOnAnotherAutocomplete) {
        setShowOptions(false);
        return;
      }

      if (!clickedInsideCurrent && !clickedOnPortalDropdown) {
        setTimeout(() => {
          const isInputFocused = document.activeElement === inputRef.current;
          if (!isInputFocused) {
            setShowOptions(false);
          }
        }, 0);
      }
    };

    document.addEventListener("mousedown", handleClickOutside);
    return () => {
      document.removeEventListener("mousedown", handleClickOutside);
    };
  }, [uniqueId]);

  useEffect(() => {
    setFilteredData(data);
  }, [data]);

  useEffect(() => {
    selectedKeysRef.current = selectedKeys;
  }, [selectedKeys]);

  useEffect(() => {
    if (isInternalChangeRef.current) {
      isInternalChangeRef.current = false;
      return;
    }

    if (selectedKeys?.length > 0 && onChangeValue) {
      const selectedItem = data.find((item) => item.key === selectedKeys[0]);
      if (selectedItem) {
        onChangeValue({
          value: selectedItem.value.trim(),
          key: selectedItem.key,
        });
      }
    }
  }, [selectedKeys, data]);

  useEffect(() => {
    if (!showOptions) {
      setIsTyping(false);
    }
  }, [showOptions]);

  useEffect(() => {
    if (!showOptions || !checkIsMobile()) return;

    const originalOverflow = window.getComputedStyle(document.body).overflow;
    const originalPosition = window.getComputedStyle(document.body).position;
    const originalTop = document.body.style.top;
    const scrollY = window.scrollY;

    document.body.style.overflow = "hidden";
    document.body.style.position = "fixed";
    document.body.style.top = `-${scrollY}px`;
    document.body.style.width = "100%";

    const preventTouchMove = (e: TouchEvent) => {
      const target = e.target as HTMLElement;
      const dropdown = document.querySelector(
        `[data-autocomplete-portal="${uniqueId}"]`
      );

      if (dropdown) {
        const touch = e.touches[0] || e.changedTouches[0];
        if (touch) {
          const elementAtPoint = document.elementFromPoint(
            touch.clientX,
            touch.clientY
          );
          if (
            elementAtPoint &&
            (dropdown.contains(elementAtPoint) || dropdown.contains(target))
          ) {
            return;
          }
        } else if (dropdown.contains(target)) {
          return;
        }
      }

      e.preventDefault();
    };

    document.addEventListener("touchmove", preventTouchMove, {
      passive: false,
    });

    return () => {
      document.body.style.overflow = originalOverflow;
      document.body.style.position = originalPosition;
      document.body.style.top = originalTop;
      document.body.style.width = "";
      window.scrollTo(0, scrollY);
      document.removeEventListener("touchmove", preventTouchMove);
    };
  }, [showOptions, uniqueId]);

  const inputValue = useMemo(() => {
    if (selectedKeys?.length > 0) {
      const selectedValues = data
        .filter((item) => selectedKeys?.includes(item.key))
        .map((item) => item.value);
      return selectedValues?.join(", ");
    }
    return "";
  }, [selectedKeys, data]);

  const [localInputValue, setLocalInputValue] = useState<string>("");
  const [isTyping, setIsTyping] = useState<boolean>(false);

  const handleInputChange = useCallback(
    (event: React.ChangeEvent<HTMLInputElement>) => {
      const value = event.target.value;
      setLocalInputValue(value);
      setIsTyping(true);
      const filtered = data.filter((item) =>
        item.value.toLowerCase().includes(value.toLowerCase())
      );
      setFilteredData(filtered);
      setShowOptions(true);
    },
    [data]
  );

  const handleChange = useCallback(
    (newSelectedKeys: (number | string)[]) => {
      isInternalChangeRef.current = true;
      onChange(newSelectedKeys);

      if (onChangeValue && newSelectedKeys.length > 0) {
        const selectedItem = data.find(
          (item) => item.key === newSelectedKeys[0]
        );
        if (selectedItem) {
          onChangeValue({
            value: selectedItem.value.trim(),
            key: selectedItem.key,
          });
        }
      }
    },
    [onChange, onChangeValue, data]
  );

  const handleOptionClick = useCallback(
    (key: number | string) => {
      const currentSelectedKeys = selectedKeysRef.current;
      let newSelectedKeys: (number | string)[];

      if (multiselect) {
        if (currentSelectedKeys.includes(key)) {
          newSelectedKeys = currentSelectedKeys.filter((item) => item !== key);
        } else {
          newSelectedKeys = [...currentSelectedKeys, key];
        }
      } else {
        if (currentSelectedKeys.includes(key)) {
          newSelectedKeys = currentSelectedKeys.filter((item) => item !== key);
        } else {
          newSelectedKeys = [key];
        }
      }

      handleChange(newSelectedKeys);

      setIsTyping(false);
      if (!multiselect) {
        setLocalInputValue("");
        setShowOptions(false);
      }
    },
    [multiselect, handleChange]
  );

  const handleInputClick = useCallback(() => {
    document.querySelectorAll(".select-group").forEach((el) => {
      if (!el.classList.contains(`select-group-${uniqueId}`)) {
        const input = el.querySelector("input");
        if (input) {
          input.blur();
        }
      }
    });

    setShowOptions(true);
    setFilteredData(data);
    setLocalInputValue("");
    setIsTyping(false);
  }, [uniqueId, data]);

  const handleCloseInput = useCallback(() => {
    setShowOptions(false);
    setIsTyping(false);
  }, []);

  const selectedKeysSet = useMemo(() => new Set(selectedKeys), [selectedKeys]);

  const handleSelectAll = useCallback(() => {
    const enabledItems = filteredData.filter((item) => !item.disabled);
    const allEnabledKeys = enabledItems.map((item) => item.key);
    handleChange(allEnabledKeys);
  }, [filteredData, handleChange]);

  const handleDeselectAll = useCallback(() => {
    handleChange([]);
  }, [handleChange]);

  const areAllSelected = useMemo(() => {
    const enabledItems = filteredData.filter((item) => !item.disabled);
    return (
      enabledItems.length > 0 &&
      enabledItems.every((item) => selectedKeysSet.has(item.key))
    );
  }, [filteredData, selectedKeysSet]);

  const dropdownOptions = useMemo(() => {
    if (filteredData.length === 0) {
      return (
        <li className="px-4 py-3 text-gray-500 dark:text-dark-400 text-center">
          نتیجه‌ای یافت نشد
        </li>
      );
    }

    const selectAllHeader = multiselect ? (
      <li
        key="select-all-header"
        onClick={areAllSelected ? handleDeselectAll : handleSelectAll}
        className="flex items-center my-1 justify-start gap-2 px-4 py-2 cursor-pointer transition-colors duration-150 rounded-md border border-gray-200 dark:border-dark-600 hover:bg-primary-100 text-dark-800 dark:text-dark-100 dark:hover:bg-dark-700 bg-gray-50 dark:bg-dark-700 font-semibold"
      >
        <span className="text-sm">
          {areAllSelected ? "عدم انتخاب همه" : "انتخاب همه"}
        </span>
        {areAllSelected && (
          <CheckIcon className="w-4 h-4 text-primary-600 dark:text-primary-400 shrink-0" />
        )}
      </li>
    ) : null;

    const options = filteredData.map((item) => {
      const isSelected = selectedKeysSet.has(item.key);
      const isGroupHeader = item.isGroupHeader;
      const handleClick = () => {
        if (isGroupHeader && onGroupHeaderClick) {
          const groupKey =
            item.originalGroupKey !== undefined
              ? item.originalGroupKey
              : String(item.key).startsWith("__group__")
              ? String(item.key).slice(11)
              : item.key;
          onGroupHeaderClick(groupKey);
        } else if (!item.disabled) {
          handleOptionClick(item.key);
        }
      };
      return (
        <li
          key={`${item.key}`}
          onClick={handleClick}
          className={`flex items-center justify-between px-4 py-2 transition-colors duration-150 rounded-md 
            ${
              isGroupHeader && onGroupHeaderClick
                ? "cursor-pointer opacity-55 hover:bg-gray-100 text-dark-800 dark:text-dark-100 dark:hover:bg-primary-900/90 font-semibold bg-gray-200 dark:bg-primary-900"
                : item.disabled
                ? "text-gray-400 dark:text-dark-500 cursor-not-allowed"
                : "cursor-pointer hover:bg-primary-100 text-dark-800 dark:text-dark-100 dark:hover:bg-dark-700"
            }
            ${
              isSelected && !isGroupHeader
                ? "bg-primary-50 dark:bg-dark-700 font-semibold"
                : ""
            }
          `}
          aria-disabled={item?.disabled && !isGroupHeader}
        >
          {checkIsMobile() ? (
            <span
              className={`truncate ${
                item?.value.length > 55 ? "text-xs" : "text-sm"
              }`}
            >
              {item.value}
            </span>
          ) : (
            <Tooltip
              title={item.value}
              hidden={item?.value?.length < 55}
              position="right"
            >
              <span
                className={`truncate ${
                  item?.value.length > 55 ? "text-xs" : "text-sm"
                }`}
              >
                {item.value}
              </span>
            </Tooltip>
          )}
          {isSelected && (
            <CheckIcon className="w-4 h-4 text-primary-600 dark:text-primary-400 shrink-0" />
          )}
        </li>
      );
    });

    return selectAllHeader ? [selectAllHeader, ...options] : options;
  }, [
    filteredData,
    selectedKeysSet,
    handleOptionClick,
    multiselect,
    areAllSelected,
    handleSelectAll,
    handleDeselectAll,
    onGroupHeaderClick,
  ]);

  const dropdownPortalContent = useMemo(() => {
    if (!showOptions) return null;

    return createPortal(
      <motion.ul
        ref={dropdownRef}
        data-autocomplete-portal={`${uniqueId}`}
        initial={{ opacity: 0, scaleY: 0.95, y: -5 }}
        animate={{ opacity: 1, scaleY: 1, y: 0 }}
        transition={{ duration: 0.25, ease: "easeOut" }}
        style={{
          position: "fixed",
          top: dropdownPosition.top,
          left: dropdownPosition.left,
          width: `${dropdownWidth}px`,
          maxHeight: `${maxHeight}px`,
          zIndex: 9999,
          transformOrigin: "top center",
          scrollbarWidth: "thin",
          scrollbarColor: "#cbd5e1 transparent",
          boxSizing: "border-box",
        }}
        className={`overflow-y-auto border border-gray-200 dark:border-dark-500 bg-white dark:bg-dark-800 divide-y divide-gray-100 dark:divide-dark-600 text-sm backdrop-blur-lg rounded-xl shadow-2xl modern-scrollbar`}
      >
        {dropdownOptions}
      </motion.ul>,
      document.body
    );
  }, [
    showOptions,
    dropdownPosition,
    dropdownWidth,
    uniqueId,
    dropdownOptions,
    maxHeight,
  ]);

  return (
    <div
      className={`select-group select-group-${uniqueId} ${
        inPage ? "w-auto" : "w-full"
      }`}
    >
      <div className="relative w-full">
        <div className="relative">
          <Textfield
            disabled={disabled}
            readOnly={selectField}
            inputMode={selectField ? "none" : undefined}
            handleCloseInput={handleCloseInput}
            error={error}
            helperText={helperText}
            ref={inputRef}
            isAutoComplete
            inputSize={size}
            value={isTyping ? localInputValue : inputValue}
            onChange={handleInputChange}
            onClick={handleInputClick}
            className="selected-value w-full p-3 pl-10 outline-0 rounded-lg border border-black-100 transition-all duration-200 text-right"
            placeholder={title || "انتخاب کنید..."}
          />
          <ChevronDownIcon
            className={`absolute left-3 text-dark-400 dark:text-dark-100 transition-transform duration-200  ${
              showOptions ? "transform rotate-180" : ""
            } ${getSizeStyles(size).icon}`}
          />
        </div>

        {dropdownPortalContent}
      </div>
    </div>
  );
};

export default AutoComplete;
