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

import {Tag, ITag} from 'components/tags';
import {isEqual} from 'components/shared/utils';
import {Label} from 'components/forms/label';

import {AdvancedSelectContainer, ComponentWrapper, InputContainer, SelectInput, TagOptions} from './style';

export interface IAdvancedSelectItem {
  value: string;
  type: string[];
  dependency?: string[];
  set?: string[];
}

interface IKeyboardEventWithTarget extends KeyboardEvent<HTMLInputElement> {
  target: HTMLInputElement;
}

const advancedSelectHeight = 250;

interface IAdvancedSelect {
  selectedItems: IAdvancedSelectItem[];
  setSelectedItems: (items: IAdvancedSelectItem[]) => void;
  data: IAdvancedSelectItem[];
  label?: string;
  labelColor?: string;
  labelIcon?: React.JSX.Element;
  labelClassName?: string;
  labelHtmlFor?: string;
  width?: number | string;
  className?: string;
  allowMultiple?: boolean;
}

export const AdvancedSelect = ({
  allowMultiple = true,
  selectedItems,
  setSelectedItems,
  data,
  label,
  labelColor,
  labelIcon,
  labelClassName,
  labelHtmlFor,
  width,
  className
}: IAdvancedSelect): React.JSX.Element => {
  const [inputValue, setInputValue] = useState<string>('');
  const [selectedSets, setSelectedSets] = useState<string[]>([]);
  const firstUpdate = useRef(true);
  const ref = useRef(null);
  const scrollToBottom = (): void => {
    ref.current.scrollTop = ref.current.scrollHeight;
  };

  const getSelectedItemsSets = () => {
    const selectedSets = [];

    selectedItems.forEach((item: IAdvancedSelectItem) => {
      if (item.set) {
        item.set.forEach(set => {
          selectedSets.push(set);
        });
      }
    });

    setSelectedSets(selectedSets);
  };

  useEffect(() => {
    getSelectedItemsSets();
  }, []);

  useEffect(() => {
    if (firstUpdate.current) {
      firstUpdate.current = false;

      return;
    }

    const timeout = setTimeout(() => scrollToBottom(), 0);

    getSelectedItemsSets();

    return () => clearTimeout(timeout);
  }, [selectedItems]);

  const getTagsContainerHeight = () => {
    return advancedSelectHeight - 32 - ref?.current?.clientHeight;
  };

  const getItemDependencies = (item: IAdvancedSelectItem): IAdvancedSelectItem[] => {
    const allDependencies = [];
    const getNestedDependencies = (dependencyItem: IAdvancedSelectItem): void => {
      const itemAlreadySelected = selectedItems.some(selectedItem => selectedItem.value === dependencyItem.value);
      const selectedItemsSet = new Set(selectedItems.map(item => item.value));
      const dependenciesSet = new Set(allDependencies.map(item => item.value));
      const oneOfTheDependenciesAlreadySelected = item.dependency?.some(dependency => selectedItemsSet.has(dependency) || dependenciesSet.has(dependency));
      const itemIsANestedDependency = item.dependency?.includes(dependencyItem.value);

      if ((itemIsANestedDependency && oneOfTheDependenciesAlreadySelected) || itemAlreadySelected) {
        return;
      }

      allDependencies.push(dependencyItem);

      if (!dependencyItem.dependency) {
        return;
      }

      dependencyItem.dependency.forEach((dependencyName: string) => {
        const originalDependencyItem = data.find((element: IAdvancedSelectItem) => isEqual(dependencyName, element.value));

        getNestedDependencies(originalDependencyItem);
      });
    };

    getNestedDependencies(item);

    return allDependencies;
  };

  const selectItem = (item: IAdvancedSelectItem): void => {
    const itemsToSelect = getItemDependencies(item);

    setSelectedItems([...selectedItems, ...itemsToSelect]);
    setInputValue('');
  };

  const filterTags = (datum: IAdvancedSelectItem) => {
    return datum.value.toLowerCase().includes(inputValue.toLowerCase());
  };

  const generateAllItems = (): ReactElement<ITag>[] => {
    const itemsToSelectFrom = data.filter((datum: IAdvancedSelectItem) => !selectedItems.includes(datum)).filter(filterTags);

    return itemsToSelectFrom.map((datum: IAdvancedSelectItem) => (
      <Tag
        disabled={(!allowMultiple && !!selectedItems.length) || (datum.set && selectedSets.some(set => datum.set.includes(set)))}
        key={datum.value}
        label={datum.value}
        handleClick={() => selectItem(datum)}
      />
    ));
  };

  const deselectItem = (item: IAdvancedSelectItem): void => {
    const filteredItems = selectedItems.filter((datum: IAdvancedSelectItem) => {
      if (datum.dependency) {
        return !isEqual(datum, item) && !datum.dependency.includes(item.value);
      }

      return !isEqual(datum, item);
    });

    setSelectedItems(filteredItems);
  };

  const generateSelectedItems = (): ReactElement<ITag>[] => {
    return selectedItems.map((item: IAdvancedSelectItem) => (
      <Tag key={item.value} label={item.value} onClose={() => deselectItem(item)} className="testId-selectedItem" />
    ));
  };

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

  const onKeyDown = (event: IKeyboardEventWithTarget): void => {
    if (event.key === 'Enter') {
      const {value} = event.target;

      selectItem({
        value,
        type: []
      });
    }
  };

  return (
    <ComponentWrapper width={width}>
      {!!label && <Label text={label} color={labelColor} icon={labelIcon} className={labelClassName} htmlFor={labelHtmlFor} />}
      <AdvancedSelectContainer className={className}>
        <InputContainer ref={ref}>
          {generateSelectedItems()}
          <SelectInput
            disabled={!allowMultiple && !!selectedItems.length}
            className="testId-advancedSelectInput"
            onChange={handleInput}
            onKeyDown={onKeyDown}
            value={inputValue}
          />
        </InputContainer>
        <TagOptions height={getTagsContainerHeight() || 0}>{generateAllItems()}</TagOptions>
      </AdvancedSelectContainer>
    </ComponentWrapper>
  );
};
