"use client"
import Downshift from "downshift"
import {
  ChangeEvent,
  FocusEvent,
  FormEvent,
  useCallback,
  useEffect,
  useReducer,
} from "react"
import { useFormContext, useWatch } from "react-hook-form"
import { useIntl } from "react-intl"

import SkeletonShimmer from "@/components/SkeletonShimmer"
import Caption from "@/components/TempDesignSystem/Text/Caption"

import Input from "../Input"
import { init, localStorageKey, reducer, sessionStorageKey } from "./reducer"
import SearchList from "./SearchList"

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

import type { BookingWidgetSchema } from "@/types/components/bookingWidget"
import { ActionType } from "@/types/components/form/bookingwidget"
import type { SearchProps } from "@/types/components/search"
import type { Location } from "@/types/trpc/routers/hotel/locations"

const name = "search"
export default function Search({ locations }: SearchProps) {
  const { register, setValue, trigger } = useFormContext<BookingWidgetSchema>()
  const intl = useIntl()
  const value = useWatch({ name })
  const [state, dispatch] = useReducer(
    reducer,
    { defaultLocations: locations },
    init
  )

  const handleMatchLocations = useCallback(
    function (searchValue: string) {
      return locations.filter((location) => {
        return location.name.toLowerCase().includes(searchValue.toLowerCase())
      })
    },
    [locations]
  )

  function handleClearSearchHistory() {
    localStorage.removeItem(localStorageKey)
    dispatch({ type: ActionType.CLEAR_HISTORY_LOCATIONS })
  }

  function dispatchInputValue(inputValue: string) {
    if (inputValue) {
      dispatch({
        payload: { search: inputValue },
        type: ActionType.SEARCH_LOCATIONS,
      })
    } else {
      dispatch({ type: ActionType.CLEAR_SEARCH_LOCATIONS })
    }
  }

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

  function handleOnFocus(evt: FocusEvent<HTMLInputElement>) {
    const searchValue = evt.currentTarget.value
    if (searchValue) {
      const matchingLocations = handleMatchLocations(searchValue)
      if (matchingLocations.length) {
        dispatch({
          payload: { search: searchValue },
          type: ActionType.SEARCH_LOCATIONS,
        })
      }
    }
  }

  function handleOnSelect(selectedItem: Location | null) {
    if (selectedItem) {
      const stringified = JSON.stringify(selectedItem)
      setValue("location", encodeURIComponent(stringified))
      sessionStorage.setItem(sessionStorageKey, stringified)
      setValue(name, selectedItem.name)
      trigger()

      const searchHistoryMap = new Map()
      searchHistoryMap.set(selectedItem.name, selectedItem)
      if (state.searchHistory) {
        state.searchHistory.forEach((location) => {
          searchHistoryMap.set(location.name, location)
        })
      }

      const searchHistory: Location[] = []
      searchHistoryMap.forEach((location) => {
        searchHistory.push(location)
      })
      localStorage.setItem(localStorageKey, JSON.stringify(searchHistory))
      dispatch({
        payload: {
          location: selectedItem,
          searchHistory,
        },
        type: ActionType.SELECT_ITEM,
      })
    } else {
      sessionStorage.removeItem(sessionStorageKey)
    }
  }

  useEffect(() => {
    const searchData =
      typeof window !== "undefined"
        ? sessionStorage.getItem(sessionStorageKey)
        : undefined
    const searchHistory =
      typeof window !== "undefined"
        ? localStorage.getItem(localStorageKey)
        : null
    if (searchData || searchHistory) {
      dispatch({
        payload: {
          searchData: searchData ? JSON.parse(searchData) : undefined,
          searchHistory: searchHistory ? JSON.parse(searchHistory) : null,
        },
        type: ActionType.SET_STORAGE_DATA,
      })
    }
  }, [dispatch])

  return (
    <Downshift
      initialSelectedItem={state.searchData}
      inputValue={value}
      itemToString={(value) => (value ? value.name : "")}
      onSelect={handleOnSelect}
      onInputValueChange={(inputValue) => dispatchInputValue(inputValue)}
    >
      {({
        closeMenu,
        getInputProps,
        getItemProps,
        getLabelProps,
        getMenuProps,
        getRootProps,
        highlightedIndex,
        isOpen,
        openMenu,
      }) => (
        <div className={styles.container}>
          <label {...getLabelProps({ htmlFor: name })} className={styles.label}>
            <Caption
              type="bold"
              color={isOpen ? "uiTextActive" : "red"}
              asChild
            >
              <span>
                {state.searchData?.type === "hotels"
                  ? state.searchData?.relationships?.city?.name
                  : intl.formatMessage({ id: "Where to" })}
              </span>
            </Caption>
          </label>
          <div {...getRootProps({}, { suppressRefError: true })}>
            <Input
              {...getInputProps({
                id: name,
                onFocus(evt) {
                  handleOnFocus(evt)
                  openMenu()
                },
                placeholder: intl.formatMessage({
                  id: "Destinations & hotels",
                }),
                ...register(name, {
                  onBlur: function () {
                    closeMenu()
                  },
                  onChange: handleOnChange,
                }),
                type: "search",
              })}
            />
          </div>
          <SearchList
            getItemProps={getItemProps}
            getMenuProps={getMenuProps}
            handleClearSearchHistory={handleClearSearchHistory}
            highlightedIndex={highlightedIndex}
            isOpen={isOpen}
            locations={state.locations}
            search={state.search}
            searchHistory={state.searchHistory}
          />
        </div>
      )}
    </Downshift>
  )
}

export function SearchSkeleton() {
  return (
    <div className={styles.container}>
      <div className={styles.label}>
        <Caption type="bold" color="red" asChild>
          <span>Where to</span>
        </Caption>
      </div>
      <div className={styles.input}>
        <SkeletonShimmer />
      </div>
    </div>
  )
}
