import useActionLogs from '@/analyst/dataHooks/useActionLogs'
import useMilestoneStates from '@/analyst/dataHooks/useMilestoneStates'
import { useRelativeTime } from '@/clientsettings/vars/relativeTime'
import { textFromRenderedContent } from '@/utils'
import { Icon, NonIdealState } from '@blueprintjs/core'
import { css, cx } from '@emotion/css'
import type { ActionLog as ActionLogFragment } from '@inject/graphql/fragments/ActionLog.generated'
import type { CustomInjectDetails } from '@inject/graphql/fragments/CustomInjectDetails.generated'
import type { EmailDetails } from '@inject/graphql/fragments/EmailDetails.generated'
import type { InjectDetails } from '@inject/graphql/fragments/InjectDetails.generated'
import type { MilestoneState } from '@inject/graphql/fragments/MilestoneState.generated'
import type { QuestionnaireDetails } from '@inject/graphql/fragments/QuestionnaireDetails.generated'
import type { ToolDetails } from '@inject/graphql/fragments/ToolDetails.generated'
import type { LogType } from '@inject/graphql/types'
import notEmpty from '@inject/shared/utils/notEmpty'
import type { Dispatch, FC, ReactNode } from 'react'
import { memo, useCallback, useContext, useMemo } from 'react'
import ExerciseContext from '../ExerciseContext'
import type { selectedReducerActionProps } from '../Overview/selectedReducer'
import useFormatTimestamp from '../useFormatTimestamp'
import {
  actionTypeColor,
  compareDates,
  ellipsized,
  getTeamById,
  highlightedOnActive,
  highlightedOnHover,
} from '../utilities'

const nonIdeal = css`
  align-self: center;
`

const td = css`
  ${ellipsized};
  padding: 0.5rem;
  cursor: pointer;
`

const getIcon = (logType: LogType) => {
  switch (logType) {
    case 'INJECT':
    case 'CUSTOM_INJECT':
    case 'TOOL':
    case 'FORM':
      return (
        <Icon icon='full-circle' color={actionTypeColor(logType.toString())} />
      )
    case 'EMAIL':
      return <Icon icon='envelope' />
    default:
      throw new Error(`unknown actionLog type: ${logType}`)
  }
}

const getRecipients = (details: EmailDetails) =>
  details.thread.participants
    .filter(participant => participant.id !== details.sender.id)
    .map(participant => participant.address)
    .join(', ')

const getContent = (actionLog: ActionLogFragment) => {
  let details
  switch (actionLog.type) {
    case 'INJECT':
      details = actionLog.details as InjectDetails
      return textFromRenderedContent(details.content.rendered)
    case 'CUSTOM_INJECT':
      details = actionLog.details as CustomInjectDetails
      return textFromRenderedContent(details.content.rendered)
    case 'TOOL':
      details = actionLog.details as ToolDetails
      return `${details.tool.name}, ${details.argument} → ${textFromRenderedContent(details.content.rendered)}`
    case 'FORM':
      details = actionLog.details as QuestionnaireDetails
      return details.title
    case 'EMAIL':
      details = actionLog.details as EmailDetails
      return `${details.sender.address} → ${getRecipients(details)}: ${textFromRenderedContent(details.content.rendered)}`
    default:
      throw new Error(`unknown actionLog type: ${actionLog.type}`)
  }
}

type ActionLogItem = {
  id: string
  icon: ReactNode
  text: string
  timestamp: string | null
  handleClick: () => void
}

interface ActionLogProps {
  selectedDispatch: Dispatch<selectedReducerActionProps>
}

const ActionLog: FC<ActionLogProps> = ({ selectedDispatch }) => {
  const milestoneStates = useMilestoneStates()
  const actionLogs = useActionLogs()
  const timeRelative = useRelativeTime()
  const formatTimestamp = useFormatTimestamp()
  const { exercise } = useContext(ExerciseContext)

  const getHandleClick = useCallback(
    (actionLog: ActionLogFragment) => () => {
      switch (actionLog.type) {
        case 'INJECT':
        case 'CUSTOM_INJECT':
        case 'TOOL':
        case 'FORM':
          return selectedDispatch({ type: 'selectActions', actionLog })
        case 'EMAIL':
          return selectedDispatch({ type: 'selectEmails', actionLog })
        default:
          throw new Error(`unknown actionLog type: ${actionLog.type}`)
      }
    },
    [selectedDispatch]
  )

  const items: ActionLogItem[] = useMemo(
    () =>
      actionLogs.map(actionLog => ({
        id: actionLog.id,
        icon: getIcon(actionLog.type),
        text: getContent(actionLog),
        timestamp: actionLog.timestamp,
        handleClick: getHandleClick(actionLog),
      })),
    [actionLogs, getHandleClick]
  )

  const milestoneStateTeamNames = (milestoneState: MilestoneState) =>
    milestoneState.teamIds
      ?.filter(notEmpty)
      .map(teamId => getTeamById(exercise.teams, teamId)?.name)
      .join(', ')

  milestoneStates
    .filter(milestoneState => milestoneState.reached)
    .forEach(milestoneState =>
      items.push({
        id: `milestone:${
          milestoneState.milestone.id
        },teams:${milestoneState.teamIds?.join(',')}`,
        icon: <Icon icon='flag' />,
        text:
          `milestone ${milestoneState.milestone.name} reached, ` +
          `teams: ${milestoneStateTeamNames(milestoneState)}`,
        timestamp: milestoneState.timestampReached,
        handleClick: () =>
          selectedDispatch({ type: 'selectMilestones', milestoneState }),
      })
    )

  return items.length > 0 ? (
    <div style={{ flex: 1 }}>
      <table style={{ borderCollapse: 'collapse' }}>
        <tbody style={{ display: 'block' }}>
          {items
            .sort(
              (a, b) =>
                -compareDates(
                  new Date(a.timestamp || ''),
                  new Date(b.timestamp || '')
                )
            )
            .map(item => (
              <tr
                key={item.id}
                className={cx(highlightedOnHover, highlightedOnActive)}
                style={{
                  display: 'table',
                  tableLayout: 'fixed',
                  width: '100%',
                }}
                onClick={item.handleClick}
              >
                <td
                  className={td}
                  style={{ width: 32 /* icon size + padding */ }}
                >
                  {item.icon}
                </td>
                <td
                  className={td}
                  style={{
                    width: timeRelative ? '12%' : '25%',
                    textAlign: 'center',
                  }}
                >
                  {formatTimestamp(item.timestamp)}
                </td>
                <td className={td}>{item.text}</td>
              </tr>
            ))}
        </tbody>
      </table>
    </div>
  ) : (
    <NonIdealState
      className={nonIdeal}
      icon='search'
      title='No action logs'
      description='There are no action logs to display'
    />
  )
}

export default memo(
  ActionLog,
  (prev, next) => prev.selectedDispatch === next.selectedDispatch
)
