import { useUserHasResponded } from 'domains/app/hooks'
import { useConfig } from 'domains/config/hooks'
import { useI18n } from 'domains/i18n/hooks'
import { selectHasError } from 'domains/interrupt/selectors'
import { ServiceDataEvent } from 'domains/store/store.types'
import { useTranslatedEventData } from 'domains/translations/hooks'
import { visibilityStates } from 'domains/visibility/constants'
import { useVisibility } from 'domains/visibility/hooks'
import { className } from 'lib/css'
import { randomId } from 'lib/id'
import { useCallback, useEffect, useMemo, useRef } from 'preact/hooks'
import { useSelector } from 'react-redux'
import SuggestionsList from 'ui/components/suggestions/suggestions-list'
import InOutTransition, {
  transitionStartStates,
} from 'ui/components/widgets/in-out-transition'
import { useSkiplinkTargetFocusing } from 'ui/hooks/focus-helper-hooks'
import { useLiveRegion } from 'ui/hooks/live-region-hooks'
import { useSeamlyHasConversation } from 'ui/hooks/seamly-api-hooks'
import {
  useSeamlyLayoutMode,
  useSeamlyServiceData,
} from 'ui/hooks/seamly-state-hooks'
import useSeamlyCommands from 'ui/hooks/use-seamly-commands'
import useSeamlyIdleDetachCountdown from 'ui/hooks/use-seamly-idle-detach-countdown'
import useSeamlyResumeConversationPrompt from 'ui/hooks/use-seamly-resume-conversation-prompt'
import { useGeneratedId } from 'ui/hooks/utility-hooks'
import { runIfElementContainsOrHasFocus } from 'ui/utils/general-utils'
import { actionTypes } from 'ui/utils/seamly-utils'

const Suggestions = ({ isAside = false }) => {
  // generic hooks
  const { isInline } = useSeamlyLayoutMode()
  const { t } = useI18n()
  const { addMessageBubble, emitEvent, sendAction } = useSeamlyCommands()
  const hasConversation = useSeamlyHasConversation()
  const { isOpen, setVisibility } = useVisibility()
  const { showSuggestions } = useConfig()
  // a11y hooks
  const sectionId = useGeneratedId()
  const focusSkiplinkTarget = useSkiplinkTargetFocusing()
  const containerRef = useRef(null)
  const { sendPolite } = useLiveRegion()
  // interrupt & countdown hooks
  const hasError = useSelector(selectHasError)
  const { hasCountdown, endCountdown } = useSeamlyIdleDetachCountdown()
  const { hasPrompt, continueChat } = useSeamlyResumeConversationPrompt()
  // data hooks
  const userHasResponded = useUserHasResponded()
  const payload = useSeamlyServiceData('suggestion')
  const { body: eventBody } = useTranslatedEventData({
    payload,
    type: 'service_data',
  })

  const suggestions = useMemo(() => {
    if (hasError) return []
    if (Array.isArray(eventBody)) {
      return eventBody
    }
    return []
  }, [eventBody, hasError])

  const prevSuggestions = useRef<ServiceDataEvent['payload']['body']>()
  const prevHasSuggestions = useRef(false)

  const previousRenderedSuggestions =
    useRef<ServiceDataEvent['payload']['body']>()
  const hasSuggestions = !!suggestions?.length
  const hideSuggestions = isInline
    ? (userHasResponded || isOpen) && !isAside
    : userHasResponded
  const prevHideSuggestions = useRef(hideSuggestions)
  const showSuggestionsContainer =
    hasSuggestions && !hideSuggestions && showSuggestions
  const renderedSuggestions = hasSuggestions
    ? suggestions
    : previousRenderedSuggestions.current
  previousRenderedSuggestions.current = renderedSuggestions

  const suggestionsClassNames = useMemo(() => {
    const classNames = ['suggestions']

    if (isAside) {
      classNames.push('suggestions--aside')
    }

    return className(classNames)
  }, [isAside])

  // click handler
  const handleClick = useCallback(
    ({
      id,
      question,
    }: {
      id: string | number | undefined
      question?: string | undefined
    }) => {
      // Do not allow interaction without a conversation
      if (!hasConversation()) {
        return
      }

      if (hasCountdown) {
        endCountdown(true)
      }

      if (hasPrompt) {
        continueChat()
      }

      const transactionId = randomId()
      const action = {
        type: actionTypes.custom,
        originMessage: payload.id,
        body: {
          type: 'faqclick',
          body: {
            faqId: id,
            faqQuestion: question,
          },
        },
        transactionId,
      }

      // @todo Refactor to 'suggestionclick'
      sendAction(action)
      addMessageBubble(question, transactionId)
      emitEvent(`action.${action.type}`, action)

      if (!isOpen) {
        setVisibility({ visibility: visibilityStates.open })
      }
      focusSkiplinkTarget()
    },
    [
      addMessageBubble,
      continueChat,
      endCountdown,
      emitEvent,
      focusSkiplinkTarget,
      hasConversation,
      hasCountdown,
      hasPrompt,
      isOpen,
      payload,
      sendAction,
      setVisibility,
    ],
  )

  useEffect(() => {
    if (prevSuggestions.current !== suggestions && !hideSuggestions) {
      if (hasSuggestions) {
        const politeText = prevHasSuggestions.current
          ? t('suggestions.srUpdatedText')
          : t('suggestions.srAvailableText')
        setTimeout(() => {
          sendPolite(politeText)
        }, 30)
      } else if (prevHasSuggestions.current) {
        sendPolite(t('suggestions.srUnavailableText'))
      }

      prevSuggestions.current = suggestions
    }

    if (!prevHideSuggestions.current && hideSuggestions) {
      runIfElementContainsOrHasFocus(containerRef.current, focusSkiplinkTarget)
      sendPolite(t('suggestions.srUnavailableText'))
    } else if (!hasSuggestions && prevHasSuggestions.current) {
      runIfElementContainsOrHasFocus(containerRef.current, focusSkiplinkTarget)
    }
    prevHasSuggestions.current = hasSuggestions
    prevHideSuggestions.current = hideSuggestions
  }, [
    suggestions,
    hasSuggestions,
    hideSuggestions,
    focusSkiplinkTarget,
    sendPolite,
    t,
  ])

  const headingText = t('suggestions.headingText')
  const footerText = t('suggestions.footerText')
  const ContainerElement = headingText ? 'section' : 'div'

  return (
    <InOutTransition
      isActive={!!showSuggestionsContainer}
      transitionStartState={transitionStartStates.notRendered}
    >
      <ContainerElement
        className={suggestionsClassNames}
        aria-labelledby={headingText ? sectionId : undefined}
        ref={containerRef}
      >
        {headingText && (
          <p id={sectionId} className={className('suggestions__heading')}>
            {headingText}
          </p>
        )}
        {!!renderedSuggestions && (
          <SuggestionsList
            suggestions={renderedSuggestions}
            onClickSuggestion={handleClick}
          />
        )}
        {footerText && !isOpen && (
          <p className={className('suggestions__footer')}>{footerText}</p>
        )}
      </ContainerElement>
    </InOutTransition>
  )
}

export default Suggestions
