"use client"

import { cva } from "class-variance-authority"
import Downshift from "downshift"
import { type ChangeEvent, type FormEvent } from "react"
import { useFormContext, useWatch } from "react-hook-form"
import { useIntl } from "react-intl"

import { Button } from "@scandic-hotels/design-system/Button"
import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon"
import { Typography } from "@scandic-hotels/design-system/Typography"

import { type AutoCompleteLocation } from "@/server/routers/autocomplete/schema"

import SkeletonShimmer from "@/components/SkeletonShimmer"
import { useSearchHistory } from "@/hooks/useSearchHistory"

import { Input } from "../Input"
import SearchList from "./SearchList"

import styles from "./search.module.css"

interface SearchProps {
  autoFocus?: boolean
  alwaysShowResults?: boolean
  className?: string
  handlePressEnter: () => void
  inputName: string
  onSelect?: (selectedItem: AutoCompleteLocation) => void
  variant?: "rounded" | "default"
  withSearchButton?: boolean
  selectOnBlur?: boolean
  includeTypes: ("cities" | "hotels" | "countries")[]
}

export function Search({
  autoFocus,
  alwaysShowResults,
  handlePressEnter,
  inputName: SEARCH_TERM_NAME,
  onSelect,
  variant,
  withSearchButton = false,
  selectOnBlur = false,
  includeTypes,
}: SearchProps) {
  const { register, setValue, setFocus } = useFormContext()
  const intl = useIntl()
  const searchTerm = useWatch({ name: SEARCH_TERM_NAME }) as string
  const { searchHistory, insertSearchHistoryItem, clearHistory } =
    useSearchHistory()

  function handleOnChange(
    evt: FormEvent<HTMLInputElement> | ChangeEvent<HTMLInputElement>
  ) {
    const newValue = evt.currentTarget.value
    setValue(SEARCH_TERM_NAME, newValue)
  }

  function handleOnSelect(selectedItem: AutoCompleteLocation | null) {
    if (!selectedItem) {
      return
    }

    setValue("selectedSearch", selectedItem.name)
    setValue(SEARCH_TERM_NAME, selectedItem.name)
    insertSearchHistoryItem(selectedItem)

    switch (selectedItem.type) {
      case "cities":
        setValue("hotel", undefined)
        setValue("city", selectedItem.cityIdentifier)

        break
      case "hotels":
        setValue("hotel", +selectedItem.id)
        setValue("city", undefined)
        break
      default:
        console.error("Unhandled type:", selectedItem.type)
        break
    }

    onSelect?.(selectedItem)
  }

  function handleClearSearchHistory() {
    clearHistory()
  }

  const searchInputClassName = searchInputVariants({
    withSearchButton: withSearchButton,
  })
  const clearButtonClassName = clearButtonVariants({
    visible: !!searchTerm?.trim(),
  })

  return (
    <Downshift
      inputValue={searchTerm}
      itemToString={(value) => (value ? value.name : "")}
      onSelect={handleOnSelect}
      defaultHighlightedIndex={0}
      isOpen={alwaysShowResults}
    >
      {({
        getInputProps,
        getItemProps,
        getLabelProps,
        getMenuProps,
        getRootProps,
        highlightedIndex,
        isOpen,
        openMenu,
        selectHighlightedItem,
      }) => (
        <div className={searchContainerVariants({ variant })}>
          <div className={styles.inputContainer}>
            <label
              {...getLabelProps({ htmlFor: SEARCH_TERM_NAME })}
              className={labelVariants({
                color: withSearchButton && isOpen ? "default" : "red",
              })}
            >
              <Typography variant="Body/Supporting text (caption)/smBold">
                <span>
                  {intl.formatMessage({ defaultMessage: "Where to?" })}
                </span>
              </Typography>
              <div {...getRootProps({}, { suppressRefError: true })}>
                <div className={searchInputClassName}>
                  <Input
                    {...getInputProps({
                      id: SEARCH_TERM_NAME,
                      onFocus() {
                        openMenu()
                      },
                      placeholder: intl.formatMessage({
                        defaultMessage: "Hotels & Destinations",
                      }),
                      value: searchTerm,
                      ...register(SEARCH_TERM_NAME, {
                        onChange: handleOnChange,
                        onBlur: () => {
                          if (selectOnBlur) {
                            selectHighlightedItem()
                          }
                        },
                      }),
                      onKeyDown: (e) => {
                        if (e.key === "Enter" && !isOpen) {
                          handlePressEnter()
                        }
                      },
                      type: "search",
                    })}
                    autoFocus={autoFocus}
                  />
                </div>
              </div>
            </label>
            {withSearchButton && (
              <div className={styles.searchButtonContainer}>
                <Typography variant="Body/Supporting text (caption)/smBold">
                  <Button
                    variant="Text"
                    size="Small"
                    aria-label={intl.formatMessage({ defaultMessage: "Clear" })}
                    onPress={() => {
                      setValue(SEARCH_TERM_NAME, "")
                    }}
                    className={clearButtonClassName}
                  >
                    <MaterialIcon icon="close" />
                  </Button>
                </Typography>
                <Button
                  className={styles.searchButton}
                  variant="Primary"
                  size="Small"
                  type="submit"
                  onPress={() => {
                    if (!searchTerm) {
                      setFocus(SEARCH_TERM_NAME)
                      return
                    }

                    openMenu()
                    setTimeout(() => {
                      // This is a workaround to ensure that the menu is open before selecting the highlighted item
                      // Otherwise there is no highlighted item.
                      // Would need to keep track of the last highlighted item otherwise
                      selectHighlightedItem()
                    }, 0)
                  }}
                >
                  <Typography variant="Body/Supporting text (caption)/smBold">
                    <span>
                      <MaterialIcon icon="search" color="CurrentColor" />
                      {intl.formatMessage({ defaultMessage: "Search" })}
                    </span>
                  </Typography>
                </Button>
              </div>
            )}
          </div>
          <SearchList
            getItemProps={getItemProps}
            getMenuProps={getMenuProps}
            handleClearSearchHistory={handleClearSearchHistory}
            highlightedIndex={highlightedIndex}
            isOpen={isOpen}
            search={searchTerm}
            searchHistory={searchHistory}
            searchInputName={SEARCH_TERM_NAME}
            includeTypes={includeTypes}
          />
        </div>
      )}
    </Downshift>
  )
}

export function SearchSkeleton() {
  const intl = useIntl()
  return (
    <div className={styles.container}>
      <div className={`${styles.label} ${styles.red}`}>
        <Typography variant="Body/Supporting text (caption)/smBold">
          <span>{intl.formatMessage({ defaultMessage: "Where to?" })}</span>
        </Typography>
      </div>
      <div>
        <SkeletonShimmer width={"100%"} display="block" height="16px" />
      </div>
    </div>
  )
}

const searchContainerVariants = cva(styles.container, {
  variants: {
    variant: {
      default: "",
      rounded: styles.rounded,
    },
  },
  defaultVariants: {
    variant: "default",
  },
})

const searchInputVariants = cva(styles.searchInput, {
  variants: {
    withSearchButton: {
      true: styles.withSearchButton,
      false: "",
    },
  },
  defaultVariants: {
    withSearchButton: false,
  },
})

const clearButtonVariants = cva(styles.clearButton, {
  variants: {
    visible: {
      true: styles.clearButtonVisible,
      false: "",
    },
  },
})

const labelVariants = cva(styles.label, {
  variants: {
    color: {
      default: "",
      red: styles.red,
    },
  },
  defaultVariants: {
    color: "default",
  },
})
