"use client";

import clsx from "clsx";
import { motion, Variants } from "framer-motion";
import { Icon, SearchNormal } from "iconsax-react";
import { ChevronDown } from "lucide-react";
import React, { useCallback, useEffect, useRef, useState } from "react";
import { createPortal } from "react-dom";

export interface ComboboxOption {
  id: string;
  title: string;
  icon?: Icon;
  imagePath?: string;
  label?: string;
}

type Props = {
  title: string;
  options: Array<ComboboxOption>;
  expanded?: boolean;
  selectedId: string;
  searchable?: boolean;
  placeholder?: string;
  icon?: React.ElementType;
  onSelectionChange: (
    e: React.MouseEvent<HTMLDivElement, MouseEvent>,
    index: number,
  ) => void;
} & React.HTMLAttributes<HTMLDivElement>;

type DropdownPosition = {
  top: number;
  left: number;
  width: number;
};

function Combobox({
  title,
  options,
  placeholder,
  icon: Icon,
  expanded,
  selectedId,
  searchable = true,
  onSelectionChange,
  className,
  ...props
}: Props) {
  const [expand, setExpand] = useState(expanded);
  const [searchValue, setSearchValue] = useState("");
  const [dropdownPosition, setDropdownPosition] = useState<DropdownPosition | null>(null);
  const boxRef = useRef<HTMLDivElement>(null);
  const triggerRef = useRef<HTMLDivElement>(null);
  const dropdownRef = useRef<HTMLDivElement>(null);

  const updateDropdownPosition = useCallback(() => {
    if (!triggerRef.current) return;

    const rect = triggerRef.current.getBoundingClientRect();
    setDropdownPosition({
      top: rect.bottom + 4,
      left: rect.left,
      width: rect.width,
    });
  }, []);

  useEffect(() => {
    if (!expand) {
      setDropdownPosition(null);
      return;
    }

    updateDropdownPosition();
    window.addEventListener("scroll", updateDropdownPosition, true);
    window.addEventListener("resize", updateDropdownPosition);

    return () => {
      window.removeEventListener("scroll", updateDropdownPosition, true);
      window.removeEventListener("resize", updateDropdownPosition);
    };
  }, [expand, updateDropdownPosition]);

  useEffect(() => {
    const handleClickOutside = (event: MouseEvent) => {
      const target = event.target as Node;
      if (
        boxRef.current?.contains(target) ||
        dropdownRef.current?.contains(target)
      ) {
        return;
      }
      setExpand(false);
    };

    if (expand) {
      document.addEventListener("mousedown", handleClickOutside);
    }

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

  const toggleExpand = () => {
    setExpand((prev) => !prev);
    setSearchValue("");
  };

  const searchChanged = (e: React.ChangeEvent<HTMLInputElement>) => {
    setSearchValue(e.target.value);
  };

  const setSelection = (
    e: React.MouseEvent<HTMLDivElement, MouseEvent>,
    index: number,
  ) => {
    e.stopPropagation();
    onSelectionChange(e, index);
    setExpand(false);
  };

  const selectedOption = options.find((x) => x.id === selectedId);
  const filteredOptions = options.filter((v) => v.title?.includes(searchValue));

  const activeIcon =
    selectedOption?.icon &&
    React.createElement(selectedOption.icon, {
      size: 16,
      color: "currentColor",
      className: "inline-block mr-1 text-primary dark:text-foreground",
    });
  const activeImage = selectedOption?.imagePath && (
    <span className="icon-delivery mr-1" aria-hidden />
  );

  const variants: Variants = {
    collapse: {
      opacity: 0,
      scale: 0.95,
      pointerEvents: "none",
      transition: {
        duration: 0.1,
        ease: "easeInOut",
      },
    },
    expand: {
      opacity: 1,
      scale: 1,
      pointerEvents: "auto",
      transition: {
        duration: 0.1,
        ease: "easeInOut",
      },
    },
  };

  const dropdown =
    expand &&
    dropdownPosition &&
    typeof document !== "undefined"
      ? createPortal(
          <motion.div
            ref={dropdownRef}
            data-expand={expand}
            aria-hidden={!expand}
            className="fixed text-foreground bg-container rounded-xl border border-border shadow-lg z-9999"
            style={{
              top: dropdownPosition.top,
              left: dropdownPosition.left,
              width: dropdownPosition.width,
            }}
            role="listbox"
            aria-label={`${title} options`}
            onClick={(e) => e.stopPropagation()}
            initial="collapse"
            animate="expand"
            variants={variants}
          >
            {searchable && (
              <div className="w-full flex gap-2 border-b border-border px-3 items-center">
                <SearchNormal size={16} className="stroke-gray-400" />
                <input
                  placeholder="جستجو ..."
                  className="w-full outline-none text-sm2 pb-2 pt-3"
                  onChange={searchChanged}
                  value={searchValue}
                />
              </div>
            )}
            <div className="flex flex-col p-1">
              {filteredOptions.map((v) => {
                const originalIndex = options.findIndex((o) => o.id === v.id);

                return (
                  <div
                    onClick={(e) => setSelection(e, originalIndex)}
                    key={v.id}
                    data-selected={v.id === selectedId}
                    className="flex gap-3 items-center justify-end px-2 py-2 cursor-pointer hover:bg-current/10 rounded-md last:rounded-b-xl"
                    tabIndex={0}
                    role="option"
                    aria-selected={v.id === selectedId}
                  >
                    {v?.imagePath ? (
                      <span className="icon-delivery shrink-0" aria-hidden />
                    ) : (
                      v?.icon &&
                      React.createElement(v.icon, {
                        size: 16,
                        color: "currentColor",
                        className:
                          "inline-block shrink-0 text-primary dark:text-foreground",
                      })
                    )}
                    <span className="text-sm2 tracking-[0.13px] leading-5 flex-1 text-right">
                      {v.title}
                    </span>
                  </div>
                );
              })}
            </div>
          </motion.div>,
          document.body,
        )
      : null;

  return (
    <div
      ref={boxRef}
      className={clsx("relative", className)}
      {...props}
    >
      <div
        ref={triggerRef}
        className="flex w-full h-11 items-center justify-end gap-2 px-3 py-4 relative bg-container/29 rounded-xl border border-solid border-border cursor-pointer"
        tabIndex={0}
        onClick={toggleExpand}
        role="combobox"
        aria-controls=""
        aria-expanded={expand}
        aria-haspopup="listbox"
        aria-label={title}
      >
        <label
          htmlFor="content-select"
          className="pointer-events-none inline-flex flex-col items-end justify-center px-1 py-0 absolute -top-6 right-0 z-2"
        >
          <span className="relative text-foreground w-fit -mt-px text-xs tracking-[0] leading-4 whitespace-nowrap">
            {title}
          </span>
        </label>

        <div className="w-full text-sm2 flex items-center justify-start gap-3">
          {activeImage
            ? activeImage
            : activeIcon
              ? activeIcon
              : Icon && (
                  <Icon
                    size={16}
                    color="currentColor"
                    className="inline-block mr-1 text-primary dark:text-foreground"
                  />
                )}
          <span
            className={clsx(
              selectedOption?.title ? "mt-0.5" : "mt-1 text-sm font-light",
            )}
          >
            {selectedOption?.title ?? placeholder}
          </span>
        </div>

        <ChevronDown
          size={20}
          data-expand={expand}
          className="transition-all stroke-foreground duration-200 data-[expand=true]:rotate-x-180"
        />
      </div>

      {dropdown}
    </div>
  );
}

export default Combobox;
