import {
  ChangeEventHandler,
  FocusEventHandler,
  FormEventHandler,
  KeyboardEventHandler,
  useCallback,
  useEffect,
  useRef,
  useState,
} from 'react'
import { Disclosure, DisclosurePanel } from '@reach/disclosure'
import { useCloseOnBlur } from '@/Hooks/useCloseOnBlur'
import { observer } from 'mobx-react-lite'
import { classNames } from '@standardnotes/utils'
import { FOCUSABLE_BUT_NOT_TABBABLE } from '@/Constants/Constants'
import LinkedItemSearchResults from './LinkedItemSearchResults'
import { LinkingController } from '@/Controllers/LinkingController'
import { KeyboardKey } from '@standardnotes/ui-services'
import { ElementIds } from '@/Constants/ElementIDs'
import Menu from '../Menu/Menu'
import { getLinkingSearchResults } from '@/Utils/Items/Search/getSearchResults'
import { useApplication } from '../ApplicationProvider'

type Props = {
  linkingController: LinkingController
  focusPreviousItem: () => void
  focusedId: string | undefined
  setFocusedId: (id: string) => void
  hoverLabel?: string
}

const ItemLinkAutocompleteInput = ({
  linkingController,
  focusPreviousItem,
  focusedId,
  setFocusedId,
  hoverLabel,
}: Props) => {
  const application = useApplication()
  const { tags, linkItemToSelectedItem, createAndAddNewTag, isEntitledToNoteLinking, activeItem } = linkingController

  const [searchQuery, setSearchQuery] = useState('')
  const { unlinkedItems, shouldShowCreateTag } = getLinkingSearchResults(searchQuery, application, activeItem)

  const [dropdownVisible, setDropdownVisible] = useState(false)
  const [dropdownMaxHeight, setDropdownMaxHeight] = useState<number | 'auto'>('auto')

  const containerRef = useRef<HTMLDivElement>(null)
  const inputRef = useRef<HTMLInputElement>(null)
  const searchResultsMenuRef = useRef<HTMLMenuElement>(null)

  const [closeOnBlur] = useCloseOnBlur(containerRef, (visible: boolean) => {
    setDropdownVisible(visible)
    setSearchQuery('')
  })

  const showDropdown = () => {
    const { clientHeight } = document.documentElement
    const inputRect = inputRef.current?.getBoundingClientRect()
    if (inputRect) {
      setDropdownMaxHeight(clientHeight - inputRect.bottom - 32 * 2)
      setDropdownVisible(true)
    }
  }

  const onSearchQueryChange: ChangeEventHandler<HTMLInputElement> = (event) => {
    setSearchQuery(event.currentTarget.value)
  }

  const onFormSubmit: FormEventHandler = async (event) => {
    event.preventDefault()
    if (searchQuery !== '') {
      await createAndAddNewTag(searchQuery)
    }
  }

  const handleFocus = () => {
    if (focusedId !== ElementIds.ItemLinkAutocompleteInput) {
      setFocusedId(ElementIds.ItemLinkAutocompleteInput)
    }
    showDropdown()
  }

  const onBlur: FocusEventHandler = (event) => {
    closeOnBlur(event)
  }

  const onKeyDown: KeyboardEventHandler = (event) => {
    switch (event.key) {
      case KeyboardKey.Left:
        if (searchQuery.length === 0) {
          focusPreviousItem()
        }
        break
      case KeyboardKey.Down:
        if (searchQuery.length > 0) {
          event.preventDefault()
          searchResultsMenuRef.current?.focus()
        }
        break
    }
  }

  useEffect(() => {
    if (focusedId === ElementIds.ItemLinkAutocompleteInput) {
      inputRef.current?.focus()
    }
  }, [focusedId])

  const areSearchResultsVisible = dropdownVisible && (unlinkedItems.length > 0 || shouldShowCreateTag)

  const handleMenuKeyDown: KeyboardEventHandler<HTMLMenuElement> = useCallback((event) => {
    if (event.key === KeyboardKey.Escape) {
      inputRef.current?.focus()
    }
  }, [])

  return (
    <div ref={containerRef}>
      <form onSubmit={onFormSubmit}>
        <Disclosure open={dropdownVisible} onChange={showDropdown}>
          <input
            ref={inputRef}
            className={classNames(
              `${tags.length > 0 ? 'w-80' : 'mr-10 w-70'}`,
              'bg-transparent text-sm text-text focus:border-b-2 focus:border-solid focus:border-info lg:text-xs',
              'no-border h-7 focus:shadow-none focus:outline-none',
            )}
            value={searchQuery}
            onChange={onSearchQueryChange}
            type="text"
            placeholder="Link tags, notes, files..."
            onBlur={onBlur}
            onFocus={handleFocus}
            onKeyDown={onKeyDown}
            id={ElementIds.ItemLinkAutocompleteInput}
            autoComplete="off"
            title={hoverLabel}
            aria-label={hoverLabel}
          />
          {areSearchResultsVisible && (
            <DisclosurePanel
              className={classNames(
                tags.length > 0 ? 'w-80' : 'mr-10 w-70',
                'absolute z-dropdown-menu flex flex-col overflow-y-auto rounded bg-default py-2 shadow-main',
              )}
              style={{
                maxHeight: dropdownMaxHeight,
              }}
              onBlur={closeOnBlur}
              tabIndex={FOCUSABLE_BUT_NOT_TABBABLE}
            >
              <Menu
                isOpen={areSearchResultsVisible}
                a11yLabel="Unlinked items search results"
                onKeyDown={handleMenuKeyDown}
                ref={searchResultsMenuRef}
                shouldAutoFocus={false}
              >
                <LinkedItemSearchResults
                  createAndAddNewTag={createAndAddNewTag}
                  linkItemToSelectedItem={linkItemToSelectedItem}
                  results={unlinkedItems}
                  searchQuery={searchQuery}
                  shouldShowCreateTag={shouldShowCreateTag}
                  onClickCallback={() => setSearchQuery('')}
                  isEntitledToNoteLinking={isEntitledToNoteLinking}
                />
              </Menu>
            </DisclosurePanel>
          )}
        </Disclosure>
      </form>
    </div>
  )
}

export default observer(ItemLinkAutocompleteInput)
