/**
 * Copyright 2024 Phenix Real Time Solutions, Inc. Confidential and Proprietary. All Rights Reserved.
 */
import React, {useState, useEffect, useRef} from 'react';

import {IChannel} from 'interfaces';
import {Label} from 'components/label';

import {DropdownContainer, DropdownInput, DropdownMenu, DropdownMenuItem} from './style';

const keys = ['ArrowDown', 'ArrowUp', 'Enter', 'Escape'];

interface IDropdown {
  itemKey: string;
  searchTerm?: string;
  onSelect: (key: string) => void;
  items: IChannel[];
  label: string;
  name: string;
  className?: string;
}
const maxNumOfItemsShown = 30;

export const Dropdown = (props: IDropdown): React.JSX.Element => {
  const {itemKey, searchTerm, onSelect, items, label, name, className} = props;
  const [showDropdownMenu, setShowDropdownMenu] = useState(false);
  const [input, setInput] = useState(searchTerm || '');
  const [filteredItems, setFilteredItems] = useState(items);
  const [selectedIndex, setSelectedIndex] = useState(0);
  const allItemsRef = useRef<any[]>([]);
  const parentElementRef = useRef<HTMLDivElement | null>(null);
  const scrollSelectedItemInView = (index: number) => {
    if (!parentElementRef.current) {
      return;
    }

    const dropdownMenu = parentElementRef.current;
    const menuItems = allItemsRef.current;

    if (!menuItems) {
      return;
    }

    const menuItem = menuItems[index];

    if (!menuItem) {
      return;
    }

    const isOutOfUpperView = menuItem.offsetTop < dropdownMenu.scrollTop;
    const isOutOfLowerView = (menuItem.offsetTop + menuItem.clientHeight) > (dropdownMenu.scrollTop + dropdownMenu.clientHeight);

    if (isOutOfUpperView) {
      dropdownMenu.scrollTop = menuItem.offsetTop;
    } else if (isOutOfLowerView) {
      dropdownMenu.scrollTop = (menuItem.offsetTop + menuItem.clientHeight) - dropdownMenu.clientHeight;
    }
  };

  const setSelectInput = (value: string) => {
    setInput(value);
    onSelect(value);
  };

  const handleClickOutside = (event: MouseEvent) => {
    if (!parentElementRef.current || parentElementRef.current.contains(event.target as Node)) {
      return;
    }

    if (!filteredItems.length) {
      setSelectInput('');
    }

    setShowDropdownMenu(false);
    onSelect(input);
  };

  useEffect(() => {
    scrollSelectedItemInView(selectedIndex);
  }, [selectedIndex]);

  useEffect(() => {
    document.addEventListener('click', handleClickOutside, true);

    return () => {
      document.removeEventListener('click', handleClickOutside, true);
    };
  });

  useEffect(() => {
    const filteredItems = items.filter(item => {
      if ((item as any)[itemKey].toLowerCase().indexOf(input.toLowerCase()) > -1) {
        return true;
      }

      return false;
    });

    if (filteredItems.length > 0) {
      setSelectedIndex(0);
    }

    setFilteredItems(filteredItems);
  }, [input, items, itemKey]);

  const selectItemInFocusBy = (offset: number) => {
    const lastIndex = filteredItems.length - 1;
    const nextIndex = selectedIndex + offset;

    if (nextIndex > lastIndex) {
      setSelectedIndex(0);
    } else if (nextIndex < 0) {
      setSelectedIndex(lastIndex);
    } else {
      setSelectedIndex(nextIndex);
    }
  };

  const handleOnKeyDown = (event: React.KeyboardEvent) => {
    if (keys.indexOf(event.key) === -1) {
      return;
    }

    const [arrDown, arrUp, enter, escape] = keys;
    const moves = {
      [arrDown]: 1,
      [arrUp]: -1
    };
    const move = moves[event.key];

    if (move !== undefined) {
      event.preventDefault();
      selectItemInFocusBy(move);
    }

    if (event.key === enter) {
      if (filteredItems[selectedIndex]) {
        setSelectInput((filteredItems[selectedIndex] as any)[itemKey]);
        setShowDropdownMenu(false);
      }
    }

    if (event.key === escape) {
      event.preventDefault();
      setShowDropdownMenu(false);
    }
  };

  const selectItem = (index: number) => {
    setSelectInput((filteredItems[index] as any)[itemKey]);
    setSelectedIndex(index);
    setShowDropdownMenu(false);
  };

  const generateMenuOptions = () => {
    return filteredItems.length
      ? filteredItems.slice(0, maxNumOfItemsShown).map((item, index) => {
        return (
          <DropdownMenuItem
            ref={ref => {
              allItemsRef.current[index] = ref;
            }}
            selected={selectedIndex === index}
            key={`dropdown-menu-item-${index}`}
            onClick={() => selectItem(index)}
          >
            {(item as any)[itemKey]}
          </DropdownMenuItem>
        );
      })
      : (
        <DropdownMenuItem disabled={true}>
          No results found
        </DropdownMenuItem>
      );
  };

  const handleInput = (event: React.ChangeEvent<HTMLInputElement>) => {
    const {target} = event;
    setInput(target.value);

    if (!showDropdownMenu) {
      setShowDropdownMenu(true);
    }
  };

  const toggleDropdownMenu = () => setShowDropdownMenu(!showDropdownMenu);

  return (
    <DropdownContainer>
      <Label
        htmlFor="autocomplete"
        text={label}
      />
      <DropdownInput
        onKeyDown={handleOnKeyDown}
        showMenu={showDropdownMenu}
        autoComplete="off"
        name={name}
        onChange={handleInput}
        value={input}
        onClick={toggleDropdownMenu}
        className={className}
      />
      {showDropdownMenu && (
        <DropdownMenu ref={parentElementRef} className="testId-generatedMenuOptions">
          {generateMenuOptions()}
        </DropdownMenu>
      )}
    </DropdownContainer>
  );
};