/* eslint-disable @typescript-eslint/no-explicit-any */
/* eslint-disable @typescript-eslint/no-non-null-assertion */
import React, { useState, useRef, useEffect, useCallback, useMemo } from "react"
import queryString from "querystring"
import cls from "classnames"

import { Input } from "@/components/header/header-search/input"

import { Container } from "@/components"
import { client, clientBlog } from "@/utils/search"
import t from "@/constants/wording"
import SearchTabs from "./search-tabs"
import Pagination from "../practice/pagination"
import AlgoliaCst from "@/constants/search"
import SearchResults from "./search-results"
import { SearchClient } from "algoliasearch"
import { SearchResponse } from "@algolia/client-search"

import * as styles from "./index.module.scss"
import Loading from "../loading"

const documentIndexName = process.env.GATSBY_ALGOLIA_DOCUMENT_INDEX_NAME

type recordList = { [k: string]: IHit[] }[]
const pageNum = 10

const { AlgoliaIndexMap } = AlgoliaCst as AlgoliaIndexMapType
// the tab name must be displayed in order
const defaultTagsAndCount = Object.entries(AlgoliaIndexMap).map(item => {
  const [, { title }] = item
  return { [title]: { total: 0, curPage: 0 } }
})
// remove all tabPage
// defaultTagsAndCount.unshift({ [t("all")]: { total: 0, curPage: 0 } })

const wrapDocuments = (documentResult: SearchResponse<IHit>) => {
  const newDocumentResult = documentResult || { nbHits: 0, page: 0 }
  const { hits: documentHits = [] } = newDocumentResult
  const documentList = documentHits.map(item => {
    const {
      _highlightResult: {
        content,
        hierarchy: { lvl0 = { matchLevel: "none", value: "" } },
      },
    } = item
    const title = item?.hierarchy?.lvl0 ?? item.content
    return {
      title,
      description: "",
      body: item.content,
      path: item.url,
      indiceTag: "documents",
      _highlightResult: {
        title: { ...lvl0 },
        description: { matchLevel: "none", value: "" },
        body: { ...content },
      },
    }
  })
  return {
    ...newDocumentResult,
    index: "documents_",
    hits: documentList,
  }
}

const SearchPage = ({ indices }: searchProps) => {
  const inputContainer = useRef<HTMLDivElement>(null)
  const [query, setQuery] = useState("")
  const [isMounted, setFirstMount] = useState(false)
  const [isLoading, setLoading] = useState(false)
  const [activeTabName, setTab] = useState(t("documents"))
  const [eachPageDataList, setPageDataList] = useState<recordList>([])
  const [tagsAndCount, setTagsAndCount] = useState<Record<string, IPage>[]>(
    defaultTagsAndCount
  )
  const [resultList, setResultList] = useState<Array<IHit>>([])
  useEffect(async () => {
    if (inputContainer.current) {
      const { keywords } = queryString.parse(
        typeof window !== "undefined"
          ? window.location.search.replace("?", "")
          : ""
      ) as { [key: string]: string }
      const input = inputContainer.current.querySelector("input")
      if (input) {
        input.focus()
        keywords && setQuery(keywords)
        keywords && (await searchWord(keywords))
        setFirstMount(true)
      }
    }
  }, [])

  const searchUtil = async (queries: queryType[], client: SearchClient) => {
    let results: SearchResponse<IHit>[]
    setLoading(true)
    try {
      const res = await client.multipleQueries(queries)
      results = res.results
    } catch (error) {
      console.error(error)
      results = []
    }
    setLoading(false)
    return results
  }
  /**
   * search all indices with query and reset pageContent
   * */
  const searchWord = async (q: string, activeTabName = t("documents")) => {
    const template = {
      indexName: "",
      query: q,
      params: {
        hitsPerPage: pageNum,
      },
    }
    const queries = indices
      .map(indice => {
        return {
          ...template,
          indexName: indice.name,
        }
      })
      .filter(i => !i.indexName.includes("documents_"))
    const documentsQuery = [
      {
        ...template,
        indexName: documentIndexName,
        params: {
          hitsPerPage: pageNum,
        },
      },
    ]
    const findings = await searchUtil(queries, client)
    const [documentResult] = await searchUtil(documentsQuery, clientBlog)
    const newDocumentResult = wrapDocuments(documentResult)

    let total = 0
    let list: any = []
    let newTagAndCount: { [k: string]: IPage }[] = JSON.parse(
      JSON.stringify(defaultTagsAndCount)
    )
    let pageDataList: { [k: string]: any[] }[] = []
    const results = [...findings, newDocumentResult]
    console.info("multipleQueries", results)
    const totalList = results.reduce((acc: any[], res: any) => {
      total += res.nbHits
      const [indexName] = res.index.split("_")
      const indexCfgItem = AlgoliaIndexMap[indexName]
      if (indexCfgItem) {
        const indexTitle = indexCfgItem.title
        // set tab name  and counts
        newTagAndCount = newTagAndCount.map(i => {
          const [name] = Object.keys(i)
          if (indexTitle === name) {
            return {
              ...i,
              ...{ [name]: { total: res.nbHits, curPage: res.page } },
            }
          }
          return { ...i }
        })
        // set each page list
        pageDataList.push({ [indexTitle]: res.hits })
      }
      return [...acc, ...res.hits]
    }, [])
    newTagAndCount = newTagAndCount.map(i => {
      const [name] = Object.keys(i)
      if (t("all") === name) {
        return { ...i, ...{ [t("all")]: { total, curPage: 0 } } }
      }
      return i
    })
    if (activeTabName !== t("all")) {
      const activeResult = results.find(res => {
        const [indexName] = (res.index || "default").split("_")
        const indexCfgItem = AlgoliaIndexMap[indexName]
        return indexCfgItem && indexCfgItem.title === activeTabName
      })
      list = activeResult?.hits
    } else {
      list = totalList
    }
    pageDataList.unshift({ [t("all")]: totalList })
    setTagsAndCount(newTagAndCount)
    setResultList(list)
    setPageDataList(pageDataList)
  }

  const search = useCallback(() => {
    query && searchWord(query, activeTabName)
  }, [query, indices, activeTabName])
  const changeVal = (q: string) => {
    setQuery(q)
  }

  const tabHandle = useCallback(
    (tabName: string) => {
      setTab(tabName)
      const tabData: any = eachPageDataList.find(i => i[tabName] !== undefined)
      tabData && setResultList([...tabData[tabName]])
    },
    [eachPageDataList]
  )
  const onPagination = useCallback(
    (n: number) => {
      const curTagAndCount =
        tagsAndCount.find(i => i[activeTabName] !== undefined) || {}
      const pageItem = curTagAndCount[activeTabName]
      const newPageItem = { ...pageItem, ...{ curPage: n } }
      const newCurTagAndCount = {
        ...curTagAndCount,
        ...{ [activeTabName]: newPageItem },
      }
      const newTagsAndCount = tagsAndCount.map(i => {
        const [name] = Object.keys(i)
        if (name === activeTabName) {
          return newCurTagAndCount
        }
        return i
      })
      setTagsAndCount(newTagsAndCount)
      searchByPageNumber(query, activeTabName, n, eachPageDataList)
    },
    [query, activeTabName, eachPageDataList, tagsAndCount]
  )

  const searchByPageNumber = (
    q: string,
    tabName: string,
    n: number,
    currentPageDataList: recordList
  ) => {
    const indice = indices.find(item => item.tabName === tabName) || {
      name: "default",
      tabName: "default",
    }
    const isSearchingDocs = indice.name.includes("documents_")
    const queryTemplate = {
      indexName: isSearchingDocs ? documentIndexName : indice.name,
      query: q,
      params: {
        hitsPerPage: pageNum,
        page: n,
      },
    }
    let queries = [queryTemplate]
    if (tabName === t("all")) {
      queries = indices.map(i => ({
        ...queryTemplate,
        indexName: i.name,
      }))
    }
    const finalClient = isSearchingDocs ? clientBlog : client
    setLoading(true)
    finalClient
      .search(queries)
      .then(({ results = [] }) => {
        if (results && results.length > 0) {
          const [resultFromSingle] = results
          const { hits } = isSearchingDocs
            ? wrapDocuments(resultFromSingle)
            : resultFromSingle

          let newList = [...hits]
          if (tabName === t("all")) {
            newList = results.reduce((acc: any[], item: any) => {
              return [...acc, ...item.hits]
            }, [])
          }
          setLoading(false)
          setResultList(newList)
          const newEachPageDataList = currentPageDataList.map(i => {
            if (i[tabName] !== undefined) {
              return { [tabName]: [...newList] }
            }
            return i
          })
          setPageDataList(newEachPageDataList)
        }
      })
      .catch(e => {
        setLoading(false)
        console.error(e)
      })
  }
  const getValFromTagsAndCount = (
    tabName: string,
    attr: string,
    map: Record<string, IPage>[]
  ) => {
    const curlVal = map.find(i => Object.keys(i)[0] === tabName)
    const targetVal = curlVal[tabName][attr]
    return targetVal
  }
  const total = useMemo(() => {
    return getValFromTagsAndCount(activeTabName, "total", tagsAndCount)
  }, [tagsAndCount, activeTabName])

  const currentPage = useMemo(() => {
    return parseInt(
      getValFromTagsAndCount(activeTabName, "curPage", tagsAndCount)
    )
  }, [tagsAndCount, activeTabName])

  const pageSize = useMemo(() => {
    return activeTabName === t("all") ? indices.length * pageNum : pageNum
  }, [activeTabName])

  const keyHandle = (e: KeyboardEvent) => {
    if (e.key === "Enter") {
      search()
    }
  }

  return (
    <section className={styles.searchPage}>
      <article className={styles.searchContainer}>
        <Container isSmall>
          <div className={cls(styles.form)}>
            <div className={cls(styles.inputWrapper)} ref={inputContainer}>
              <Input onChange={changeVal} value={query} keyHandle={keyHandle} />
            </div>
            <button className="button is-primary ml-4" onClick={search}>
              {t("search")}
            </button>
          </div>
        </Container>
      </article>
      <Container className={styles.resultContainer} isSmall>
        <div className={cls(styles.hitsWrapper)}>
          <div className={styles.tabsContainer}>
            <SearchTabs
              activeName={activeTabName}
              tagsAndCount={tagsAndCount}
              tabHandle={tabHandle}
            />
          </div>
          <Loading isLoading={isLoading}>
            {!isMounted ? (
              <article style={{ height: "23rem" }} />
            ) : (
              <SearchResults resultList={resultList} />
            )}
          </Loading>
          {resultList && resultList.length > 0 ? (
            <div className={styles.paginationWrapper}>
              <Pagination
                total={total}
                onSelect={onPagination}
                pageSize={pageSize}
                currentPage={currentPage}
              />
            </div>
          ) : null}
        </div>
      </Container>
    </section>
  )
}

export default SearchPage
