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,
  MilestoneState,
} from '@inject/graphql/fragment-types'
import StyledTag from '@inject/shared/components/StyledTag'
import { ellipsized } from '@inject/shared/css/textOverflow'
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,
  getTeamById,
  highlightedOnActive,
  highlightedOnHover,
} from '../utilities'

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

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

const getIcon = (logType: ActionLogFragment['type']) => {
  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 getContent = (actionLog: ActionLogFragment) => {
  switch (actionLog.details.__typename) {
    case 'InjectDetailsType':
      return textFromRenderedContent(actionLog.details.content.rendered)
    case 'CustomInjectDetailsType':
      return textFromRenderedContent(actionLog.details.content.rendered)
    case 'ToolDetailsType':
      return `${actionLog.details.tool.name}, ${actionLog.details.argument} → ${textFromRenderedContent(actionLog.details.content.rendered)}`
    case 'QuestionnaireType':
      return actionLog.details.title
    case 'EmailType': {
      const senderId = actionLog.details.sender.id
      const recipients = actionLog.details.thread.participants
        .filter(participant => participant.id !== senderId)
        .map(participant => participant.address)
        .join(', ')
      return `${actionLog.details.sender.address} → ${recipients}: ${textFromRenderedContent(actionLog.details.content.rendered)}`
    }
    default:
      throw new Error(`unknown 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 milestoneStateTeamNames = useCallback(
    (milestoneState: MilestoneState) =>
      milestoneState.teamIds
        ?.filter(notEmpty)
        .map(teamId => getTeamById(exercise.teams, teamId)?.name)
        .join(', '),
    [exercise.teams]
  )

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

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

    return items.sort(
      (a, b) =>
        -compareDates(new Date(a.timestamp || 0), new Date(b.timestamp || 0))
    )
  }, [
    actionLogs,
    getHandleClick,
    milestoneStateTeamNames,
    milestoneStates,
    selectedDispatch,
  ])

  return items.length > 0 ? (
    <div style={{ flex: 1 }}>
      <table style={{ borderCollapse: 'collapse' }}>
        <tbody style={{ display: 'block' }}>
          {items.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 ? '13ch' : '22ch',
                  textAlign: 'center',
                }}
              >
                <StyledTag content={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
)
