'use client'

import {
  Button,
  ComboBox,
  Input,
  Key,
  ListBox,
  Popover,
  Label,
  ComboBoxStateContext,
} from 'react-aria-components'
import {
  PropsWithChildren,
  RefObject,
  useContext,
  useEffect,
  useRef,
  useState,
} from 'react'

import { MaterialIcon } from '../Icons/MaterialIcon'
import { Typography } from '../Typography'
import { SelectItem } from './SelectItem'

import type { SelectFilterProps } from './types'

import styles from './select.module.css'

/**
 * ComboBoxInner
 * Used to simulate a press on down arrow key to highlight the first item.
 * We do this so that a user can type some characters and then select the first
 * option directly with tab. Might require revisit later when assistive
 * technologies test it out as it might be a bit verbose for the announcements
 * because it will most likely announce the "first item" for each typed
 * character from the user.
 */
function ComboBoxInner({
  inputRef,
  children,
}: PropsWithChildren<{
  inputRef: RefObject<HTMLInputElement>
}>) {
  // Get the state for the ComboBox from RAC
  const comboBoxState = useContext(ComboBoxStateContext)

  const { isFocused, selectedKey, inputValue } = comboBoxState ?? {}

  // Act after render
  useEffect(() => {
    let timeout: Timer

    // We only want to act on focused field which has rerendered due to
    // changes to its input value. Selecting a value will also rerender, but
    // that we want to ignore as that is not a change to its input value.
    if (isFocused && selectedKey !== inputValue) {
      // The simlulated event has to originate from the input field.
      if (inputRef.current) {
        // Simulate a press on down arrow key.
        const e = new KeyboardEvent('keydown', {
          key: 'ArrowDown',
          bubbles: true /* important so RAC can act on it */,
        })

        // Dispatch after everything has rendered completely
        timeout = setTimeout(() => {
          inputRef.current?.dispatchEvent(e)
        }, 0)
      }
    }

    // Clean up
    return () => {
      if (timeout) {
        clearTimeout(timeout)
      }
    }
  }, [inputRef, inputValue, isFocused, selectedKey])

  return children
}

export function SelectFilter({
  name,
  label,
  items,
  isDisabled,
  icon,
  itemIcon,
  defaultSelectedKey,
  onSelectionChange = () => undefined,
  onFocus = () => undefined,
  onBlur = () => undefined,
  autoComplete,
  ...props
}: SelectFilterProps) {
  const [focus, setFocus] = useState(false)
  const [value, setValue] = useState<Key | null>(defaultSelectedKey ?? null)
  const iconColor = isDisabled ? 'Icon/Interactive/Disabled' : 'Icon/Default'

  const inputRef = useRef<HTMLInputElement>(null!)

  return (
    <ComboBox
      className={styles.select}
      name={name}
      aria-label={label}
      isDisabled={isDisabled}
      onSelectionChange={(val) => {
        setValue(val)
        onSelectionChange(val)
      }}
      onFocus={(e) => {
        setFocus(true)
        onFocus(e)
      }}
      onBlur={(e) => {
        setFocus(false)
        onBlur(e)
      }}
      defaultSelectedKey={defaultSelectedKey}
      {...props}
    >
      <ComboBoxInner inputRef={inputRef}>
        <Label className={styles.inner}>
          {icon ? (
            <MaterialIcon
              icon={icon}
              size={24}
              color={iconColor}
              aria-hidden="true"
            />
          ) : null}

          <span className={styles.displayText}>
            <Typography
              variant={
                focus || value ? 'Label/xsRegular' : 'Body/Paragraph/mdRegular'
              }
            >
              <span className={styles.label}>{label}</span>
            </Typography>
            <Typography variant="Body/Paragraph/mdRegular">
              <Input
                ref={inputRef}
                className={styles.input}
                autoComplete={autoComplete}
              />
            </Typography>
          </span>
          <Button className={styles.button}>
            <MaterialIcon
              icon="chevron_right"
              size={24}
              color={iconColor}
              aria-hidden="true"
              className={styles.chevron}
            />
          </Button>
        </Label>

        <Popover
          className={styles.popover}
          shouldFlip={false}
          crossOffset={icon ? -40 : -8}
          offset={22}
        >
          <ListBox className={styles.listBox}>
            {items.map((item) => (
              <SelectItem
                key={item.value}
                id={item.value}
                icon={item.icon || itemIcon}
                isDisabled={item.isDisabled}
              >
                {item.label}
              </SelectItem>
            ))}
          </ListBox>
        </Popover>
      </ComboBoxInner>
    </ComboBox>
  )
}
