"use client"

import React, { useEffect, useMemo, useRef, useState } from "react"
import algoliasearch, { SearchClient } from "algoliasearch/lite"
import { InstantSearch, SearchBox } from "react-instantsearch"
import Modal from "../../Modal"
import clsx from "clsx"
import IconMagnifyingGlass from "../../Icons/MagnifyingGlass"
import IconXMark from "../../Icons/XMark"
import SearchEmptyQueryBoundary from "../EmptyQueryBoundary"
import SearchSuggestions from "../Suggestions"
import { useSearch } from "../../../providers/search"
import checkArraySameElms from "../../../utils/array-same-elms"
import SearchHitsWrapper from "../Hits"
import Button from "../../Button"
import Kbd from "../../MDXComponents/Kbd"
import { OptionType } from "../../../hooks/use-select"
import SelectBadge from "../../Select/Badge"
import useKeyboardShortcut from "../../../hooks/use-keyboard-shortcut"
import { findNextSibling, findPrevSibling } from "../../../utils/dom-utils"

const algoliaClient = algoliasearch(
  process.env.NEXT_PUBLIC_ALGOLIA_APP_ID || "temp",
  process.env.NEXT_PUBLIC_ALGOLIA_API_KEY || "temp"
)

const searchClient: SearchClient = {
  ...algoliaClient,
  async search(requests) {
    if (requests.every(({ params }) => !params?.query)) {
      return Promise.resolve({
        results: requests.map(() => ({
          hits: [],
          nbHits: 0,
          nbPages: 0,
          page: 0,
          processingTimeMS: 0,
          hitsPerPage: 0,
          exhaustiveNbHits: false,
          query: "",
          params: "",
        })),
      })
    }

    return algoliaClient.search(requests)
  },
}

const SearchModal = () => {
  const modalRef = useRef<HTMLDialogElement | null>(null)
  const options: OptionType[] = useMemo(() => {
    return [
      {
        value: "admin",
        label: "Admin API",
      },
      {
        value: "store",
        label: "Store API",
      },
      {
        value: "docs",
        label: "Docs",
      },
      {
        value: "user-guide",
        label: "User Guide",
      },
      {
        value: "plugins",
        label: "Plugins",
      },
      {
        value: "reference",
        label: "References",
      },
      {
        value: "ui",
        label: "UI",
      },
    ]
  }, [])
  const { isOpen, setIsOpen, defaultFilters } = useSearch()
  const [filters, setFilters] = useState<string[]>(defaultFilters)
  const formattedFilters: string = useMemo(() => {
    let formatted = ""
    filters.forEach((filter) => {
      const split = filter.split("_")
      split.forEach((f) => {
        if (formatted.length) {
          formatted += " OR "
        }
        formatted += `_tags:${f}`
      })
    })
    return formatted
  }, [filters])
  const searchBoxRef = useRef<HTMLFormElement>(null)

  const focusSearchInput = () =>
    searchBoxRef.current?.querySelector("input")?.focus()

  useEffect(() => {
    if (!checkArraySameElms(defaultFilters, filters)) {
      setFilters(defaultFilters)
    }
  }, [defaultFilters])

  useEffect(() => {
    if (isOpen && searchBoxRef.current) {
      focusSearchInput()
    }
  }, [isOpen])

  const handleKeyAction = (e: KeyboardEvent) => {
    if (!isOpen) {
      return
    }
    e.preventDefault()
    const focusedItem = modalRef.current?.querySelector(":focus") as HTMLElement
    if (!focusedItem) {
      // focus the first data-hit
      const nextItem = modalRef.current?.querySelector(
        "[data-hit]"
      ) as HTMLElement
      nextItem?.focus()
      return
    }

    const isHit = focusedItem.hasAttribute("data-hit")
    const isInput = focusedItem.tagName.toLowerCase() === "input"

    if (!isHit && !isInput) {
      // ignore if focused items aren't input/data-hit
      return
    }

    const lowerPressedKey = e.key.toLowerCase()

    if (lowerPressedKey === "enter") {
      if (isHit) {
        // trigger click event of the focused element
        focusedItem.click()
      }
      return
    }

    if (lowerPressedKey === "arrowup") {
      // only hit items has action on arrow up
      if (isHit) {
        // find if there's a data-hit item before this one
        const beforeItem = findPrevSibling(focusedItem, "[data-hit]")
        if (!beforeItem) {
          // focus the input
          focusSearchInput()
        } else {
          // focus the previous item
          beforeItem.focus()
        }
      }
    } else if (lowerPressedKey === "arrowdown") {
      // check if item is input or hit
      if (isInput) {
        // go to the first data-hit item
        const nextItem = modalRef.current?.querySelector(
          "[data-hit]"
        ) as HTMLElement
        nextItem?.focus()
      } else {
        // handle go down for hit items
        // find if there's a data-hit item after this one
        const afterItem = findNextSibling(focusedItem, "[data-hit]")
        if (afterItem) {
          // focus the next item
          afterItem.focus()
        }
      }
    }
  }

  useKeyboardShortcut({
    metakey: false,
    shortcutKeys: ["ArrowUp", "ArrowDown", "Enter"],
    action: handleKeyAction,
    checkEditing: false,
    preventDefault: false,
  })

  return (
    <Modal
      contentClassName={clsx(
        "!p-0 overflow-hidden relative h-full",
        "rounded-none md:rounded-lg flex flex-col justify-between"
      )}
      modalContainerClassName="w-screen h-screen !rounded-none md:!rounded-lg"
      open={isOpen}
      onClose={() => setIsOpen(false)}
      ref={modalRef}
    >
      <InstantSearch
        indexName={process.env.NEXT_PUBLIC_API_ALGOLIA_INDEX_NAME}
        searchClient={searchClient}
      >
        <div
          className={clsx("bg-medusa-bg-base dark:bg-medusa-bg-base-dark flex")}
        >
          <SearchBox
            classNames={{
              root: clsx(
                "h-[56px] w-full md:rounded-t-xl relative border-b border-medusa-border-base dark:border-medusa-border-base-dark",
                "bg-transparent"
              ),
              form: clsx("h-full md:rounded-t-xl bg-transparent"),
              input: clsx(
                "w-full h-full pl-3 text-medusa-fg-base dark:text-medusa-fg-base-dark",
                "placeholder:text-medusa-fg-muted dark:placeholder:text-medusa-fg-muted-dark",
                "md:rounded-t-xl text-compact-medium bg-transparent",
                "appearance-none search-cancel:hidden active:outline-none focus:outline-none"
              ),
              submit: clsx("absolute top-[18px] left-1"),
              reset: clsx(
                "absolute top-0.75 right-1 hover:bg-medusa-bg-base-hover dark:hover:bg-medusa-bg-base-hover-dark",
                "p-[5px] md:rounded"
              ),
              loadingIndicator: clsx("absolute top-[18px] right-1"),
            }}
            submitIconComponent={() => (
              <IconMagnifyingGlass iconColorClassName="stroke-medusa-fg-muted dark:stroke-medusa-fg-muted-dark" />
            )}
            resetIconComponent={() => (
              <IconXMark
                iconColorClassName="stroke-medusa-fg-subtle dark:stroke-medusa-fg-subtle-dark"
                className="hidden md:block"
              />
            )}
            placeholder="Find something..."
            autoFocus
            formRef={searchBoxRef}
          />
          <Button
            variant="clear"
            className={clsx(
              "bg-medusa-bg-base dark:bg-medusa-bg-base-dark block md:hidden",
              "border-medusa-border-base dark:border-medusa-border-base-dark border-b",
              "pr-1"
            )}
            onClick={() => setIsOpen(false)}
          >
            <IconXMark iconColorClassName="stroke-medusa-fg-muted dark:stroke-medusa-fg-muted-dark" />
          </Button>
        </div>
        <div className="mx-0.5 h-[calc(100%-120px)] md:h-[332px] md:flex-initial lg:max-h-[332px] lg:min-h-[332px]">
          <SearchEmptyQueryBoundary fallback={<SearchSuggestions />}>
            <SearchHitsWrapper
              configureProps={{
                filters: formattedFilters,
                attributesToSnippet: [
                  "content",
                  "hierarchy.lvl1",
                  "hierarchy.lvl2",
                ],
                attributesToHighlight: [
                  "content",
                  "hierarchy.lvl1",
                  "hierarchy.lvl2",
                ],
              }}
            />
          </SearchEmptyQueryBoundary>
        </div>
      </InstantSearch>
      <div
        className={clsx(
          "py-0.75 flex items-center justify-between px-1",
          "border-medusa-border-base dark:border-medusa-border-base-dark border-t",
          "bg-medusa-bg-base dark:bg-medusa-bg-base-dark"
        )}
      >
        <SelectBadge
          multiple
          options={options}
          value={filters}
          setSelected={(value) =>
            setFilters(Array.isArray(value) ? [...value] : [value])
          }
          addSelected={(value) => setFilters((prev) => [...prev, value])}
          removeSelected={(value) =>
            setFilters((prev) => prev.filter((v) => v !== value))
          }
          showClearButton={false}
          placeholder="Filters"
          handleAddAll={(isAllSelected: boolean) => {
            if (isAllSelected) {
              setFilters(defaultFilters)
            } else {
              setFilters(options.map((option) => option.value))
            }
          }}
        />
        <div className="hidden items-center gap-1 md:flex">
          <div className="flex items-center gap-0.5">
            <span
              className={clsx(
                "text-medusa-fg-subtle dark:text-medusa-fg-subtle-dark",
                "text-compact-x-small"
              )}
            >
              Navigation
            </span>
            <span className="gap-0.25 flex">
              <Kbd>↑</Kbd>
              <Kbd>↓</Kbd>
            </span>
          </div>
          <div className="flex items-center gap-0.5">
            <span
              className={clsx(
                "text-medusa-fg-subtle dark:text-medusa-fg-subtle-dark",
                "text-compact-x-small"
              )}
            >
              Open Result
            </span>
            <Kbd>↵</Kbd>
          </div>
        </div>
      </div>
    </Modal>
  )
}

export default SearchModal
