/* eslint-disable react/destructuring-assignment */
import React, { HTMLAttributes, MouseEvent } from 'react'
import parse from 'autosuggest-highlight/parse'
import match from 'autosuggest-highlight/match'

import { useTranslation } from 'next-i18next'

import { ITreeItem } from '@elastic-suite/gally-admin-shared'

import Checkbox from '../form/CheckboxWithoutError'
import IonIcon from '../IonIcon/IonIcon'

import {
  CustomBtn,
  CustomContainer,
  CustomLi,
  CustomRoot,
  CustomTitle,
  CustomTitleBase,
  CustomTitleContainer,
  CustomVirtual,
  SmallCustomBtn,
  SmallCustomContainer,
  Underline,
} from './Tree.styled'
import { TestId, generateTestId } from '../../../utils/testIds'

function highlight(label: string, search: string): JSX.Element[] {
  const matches = match(label, search)
  const parts = parse(label, matches)
  return parts.map(
    (
      { highlight, text }: { highlight: boolean; text: string },
      index: number
    ) => {
      const Span = highlight ? Underline : 'span'
      return (
        // eslint-disable-next-line react/no-array-index-key
        <Span key={index}>{text}</Span>
      )
    }
  )
}

interface IProps<Multiple extends boolean | undefined> {
  base?: boolean
  data: ITreeItem[]
  getItemProps?: (item: ITreeItem) => HTMLAttributes<HTMLLIElement>
  getListboxProps?: () => HTMLAttributes<HTMLUListElement>
  multiple?: Multiple
  onChange?: Multiple extends true
    ? (item?: ITreeItem[]) => void
    : (item?: ITreeItem) => void
  onToggle?: (items: Record<string, boolean>) => void
  openItems?: Record<string, boolean>
  search?: string
  small?: boolean
  value?: Multiple extends true ? ITreeItem[] : ITreeItem
  componentId?: string
}

function Tree<Multiple extends boolean | undefined>(
  props: IProps<Multiple>
): JSX.Element {
  const {
    base = true,
    data,
    getItemProps,
    getListboxProps,
    multiple,
    onChange,
    onToggle,
    openItems,
    search,
    small,
    value,
    componentId,
  } = props

  const { t } = useTranslation('categories')

  const Title = base ? CustomTitleBase : CustomTitle
  const Container = small ? SmallCustomContainer : CustomContainer
  const Button = small ? SmallCustomBtn : CustomBtn

  function handleClick(event: MouseEvent<HTMLUListElement>): void {
    event.stopPropagation()
  }

  function handleToggle(item: ITreeItem) {
    return (event: MouseEvent<HTMLButtonElement>): void => {
      event.stopPropagation()
      onToggle({
        ...openItems,
        [item.id]: !openItems[item.id],
      })
    }
  }

  function handleChecked(item: ITreeItem) {
    return (): void => {
      if (onChange) {
        if (multiple) {
          const onChange = props.onChange as (item?: ITreeItem[]) => void
          const value = props.value as ITreeItem[]
          const checked = value.includes(item)
          checked
            ? onChange(value.filter((val) => val !== item))
            : onChange(value.concat(item))
        } else {
          const onChange = props.onChange as (item?: ITreeItem) => void
          const value = props.value as ITreeItem
          const checked = value === item
          !checked && onChange(item)
        }
      }
    }
  }

  return (
    <CustomRoot
      onClick={handleClick}
      {...(base && getListboxProps?.())}
      data-testid={generateTestId(TestId.TREE, componentId)}
    >
      {data.map((item: ITreeItem) => {
        const title = (
          <Title
            data-testid={generateTestId(
              TestId.TREE_ITEM_TITLE_BUTTON,
              componentId
            )}
            onClick={handleChecked(item)}
          >
            {search ? (
              highlight(item.name, search)
            ) : !(value instanceof Array) && value === item ? (
              <Underline>{item.name}</Underline>
            ) : (
              item.name
            )}
          </Title>
        )
        return (
          <CustomLi
            key={item.id}
            {...getItemProps?.(item)}
            style={{ marginLeft: base ? 0 : small ? 12 : 20 }}
          >
            <Container
              style={{
                marginLeft: item.children ? 0 : small ? 19 : 28,
              }}
            >
              {item.children ? (
                <Button
                  onClick={handleToggle(item)}
                  data-testid={generateTestId(
                    TestId.TREE_COLLAPSING_ITEM_LIST_BUTTON,
                    componentId
                  )}
                >
                  <IonIcon
                    name={openItems[item.id] ? 'minus' : 'more'}
                    style={{ fontSize: small ? '15px' : '24px' }}
                  />
                </Button>
              ) : null}
              <CustomTitleContainer>
                {multiple ? (
                  <Checkbox
                    checked={
                      value instanceof Array
                        ? value.includes(item)
                        : value === item
                    }
                    label={title}
                    list
                    onChange={handleChecked(item)}
                    small={small}
                  />
                ) : (
                  title
                )}
                {item.isVirtual ? (
                  <CustomVirtual>{t('tree.virtual')}</CustomVirtual>
                ) : null}
              </CustomTitleContainer>
            </Container>
            {Boolean(openItems[item.id] && item.children) && (
              <Tree
                {...props}
                base={false}
                data={item.children}
                componentId={componentId}
              />
            )}
          </CustomLi>
        )
      })}
    </CustomRoot>
  )
}

Tree.defaultProps = {
  base: true,
  openItems: {},
}

export default Tree
