import React, { useEffect, useState } from 'react'

import styles from './_menu.module.scss'
import { hasValue } from '../../services/HelperServiceTyped'
import Icon from '../Icons/Icon'
import { type IconStringList } from '../Icons/Icon.models'
import { type TooltipProps } from '../Tooltip/Tooltip'
import { type ConfirmationPopoverContentProps } from '../ConfirmationPopover/ConfirmationPopoverContent'
import MenuCsvRow from './MenuComponent/MenuCsvRow'
import MenuToggleRow from './MenuComponent/MenuToggleRow'
import MenuStandardRow from './MenuComponent/MenuStandardRow'
import MenuConfirmation from './MenuComponent/MenuConfirmation'
import { c } from '../../translations/LibraryTranslationService'
import type SingleCsv from '../CsvExport/SingleCsv'
import SubMenu from './MenuComponent/SubMenu'

export type MenuActionPropsBase = {
  /** Optionally style the action `text` and `icon` as red */
  destructive?: boolean
  /** Optionally add a divider above an action. This should only be used for a single action. If there are multiple actions that should appear after the divider, make sure those actions appear after the action that has this prop. */
  hasDivider?: boolean
  /** Optionally disable an action */
  disabled?: {
    /** The current value of the disabled prop */
    value: boolean
    /** Optionally show a tooltip when an action is disabled */
    tooltip?: Omit<TooltipProps, 'children'>
  }
  /** Optionally show a confirmation when selecting an option */
  confirmation?: Omit<ConfirmationPopoverContentProps, 'cancelCallout'>
  /** Optional prop to add a test id to the Menu actions for QA testing */
  qaTestId?: string
}

type MenuActionPropsStandard = MenuActionPropsBase & {
  /** Optional icon to appear to the left of the `text` */
  icon?: IconStringList
  /** Text displayed for an action */
  text: string
  /** Function callout for an action */
  callout?: () => void
  /** Callback function triggered when a CSV action is performed. */
  handleCsvAction?: never
  /** Secondary content to be displayed on the right side of the row */
  secondaryContent?: React.ReactNode
  toggle?: never
  csv?: never
  subMenuTriggerActions?: never
}

export type MenuActionPropsWithSubMenu = MenuActionPropsBase & {
  text: string
  icon?: IconStringList
  /** Actions to render in a new submenu, triggered by this item. */
  subMenuTriggerActions: MenuActionProps[]
  callout?: never
  link?: never
  routerComponent?: never
  routerProp?: never
  toggle?: never
  csv?: never
  secondaryContent?: never
  handleCsvAction?: never
  setLoading?: never
  loading?: never
}

type MenuActionPropsWithLink = MenuActionPropsStandard & {
  /** If a `link` is provided, then the Button will render as='link' */
  link: string
  /** Router component to use for the link */
  routerComponent: React.ElementType
  /** Router prop to use for the link */
  routerProp: string
  setLoading?: never
  loading?: never
  secondaryContent?: never
  /** Callback function triggered when a CSV action is performed. */
  handleCsvAction?: never
}

type MenuActionPropsWithoutLink = MenuActionPropsStandard & {
  link?: never
  routerComponent?: never
  routerProp?: never
  setLoading?: never
  loading?: never
  /** Callback function triggered when a CSV action is performed. */
  handleCsvAction?: never
}

export type MenuActionPropsWithToggle = MenuActionPropsBase & {
  /** Optional icon to appear to the left of the `text` */
  icon?: IconStringList
  /** Text displayed for an action */
  text: string
  callout?: never
  handleCsvAction?: never
  /** `Toggle` to appear to the right of the `text` */
  toggle: {
    checked: boolean
    callout: (value: boolean) => void
  }
  csv?: never
  link?: never
  routerComponent?: never
  routerProp?: never
  setLoading?: never
  loading?: never
  secondaryContent?: never
  subMenuTriggerActions?: never
}

type SingleCsvProps = React.ComponentProps<typeof SingleCsv>

type MenuActionPropsWithCSV = MenuActionPropsBase & {
  /** CSV action */
  csv: SingleCsvProps['csv']
  /** Callback function triggered when a CSV action is performed. */
  handleCsvAction?: SingleCsvProps['handleCsvAction']
  /** Callback to update the state indicating change in download. */
  setLoading?: SingleCsvProps['setLoading']
  /** State indicating download status */
  loading?: SingleCsvProps['loading']
  text?: never
  callout?: never
  toggle?: never
  icon?: never
  link?: never
  routerComponent?: never
  routerProp?: never
  secondaryContent?: never
  subMenuTriggerActions?: never
}

export type MenuActionProps =
  | MenuActionPropsWithToggle
  | MenuActionPropsWithCSV
  | MenuActionPropsWithLink
  | MenuActionPropsWithSubMenu
  | MenuActionPropsWithoutLink

export interface MenuProps {
  /** Array of actions to used for the Menu */
  actions: MenuActionProps[]
  /** A close function is often required to close a popover where the Menu is being used. */
  close?: () => void
  /** This tells the consuming component when the form state is being changed so that component can re-render correctly. Only used when there is a confirmation option. */
  reRenderView?: () => void
  /** This is used to close the confirmation within a popover. */
  closeConfirmation?: boolean
  /** Optional prop to add a test id to the Menu for QA testing */
  qaTestId?: string
}

const Menu = ({
  actions,
  close,
  reRenderView,
  closeConfirmation,
  qaTestId = 'menu',
}: MenuProps): React.JSX.Element => {
  const [currentConfirmation, setCurrentConfirmation] =
    useState<MenuActionProps['confirmation']>()

  const confirmationCallout = (isConfirm: boolean) => {
    setCurrentConfirmation(undefined)
    reRenderView?.()
    isConfirm && close?.()
  }

  actions.forEach((action) => {
    if (
      !action.csv &&
      !action.toggle &&
      !action.callout &&
      !action.link &&
      !action.confirmation &&
      !action.subMenuTriggerActions
    ) {
      throw new Error(c('errorMenuActionProps'))
    }
  })

  useEffect(() => {
    setCurrentConfirmation(undefined)
  }, [closeConfirmation])

  return currentConfirmation ? (
    <MenuConfirmation
      confirmation={currentConfirmation}
      callout={confirmationCallout}
      qaTestId={qaTestId}
    />
  ) : (
    <div className={styles.menu} qa-test-id={qaTestId}>
      {actions.map((action, index) => {
        if (actions.filter((act) => act.hasDivider).length > 1) {
          throw new Error(c('errorMenuDuplicateDivider'))
        }

        const keyText = action.text
        let keyCsv: string | undefined
        if ('csv' in action && action.csv) {
          keyCsv = action.csv.csvName
        }

        let itemKey: string | undefined
        if ('subMenuTriggerActions' in action && action.subMenuTriggerActions) {
          itemKey = keyText
        } else {
          itemKey = keyText ?? keyCsv
        }

        return (
          <MenuRow
            {...action}
            key={itemKey ?? `menu-action-${index}`}
            setCurrentConfirmation={(confirmation) => {
              setCurrentConfirmation(confirmation)
              reRenderView?.()
            }}
            close={close}
          />
        )
      })}
    </div>
  )
}

export default Menu

const MenuRow = ({
  text,
  icon,
  destructive,
  hasDivider,
  disabled,
  confirmation,
  callout,
  link,
  routerComponent,
  routerProp,
  toggle,
  csv,
  setCurrentConfirmation,
  close,
  setLoading,
  loading,
  secondaryContent,
  handleCsvAction,
  subMenuTriggerActions,
  qaTestId,
}: MenuActionProps & {
  setCurrentConfirmation: (
    confirmation: MenuActionProps['confirmation'],
  ) => void
  close?: () => void
}) => {
  const CONTAINER_CLASSES = `${styles.menuItem} ${
    destructive ? styles.darkRed : ''
  } ${hasDivider ? styles.highlighted : ''} ${
    disabled?.value ? styles.disabled : ''
  }`
  let rowType = 'standard'

  switch (true) {
    case hasValue(subMenuTriggerActions):
      rowType = 'subMenu'
      break
    case hasValue(toggle):
      rowType = 'toggle'
      break
    case hasValue(csv):
      rowType = 'csv'
      break
    case hasValue(link):
      rowType = 'link'
      break
    default:
      break
  }

  return {
    subMenu:
      rowType === 'subMenu' && subMenuTriggerActions ? (
        <SubMenu
          text={text!}
          icon={icon}
          destructive={destructive}
          disabled={disabled}
          hasDivider={hasDivider}
          subMenuActions={subMenuTriggerActions}
          className={CONTAINER_CLASSES}
          closeParentMenu={close}
          qaTestId={qaTestId}
        />
      ) : null,
    toggle:
      rowType === 'toggle' && toggle ? (
        <MenuToggleRow
          text={text!}
          icon={icon}
          toggle={toggle}
          disabled={disabled}
          className={CONTAINER_CLASSES}
          close={close}
          qaTestId={qaTestId}
        />
      ) : null,
    csv:
      rowType === 'csv' && csv ? (
        <MenuCsvRow
          csv={csv}
          className={CONTAINER_CLASSES}
          disabled={disabled}
          close={close}
          handleCsvAction={handleCsvAction}
          setLoading={setLoading}
          loading={loading}
          {...(destructive ? { destructive } : {})}
          qaTestId={qaTestId}
        />
      ) : null,
    link:
      rowType === 'link' && link ? (
        <MenuStandardRow
          className={CONTAINER_CLASSES}
          disabled={disabled}
          confirmation={confirmation}
          callout={callout}
          setCurrentConfirmation={setCurrentConfirmation}
          text={text!}
          icon={icon}
          link={link}
          routerComponent={routerComponent!}
          routerProp={routerProp!}
          destructive={destructive}
          secondaryContent={secondaryContent}
          qaTestId={qaTestId}
        />
      ) : null,
    standard:
      rowType === 'standard' ? (
        <MenuStandardRow
          className={CONTAINER_CLASSES}
          disabled={disabled}
          confirmation={confirmation}
          callout={() => {
            callout?.()
            close?.()
          }}
          setCurrentConfirmation={setCurrentConfirmation}
          text={text!}
          icon={icon}
          destructive={destructive}
          secondaryContent={secondaryContent}
          qaTestId={qaTestId}
        />
      ) : null,
  }[rowType]
}

export const IconAndName = ({
  icon,
  text,
  destructive,
}: Pick<MenuActionProps, 'icon' | 'text' | 'destructive'>) => {
  return (
    <span className={styles.iconAndName}>
      {icon ? (
        <Icon
          icon={icon}
          iconSize='16px'
          color={destructive ? 'dark-red' : 'purple'}
        />
      ) : null}
      {text}
    </span>
  )
}
