import React, { forwardRef, useCallback, useEffect, useRef, useState } from "react"
import {
  autoUpdate,
  size,
  flip,
  offset,
  useClick,
  useDismiss,
  useFloating,
  useInteractions,
  useListNavigation,
  useTypeahead,
  useMergeRefs,
  useRole,
  FloatingFocusManager,
  FloatingList,
} from "@floating-ui/react"
import cx from "classnames"
import { omit } from "lodash"
import CKIcon from "../ck-icon"
import Portal from "../portal"
import { Option } from "./input"
import { SelectContext, MENU_CLASSES, MAX_MENU_HEIGHT, MIN_MENU_WIDTH, MENU_OFFSET } from "../select-menu/select-menu"

const DEFAULT_BTN_CLASSES = "inline-flex items-center border transition-colors delay-75 text-sm gap-2 justify-between rounded select-none pl-3 pr-2 outline-none hover:bg-gray-50"

const filterOption = (options, value) => {
  return options.filter(option => value?.includes(option.value))
}

const MultiSelectFilter = forwardRef(({
  className,
  label = "Select",
  options,
  value,
  ...props
}, forwardedRef) => {
  const [isOpen, setIsOpen] = useState(false)
  const [selected, setSelected] = useState(filterOption(options, value) || [])
  const [activeIndex, setActiveIndex] = useState(null)

  const elementsRef = useRef([])
  const labelsRef = useRef([])

  // Reload when value prop changes
  useEffect(() => {
    setSelected(filterOption(options, value) || [])
  }, [options, value])

  const { refs, floatingStyles, context } = useFloating({
    placement: "bottom-start",
    open: isOpen,
    onOpenChange: props.disabled ? () => null : setIsOpen,
    whileElementsMounted: autoUpdate,
    middleware: [
      flip(),
      size(),
      offset(MENU_OFFSET)
    ]
  })

  const handleTypeaheadMatch = index => {
    if (isOpen) {
      setActiveIndex(index)
    }
  }

  const typeahead = useTypeahead(context, {
    listRef: labelsRef,
    activeIndex,
    onMatch: handleTypeaheadMatch
  })
  const click = useClick(context)
  const role = useRole(context, { role: "listbox" })
  const dismiss = useDismiss(context)
  const listNav = useListNavigation(context, {
    listRef: elementsRef,
    activeIndex,
    onNavigate: setActiveIndex,
    virtual: true,
    loop: true
  })

  const {
    getReferenceProps,
    getFloatingProps,
    getItemProps
  } = useInteractions([role, dismiss, click, typeahead, listNav])

  const handleSelect = useCallback(index => {
    let selections
    if(selected.find(option => option.value === options[index].value)) {
      selections = selected.filter(option => option.value !== options[index].value)
    } else {
      selections = [...selected, options[index]]
    }
    setSelected(selections)
    if(props.onChange) {
      props.onChange({ target: { name: props.name, value: selections } })
    }
    document.body.focus()
  }, [options, selected, props])

  const selectContext = React.useMemo(
    () => ({
      activeIndex,
      getItemProps,
      handleSelect,
      selected
    }),
    [activeIndex, getItemProps, handleSelect, selected]
  )
  const btnProps = omit(props, ["aria-label", "aria-labelledby", "error", "value"])
  const btnClassName = cx(
    DEFAULT_BTN_CLASSES,
    className,
    {
      "bg-gray-50": isOpen,
      "border-transparent": !props.error,
      "border-red-500": props.error,
    }
  )

  return (
    <>
      <div
        ref={useMergeRefs([refs.setReference, forwardedRef])}
        tabIndex={0}
        {...getReferenceProps()}
        {...btnProps}
        className={btnClassName}
        aria-disabled={props.disabled || false}
        aria-labelledby={props["aria-labelledby"]}
        aria-label={props["aria-label"] || "select multiple options"}
        style={{ paddingBlock: "7px" }}
      >
        <Indicator count={selected.length} />
        <span className="font-semibold">{label}</span>
        <CKIcon name="down" className={cx("duration-75 transition-transform transform text-gray-800", { "rotate-180" : isOpen })} aria-hidden="true" />
      </div>
      <SelectContext.Provider value={selectContext}>
        {isOpen && (
          <FloatingFocusManager
            context={context}
            initialFocus={-1}
            modal={false}
          >
            <Portal>
              <div
                ref={refs.setFloating}
                style={{ ...floatingStyles, maxHeight: MAX_MENU_HEIGHT, minWidth: MIN_MENU_WIDTH }}
                className={MENU_CLASSES}
                aria-label="Options"
                {...getFloatingProps()}
              >
                <FloatingList elementsRef={elementsRef} labelsRef={labelsRef}>
                  {options.length === 0 && (
                    <Option disabled className="text-center text-gray-600">
                      No result found
                    </Option>
                  )}
                  {options.map((option) => (
                    <Option
                      key={`${option.value}-${option.label}`}
                      value={option.value}
                    >
                      {option.label}
                    </Option>
                  ))}
                </FloatingList>
              </div>
            </Portal>
          </FloatingFocusManager>
        )}
      </SelectContext.Provider>
    </>
  )
})

export default MultiSelectFilter

function Indicator({ count }) {
  return (
    <div
      className={cx("text-blue-700", {"hidden": count === 0 })}
      style={{ fontSize: "28px", lineHeight: "22px", fontFamily: "sans-serif" }}
      role="status"
      aria-label={`${count} filter${count !== 1 ? "s" : ""} active`}
    >
      •
    </div>
  )
}
