import clsx from "clsx"
import Link from "next/link"
import { Fragment, useEffect, useMemo, useState } from "react"
import {
  Configure,
  ConfigureProps,
  Index,
  Snippet,
  useHits,
  useInstantSearch,
} from "react-instantsearch"
import SearchNoResult from "../NoResults"
import SearchHitGroupName from "./GroupName"
import getBaseUrl from "../../../utils/get-base-url"
import { useSearch } from "../../../providers/search"

type Hierarchy = "lvl0" | "lvl1" | "lvl2" | "lvl3" | "lvl4" | "lvl5"

export type HitType = {
  hierarchy: {
    lvl0: string | null
    lvl1: string | null
    lvl2: string | null
    lvl3: string | null
    lvl4: string | null
    lvl5: string | null
  }
  _tags: string[]
  url: string
  type?: "lvl1" | "lvl2" | "lvl3" | "lvl4" | "lvl5" | "content"
  content?: string
  __position: number
  __queryID?: string
  objectID: string
}

type GroupedHitType = {
  [k: string]: HitType[]
}

type SearchHitWrapperProps = {
  configureProps: ConfigureProps
}

type IndexResults = {
  [k: string]: boolean
}

const SearchHitsWrapper = ({ configureProps }: SearchHitWrapperProps) => {
  const { status } = useInstantSearch()
  const indices = useMemo(
    () => [
      process.env.NEXT_PUBLIC_API_ALGOLIA_INDEX_NAME || "temp",
      process.env.NEXT_PUBLIC_DOCS_ALGOLIA_INDEX_NAME || "temp",
    ],
    []
  )
  const [hasNoResults, setHashNoResults] = useState<IndexResults>({
    [indices[0]]: false,
    [indices[1]]: false,
  })
  const showNoResults = useMemo(() => {
    return Object.values(hasNoResults).every((value) => value === true)
  }, [hasNoResults])

  const setNoResults = (index: string, value: boolean) => {
    setHashNoResults((prev: IndexResults) => ({
      ...prev,
      [index]: value,
    }))
  }

  return (
    <div className="h-full overflow-auto">
      {status !== "loading" && showNoResults && <SearchNoResult />}
      {indices.map((indexName, index) => (
        <Index indexName={indexName} key={index}>
          <SearchHits indexName={indexName} setNoResults={setNoResults} />
          <Configure {...configureProps} />
        </Index>
      ))}
    </div>
  )
}

type SearchHitsProps = {
  indexName: string
  setNoResults: (index: string, value: boolean) => void
}

const SearchHits = ({ indexName, setNoResults }: SearchHitsProps) => {
  const { hits } = useHits<HitType>()
  const { status } = useInstantSearch()
  const { setIsOpen } = useSearch()

  // group by lvl0
  const grouped = useMemo(() => {
    const grouped: GroupedHitType = {}
    hits.forEach((hit) => {
      if (hit.hierarchy.lvl0) {
        if (!grouped[hit.hierarchy.lvl0]) {
          grouped[hit.hierarchy.lvl0] = []
        }
        grouped[hit.hierarchy.lvl0].push(hit)
      }
    })

    return grouped
  }, [hits])

  useEffect(() => {
    if (status !== "loading" && status !== "stalled") {
      setNoResults(indexName, hits.length === 0)
    }
  }, [hits, status])

  const getLastAvailableHeirarchy = (item: HitType) => {
    return (
      Object.keys(item.hierarchy)
        .reverse()
        .find((key) => item.hierarchy[key as Hierarchy] !== null) || ""
    )
  }

  const baseUrl = useMemo(() => getBaseUrl(), [])

  const checkIfInternal = (url: string): boolean => {
    const testRegex = new RegExp(`^${baseUrl}/api/(admin|store)`)
    return testRegex.test(url)
  }

  return (
    <div className="overflow-auto">
      {Object.keys(grouped).map((groupName, index) => (
        <Fragment key={index}>
          <SearchHitGroupName name={groupName} />
          {grouped[groupName].map((item, index) => (
            <div
              className={clsx(
                "gap-0.25 relative flex flex-1 flex-col p-0.5",
                "overflow-x-hidden text-ellipsis whitespace-nowrap break-words",
                "hover:bg-medusa-bg-base-hover dark:hover:bg-medusa-bg-base-hover-dark",
                "focus:bg-medusa-bg-base-hover dark:focus:bg-medusa-bg-base-hover-dark",
                "focus:outline-none"
              )}
              key={index}
              tabIndex={index}
              data-hit
              onClick={(e) => {
                const target = e.target as Element
                if (target.tagName.toLowerCase() === "div") {
                  target.querySelector("a")?.click()
                }
              }}
            >
              <span
                className={clsx(
                  "text-compact-small-plus text-medusa-fg-base dark:text-medusa-fg-base-dark",
                  "max-w-full"
                )}
              >
                <Snippet
                  attribute={[
                    "hierarchy",
                    item.type && item.type !== "content"
                      ? item.type
                      : item.hierarchy.lvl1
                      ? "lvl1"
                      : getLastAvailableHeirarchy(item),
                  ]}
                  hit={item}
                />
              </span>
              {item.type !== "lvl1" && (
                <span className="text-compact-small text-medusa-fg-subtle dark:text-medusa-fg-subtle-dark">
                  <Snippet
                    attribute={
                      item.content
                        ? "content"
                        : [
                            "hierarchy",
                            item.type || getLastAvailableHeirarchy(item),
                          ]
                    }
                    hit={item}
                  />
                </span>
              )}
              <Link
                href={item.url}
                className="absolute top-0 left-0 h-full w-full"
                target="_self"
                onClick={(e) => {
                  if (checkIfInternal(item.url)) {
                    e.preventDefault()
                    window.location.href = item.url
                    setIsOpen(false)
                  }
                }}
              />
            </div>
          ))}
        </Fragment>
      ))}
    </div>
  )
}

export default SearchHitsWrapper
