import React, { useMemo } from "react";
import { RulePill } from "../../RulePill";
import {
  MultiSelect,
  MultiSelectHeadingChild,
  MultiSelectHeadingChildProps,
  MultiSelectFooterChild,
  MultiSelectFooterChildProps,
} from "@komi-app/components";
import { SelectOption } from "../../../types/select";


import { anyOption } from "../../../constants/segments/attributes";


import { noop } from "../../../utils/fns";
import { arrayify } from "../../../utils/array";
import { findValuesInOptions } from "../../../utils/select";

import classnames from "classnames";
import "../RuleBlock.scss";

export interface SelectRuleBlockProps<
  OptionType extends unknown = string,
  SelectOptionType extends SelectOption<OptionType> = SelectOption<OptionType>,
> extends React.HTMLProps<HTMLDivElement> {
  footers?: MultiSelectFooterChild<OptionType, SelectOptionType>[];
  headings?: MultiSelectHeadingChild<OptionType, SelectOptionType>[];
  selectedOptions?: SelectOptionType | SelectOptionType[];
  selectedValues?: OptionType | OptionType[];
  disabledValues?: OptionType[];
  isAnyOption?: boolean;
  isMultiselection?: boolean;
  loading?: boolean;
  options: SelectOptionType[];
  onOptionSelectionChange?: (values: SelectOptionType[]) => void;
  onValueSelectionChange?: (values: OptionType[]) => void;
}
export const SelectRuleBlock = <
  OptionType extends unknown = string,
  SelectOptionType extends SelectOption<OptionType> = SelectOption<OptionType>,
>({
  footers = [],
  headings = [],
  selectedOptions: injectedSelectedOptions = [],
  selectedValues,
  disabledValues,
  isAnyOption = false,
  isMultiselection = false,
  loading = false,
  options,
  onOptionSelectionChange = noop,
  onValueSelectionChange = noop,
  ...props
}: SelectRuleBlockProps<OptionType, SelectOptionType>) => {
  const selectedValuesArray = arrayify(selectedValues);
  const selectedValueOptions = findValuesInOptions(
    selectedValuesArray,
    options
  );

  // Merge the initial selected options from the values and the options injected
  const mergedSelectedOptions = arrayify(
    injectedSelectedOptions,
    selectedValueOptions
  );
  // Sometimes, the values and options will be of the same value - let's filter those out!
  const selectedOptionsMap = mergedSelectedOptions.reduce(
    (map, option) => ({ ...map, [String(option.value)]: option }),
    {} as Record<string, SelectOptionType>
  );
  const uniqueSelectedOptionsValues = Array.from(
    new Set(mergedSelectedOptions.map((option) => option.value))
  );
  const selectedOptions = uniqueSelectedOptionsValues.map(
    (value) => selectedOptionsMap[String(value)]
  );

  const handleOptionSelectionChange = (
    options: SelectOptionType | SelectOptionType[]
  ) => {
    const resolvedOptions = arrayify(options);

    onValueSelectionChange(resolvedOptions.map((option) => option.value));
    onOptionSelectionChange(resolvedOptions);
  };

  const getOptionLabel = (option: SelectOptionType) => {
    return option ? option.label : "";
  };
  const pillLabel = useMemo(() => {
    if (options.length === selectedOptions.length) {
      return anyOption.label;
    }

    // Default to the label of the first option
    if (selectedOptions.length === 0) {
      return "None";
    }

    // First label if only one option is selected
    const firstSelectedValue = selectedOptions[0];
    const firstSelectedLabel = getOptionLabel(firstSelectedValue);
    if (selectedOptions.length === 1) {
      return firstSelectedLabel;
    }

    // First and second label if two are selected
    const secondSelectedValue = selectedOptions[1];
    const secondSelectedLabel = getOptionLabel(secondSelectedValue);
    if (selectedOptions.length === 2) {
      return `${firstSelectedLabel} or ${secondSelectedLabel}`;
    }

    // Overflow when three or more are selected
    const overflowCount = selectedOptions.length - 2;
    return `${firstSelectedLabel}, ${secondSelectedLabel} or ${overflowCount} more`;
  }, [mergedSelectedOptions, selectedOptions]);

  const classNames = classnames("rule-block-select", props.className);

  return (
    <div {...props} className={classNames}>
      <MultiSelect<OptionType, SelectOptionType>
        optionsClassName="rule-block-select__input"
        selectedOptions={selectedOptions}
        isAnyOption={isAnyOption}
        isMultiselection={isMultiselection}
        loading={loading}
        options={options}
        disabledValues={disabledValues}
        onOptionSelectionChange={handleOptionSelectionChange}
      >
        <MultiSelect.Toggle>
          {({ setIsMenuVisible = noop }) => (
            <RulePill
              className="rule-block__pill rule-block-select__pill"
              isActive={false} // TODO
              value={pillLabel}
              onClick={() => setIsMenuVisible((visibility) => !visibility)}
            />
          )}
        </MultiSelect.Toggle>

        {headings.map((heading, index) => (
          <MultiSelect.Heading<OptionType, SelectOptionType> key={index}>
            {heading}
          </MultiSelect.Heading>
        ))}

        {footers.map((footer, index) => (
          <MultiSelect.Footer<OptionType, SelectOptionType> key={index}>
            {footer}
          </MultiSelect.Footer>
        ))}
      </MultiSelect>
    </div>
  );
};
