import { memoizeOne } from "@investtal/toolkit"
import { createElement } from "react"
import { findAll } from "./highlight-words-core"

interface HighlighterProps {
  activeClassName?: string
  activeIndex?: number
  activeStyle?: React.CSSProperties
  autoEscape?: boolean
  caseSensitive?: boolean
  className?: string
  findChunks?: (options: any) => Array<{ start: number; end: number }>
  highlightClassName?: string | Record<string, string>
  highlightStyle?: React.CSSProperties
  highlightTag?: React.ComponentType<any> | string
  sanitize?: (text: string) => string
  searchWords: Array<string>
  textToHighlight: string
  unhighlightTag?: React.ComponentType<any> | string
  unhighlightClassName?: string
  unhighlightStyle?: React.CSSProperties
  [key: string]: any
}

export default function Highlighter({
  activeClassName = "",
  activeIndex = -1,
  activeStyle,
  autoEscape,
  caseSensitive = false,
  className,
  findChunks,
  highlightClassName = "",
  highlightStyle = {},
  highlightTag = "mark",
  sanitize,
  searchWords,
  textToHighlight,
  unhighlightTag = "span",
  unhighlightClassName = "",
  unhighlightStyle,
  ...rest
}: HighlighterProps) {
  const chunks = findAll({
    autoEscape,
    caseSensitive,
    findChunks,
    sanitize,
    searchWords,
    textToHighlight,
  })
  const HighlightTag = highlightTag
  let highlightIndex = -1
  let highlightClassNames = ""
  let highlightStyles: React.CSSProperties = {}

  const lowercaseProps = (object: Record<string, string>): Record<string, string> => {
    const mapped: Record<string, string> = {}
    for (const key in object) {
      mapped[key.toLowerCase()] = object[key]
    }
    return mapped
  }
  const memoizedLowercaseProps = memoizeOne(lowercaseProps)

  return createElement(
    "span",
    {
      className,
      ...rest,
    },
    chunks.map((chunk, index) => {
      const text = textToHighlight.slice(chunk.start, chunk.end)

      if (chunk.highlight) {
        highlightIndex++

        let highlightClass: string
        if (typeof highlightClassName === "object") {
          const classNameMap = !caseSensitive
            ? memoizedLowercaseProps(highlightClassName)
            : highlightClassName
          highlightClass = classNameMap[!caseSensitive ? text.toLowerCase() : text]
        } else {
          highlightClass = highlightClassName
        }

        const isActive = highlightIndex === +activeIndex

        highlightClassNames = `${highlightClass} ${isActive ? activeClassName : ""}`
        highlightStyles =
          isActive === true && activeStyle != null
            ? { ...highlightStyle, ...activeStyle }
            : highlightStyle

        const props: any = {
          className: highlightClassNames,
          key: index,
          style: highlightStyles,
        }

        // Don't attach arbitrary props to DOM elements; this triggers React DEV warnings (https://fb.me/react-unknown-prop)
        // Only pass through the highlightIndex attribute for custom components.
        if (typeof HighlightTag !== "string") {
          props.highlightIndex = highlightIndex
        }

        return createElement(HighlightTag, props, text)
      }

      return createElement(
        unhighlightTag,
        {
          className: unhighlightClassName,
          key: index,
          style: unhighlightStyle,
        },
        text,
      )
    }),
  )
}
