'use client';

import { DropdownMenuProps } from '@radix-ui/react-dropdown-menu';
import React, { useEffect, useMemo } from 'react';
import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuGroup, //Do I need this?
  DropdownMenuLabel, //Do I need this?
  DropdownMenuItem,
  DropdownMenuPortal,
  DropdownMenuSeparator,
  DropdownMenuShortcut,
  DropdownMenuSub,
  DropdownMenuSubContent,
  DropdownMenuSubTrigger,
  DropdownMenuTrigger,
  DropdownMenuCheckboxItemSingle,
  DropdownMenuCheckboxItemMultiple,
  DropdownMenuLink
} from './DropdownBase';
import { CircleSpinnerLoader } from '../loaders/CircleSpinnerLoader';
import { useMemoizedFn } from '@/hooks';
import { cn } from '@/lib/classMerge';
import { Input } from '../inputs/Input';
import { useDebounceSearch } from '@/hooks';
import Link from 'next/link';
import { useHotkeys } from 'react-hotkeys-hook';

export interface DropdownItem<T = string> {
  label: React.ReactNode | string;
  truncate?: boolean;
  searchLabel?: string; // Used for filtering
  secondaryLabel?: string;
  value: T;
  shortcut?: string;
  onClick?: () => void;
  icon?: React.ReactNode;
  disabled?: boolean;
  loading?: boolean;
  selected?: boolean;
  items?: DropdownItems<T>;
  link?: string;
  linkIcon?: 'arrow-right' | 'arrow-external' | 'caret-right';
}

export interface DropdownDivider {
  type: 'divider';
}

export type DropdownItems<T = string> = (DropdownItem<T> | DropdownDivider | React.ReactNode)[];

export interface DropdownProps<T = string> extends DropdownMenuProps {
  items: DropdownItems<T>;
  selectType?: 'single' | 'multiple' | 'none';
  menuHeader?: string | React.ReactNode; //if string it will render a search box
  closeOnSelect?: boolean;
  onSelect?: (value: T) => void;
  align?: 'start' | 'center' | 'end';
  side?: 'top' | 'right' | 'bottom' | 'left';
  emptyStateText?: string;
  className?: string;
  footerContent?: React.ReactNode;
  showIndex?: boolean;
  contentClassName?: string;
  footerClassName?: string;
  sideOffset?: number;
  disabled?: boolean;
  menuHeaderClassName?: string;
}

export interface DropdownContentProps<T = string>
  extends Omit<DropdownProps<T>, 'align' | 'side'> {}

const dropdownItemKey = <T,>(item: DropdownItems<T>[number], index: number): string => {
  if ((item as DropdownDivider).type === 'divider') return `divider-${index}`;
  if ((item as DropdownItem<T>).value) return String((item as DropdownItem<T>).value);
  return `item-${index}`;
};

export const DropdownBase = <T,>({
  items,
  selectType = 'none',
  menuHeader,
  contentClassName = '',
  closeOnSelect = true,
  onSelect,
  children,
  align = 'start',
  side = 'bottom',
  open,
  onOpenChange,
  emptyStateText,
  className,
  footerContent,
  dir,
  modal,
  menuHeaderClassName,
  sideOffset,
  footerClassName = '',
  showIndex = false,
  disabled = false
}: DropdownProps<T>) => {
  return (
    <DropdownMenu
      open={open}
      defaultOpen={open}
      onOpenChange={onOpenChange}
      dir={dir}
      modal={modal}>
      <DropdownMenuTrigger asChild disabled={disabled}>
        <span className="dropdown-trigger">{children}</span>
      </DropdownMenuTrigger>
      <DropdownMenuContent
        className={cn('max-w-72 min-w-48', className)}
        align={align}
        side={side}
        sideOffset={sideOffset}>
        <DropdownContent
          items={items}
          selectType={selectType}
          menuHeader={menuHeader}
          closeOnSelect={closeOnSelect}
          onSelect={onSelect}
          showIndex={showIndex}
          emptyStateText={emptyStateText}
          footerContent={footerContent}
          className={contentClassName}
          footerClassName={footerClassName}
          menuHeaderClassName={menuHeaderClassName}
        />
      </DropdownMenuContent>
    </DropdownMenu>
  );
};
DropdownBase.displayName = 'Dropdown';
export const Dropdown = React.memo(DropdownBase) as unknown as typeof DropdownBase;

export const DropdownContent = <T,>({
  items,
  selectType,
  menuHeader,
  closeOnSelect = true,
  showIndex = false,
  emptyStateText = 'No items found',
  footerContent,
  className,
  menuHeaderClassName,
  footerClassName,
  onSelect
}: DropdownContentProps<T>) => {
  const { filteredItems, searchText, handleSearchChange } = useDebounceSearch({
    items,
    searchPredicate: (item, searchText) => {
      if ((item as DropdownItem).value) {
        const _item = item as DropdownItem;
        const searchContent =
          _item.searchLabel || (typeof _item.label === 'string' ? _item.label : '');
        return searchContent?.toLowerCase().includes(searchText.toLowerCase());
      }
      return true;
    },
    debounceTime: 50
  });

  const hasShownItem = useMemo(() => {
    return filteredItems.length > 0 && filteredItems.some((item) => (item as DropdownItem).value);
  }, [filteredItems]);

  const { selectedItems, unselectedItems } = useMemo(() => {
    if (selectType === 'multiple') {
      const [selectedItems, unselectedItems] = filteredItems.reduce(
        (acc, item) => {
          if ((item as DropdownItem).selected) {
            acc[0].push(item);
          } else {
            acc[1].push(item);
          }
          return acc;
        },
        [[], []] as [typeof filteredItems, typeof filteredItems]
      );
      return { selectedItems, unselectedItems };
    }
    return {
      selectedItems: [],
      unselectedItems: []
    };
  }, [selectType, filteredItems]);

  // Keep track of selectable item index
  let hotkeyIndex = -1;

  const dropdownItems = selectType === 'multiple' ? unselectedItems : filteredItems;

  const onSelectItem = useMemoizedFn((index: number) => {
    const correctIndex = dropdownItems.filter((item) => (item as DropdownItem).value);
    const item = correctIndex[index] as DropdownItem<T>;

    if (item) {
      const disabled = (item as DropdownItem).disabled;
      if (!disabled && onSelect) {
        onSelect(item.value);
        // Close the dropdown if closeOnSelect is true
        if (closeOnSelect) {
          const dropdownTrigger = document.querySelector('[data-state="open"][role="menu"]');
          if (dropdownTrigger) {
            const closeEvent = new KeyboardEvent('keydown', { key: 'Escape', bubbles: true });
            dropdownTrigger.dispatchEvent(closeEvent);
          }
        }
      }
    }
  });

  return (
    <>
      {menuHeader && (
        <div className="flex flex-col">
          <div className={cn('p-1', menuHeaderClassName)}>
            <DropdownMenuHeaderSelector
              menuHeader={menuHeader}
              onChange={handleSearchChange}
              text={searchText}
              onSelectItem={onSelectItem}
              showIndex={showIndex}
            />
          </div>
          <div className="bg-border h-[0.5px] w-full" />
        </div>
      )}

      <div
        className={cn('max-h-[375px] overflow-y-auto', className)}
        onWheel={(e) => {
          //this is need to prevent bug when it is inside a dialog or modal
          e.stopPropagation();
        }}>
        {hasShownItem ? (
          <>
            {selectedItems.map((item) => {
              // Only increment index for selectable items
              if ((item as DropdownItem).value && !(item as DropdownItem).items) {
                hotkeyIndex++;
              }

              return (
                <DropdownItemSelector
                  key={dropdownItemKey(item, hotkeyIndex)}
                  item={item}
                  index={hotkeyIndex}
                  selectType={selectType}
                  onSelect={onSelect}
                  onSelectItem={onSelectItem}
                  closeOnSelect={closeOnSelect}
                  showIndex={showIndex}
                />
              );
            })}

            {selectedItems.length > 0 && <DropdownMenuSeparator />}

            {dropdownItems.map((item) => {
              // Only increment index for selectable items
              if ((item as DropdownItem).value && !(item as DropdownItem).items) {
                hotkeyIndex++;
              }

              return (
                <DropdownItemSelector
                  key={dropdownItemKey(item, hotkeyIndex)}
                  item={item}
                  index={hotkeyIndex}
                  selectType={selectType}
                  onSelect={onSelect}
                  onSelectItem={onSelectItem}
                  closeOnSelect={closeOnSelect}
                  showIndex={showIndex}
                />
              );
            })}
          </>
        ) : (
          <DropdownMenuItem disabled className="text-gray-light text-center">
            {emptyStateText}
          </DropdownMenuItem>
        )}
      </div>

      {footerContent && <div className={cn('border-t p-1', footerClassName)}>{footerContent}</div>}
    </>
  );
};

const DropdownItemSelector = React.memo(
  <T,>({
    item,
    index,
    onSelect,
    onSelectItem,
    closeOnSelect,
    selectType,
    showIndex
  }: {
    item: DropdownItems<T>[number];
    index: number;
    onSelect?: (value: any) => void; // Using any here to resolve the type mismatch
    onSelectItem: (index: number) => void;
    closeOnSelect: boolean;
    showIndex: boolean;
    selectType: DropdownProps<T>['selectType'];
  }) => {
    if ((item as DropdownDivider).type === 'divider') {
      return <DropdownMenuSeparator />;
    }

    if (typeof item === 'object' && React.isValidElement(item)) {
      return item;
    }

    return (
      <DropdownItem
        {...(item as DropdownItem<T>)}
        closeOnSelect={closeOnSelect}
        onSelect={onSelect}
        onSelectItem={onSelectItem}
        selectType={selectType}
        index={index}
        showIndex={showIndex}
      />
    );
  }
);
DropdownItemSelector.displayName = 'DropdownItemSelector';

const DropdownItem = <T,>({
  label,
  value,
  showIndex,
  shortcut,
  onClick,
  icon,
  disabled = false,
  loading,
  selected,
  index,
  items,
  closeOnSelect,
  onSelect,
  onSelectItem,
  selectType,
  secondaryLabel,
  truncate,
  link,
  linkIcon
}: DropdownItem<T> & {
  onSelect?: (value: T) => void;
  onSelectItem: (index: number) => void;
  closeOnSelect: boolean;
  index: number;
  showIndex: boolean;
  selectType: DropdownProps<T>['selectType'];
}) => {
  const onClickItem = useMemoizedFn((e: React.MouseEvent<HTMLDivElement>) => {
    if (onClick) onClick();
    if (onSelect) onSelect(value as T);
  });
  const enabledHotKeys = showIndex && !disabled && !!onSelectItem;

  // Add hotkey support when showIndex is true
  useHotkeys(showIndex ? `${index}` : '', (e) => onSelectItem(index), {
    enabled: enabledHotKeys
  });

  const isSubItem = items && items.length > 0;
  const isSelectable = !!selectType && selectType !== 'none';

  // Helper function to render the content consistently with proper type safety
  const renderContent = () => {
    const content = (
      <>
        {icon && !loading && <span className="text-icon-color text-lg">{icon}</span>}
        <div className={cn('flex flex-col space-y-1', truncate && 'overflow-hidden')}>
          <span className={cn(truncate && 'truncate')}>{label}</span>
          {secondaryLabel && <span className="text-gray-light text-xs">{secondaryLabel}</span>}
        </div>
        {loading && <CircleSpinnerLoader size={9} />}
        {shortcut && <DropdownMenuShortcut>{shortcut}</DropdownMenuShortcut>}
        {link && (
          <DropdownMenuLink
            className="-mr-1 ml-auto opacity-0 group-hover:opacity-50 hover:opacity-100"
            link={isSelectable ? link : null}
            linkIcon={linkIcon}
          />
        )}
      </>
    );

    // Wrap with Link if needed
    if (!isSelectable && link) {
      return (
        <Link className="flex w-full items-center gap-x-2" href={link}>
          {content}
        </Link>
      );
    }

    return content;
  };

  if (isSubItem) {
    return (
      <DropdownSubMenuWrapper
        items={items}
        closeOnSelect={closeOnSelect}
        onSelect={onSelect}
        onSelectItem={onSelectItem}
        showIndex={showIndex}
        selectType={selectType}>
        {renderContent()}
      </DropdownSubMenuWrapper>
    );
  }

  //I do not think this selected check is stable... look into refactoring
  if (selectType === 'single') {
    return (
      <DropdownMenuCheckboxItemSingle
        checked={selected}
        disabled={disabled}
        onClick={onClickItem}
        index={showIndex ? index : undefined}
        closeOnSelect={closeOnSelect}>
        {renderContent()}
      </DropdownMenuCheckboxItemSingle>
    );
  }

  if (selectType === 'multiple') {
    return (
      <DropdownMenuCheckboxItemMultiple
        checked={selected}
        disabled={disabled}
        onClick={onClickItem}
        closeOnSelect={closeOnSelect}>
        {renderContent()}
      </DropdownMenuCheckboxItemMultiple>
    );
  }

  return (
    <DropdownMenuItem
      truncate={truncate}
      disabled={disabled}
      onClick={onClickItem}
      closeOnSelect={closeOnSelect}>
      {renderContent()}
    </DropdownMenuItem>
  );
};

interface DropdownSubMenuWrapperProps<T> {
  items: DropdownItems<T> | undefined;
  children: React.ReactNode;
  closeOnSelect: boolean;
  showIndex: boolean;
  onSelect?: (value: T) => void;
  onSelectItem: (index: number) => void;
  selectType: DropdownProps<T>['selectType'];
}

const DropdownSubMenuWrapper = <T,>({
  items,
  children,
  closeOnSelect,
  onSelect,
  onSelectItem,
  selectType,
  showIndex
}: DropdownSubMenuWrapperProps<T>) => {
  const subContentRef = React.useRef<HTMLDivElement>(null);
  const [isOpen, setIsOpen] = React.useState(false);

  const scrollToSelectedItem = React.useCallback(() => {
    if (!subContentRef.current) return;

    const selectedIndex = items?.findIndex((item) => (item as DropdownItem).selected);
    if (selectedIndex === undefined || selectedIndex === -1) return;

    const menuItems = subContentRef.current.querySelectorAll('[role="menuitem"]');
    const selectedElement = menuItems[selectedIndex - 1];

    if (selectedElement) {
      selectedElement.scrollIntoView({ block: 'start', behavior: 'instant', inline: 'start' });
    }
  }, [items]);

  useEffect(() => {
    if (isOpen) {
      const timeoutId = setTimeout(scrollToSelectedItem, 70);
      return () => clearTimeout(timeoutId);
    }
  }, [isOpen, scrollToSelectedItem]);

  return (
    <DropdownMenuSub onOpenChange={setIsOpen}>
      <DropdownMenuSubTrigger>{children}</DropdownMenuSubTrigger>
      <DropdownMenuPortal>
        <DropdownMenuSubContent
          ref={subContentRef}
          sideOffset={8}
          className="max-h-[375px] overflow-y-auto">
          {items?.map((item, index) => (
            <DropdownItemSelector
              key={dropdownItemKey(item, index)}
              item={item}
              index={index}
              onSelect={onSelect}
              onSelectItem={onSelectItem}
              closeOnSelect={closeOnSelect}
              selectType={selectType}
              showIndex={showIndex}
            />
          ))}
        </DropdownMenuSubContent>
      </DropdownMenuPortal>
    </DropdownMenuSub>
  );
};

const DropdownMenuHeaderSelector = <T,>({
  menuHeader,
  onChange,
  onSelectItem,
  text,
  showIndex
}: {
  menuHeader: NonNullable<DropdownProps<T>['menuHeader']>;
  onSelectItem: (index: number) => void;
  onChange: (text: string) => void;
  text: string;
  showIndex: boolean;
}) => {
  if (typeof menuHeader === 'string') {
    return (
      <DropdownMenuHeaderSearch
        showIndex={showIndex}
        placeholder={menuHeader}
        onChange={onChange}
        onSelectItem={onSelectItem}
        text={text}
      />
    );
  }
  return menuHeader;
};
DropdownMenuHeaderSelector.displayName = 'DropdownMenuHeaderSelector';

interface DropdownMenuHeaderSearchProps<T> {
  text: string;
  onChange: (text: string) => void;
  onSelectItem: (index: number) => void;
  placeholder?: string;
  showIndex: boolean;
}

const DropdownMenuHeaderSearch = <T,>({
  text,
  onChange,
  onSelectItem,
  showIndex,
  placeholder
}: DropdownMenuHeaderSearchProps<T>) => {
  const onChangePreflight = useMemoizedFn((e: React.ChangeEvent<HTMLInputElement>) => {
    e.stopPropagation();
    e.preventDefault();
    onChange(e.target.value);
  });

  const onKeyDownPreflight = useMemoizedFn((e: React.KeyboardEvent<HTMLInputElement>) => {
    const isFirstCharacter = (e.target as HTMLInputElement).value.length === 0;

    // Only prevent default for digit shortcuts when showIndex is true
    if (showIndex && isFirstCharacter && /^Digit[0-9]$/.test(e.code)) {
      e.preventDefault();
      const index = parseInt(e.key);
      onSelectItem?.(index);
    } else {
      e.stopPropagation();
    }
  });

  return (
    <div className="flex items-center gap-x-2">
      <Input
        autoFocus
        variant={'ghost'}
        size={'small'}
        placeholder={placeholder}
        value={text}
        onChange={onChangePreflight}
        onKeyDown={onKeyDownPreflight}
      />
    </div>
  );
};

DropdownMenuHeaderSearch.displayName = 'DropdownMenuHeaderSearch';
