import { getTitle } from '@/actionlog/InjectMessage/utils'
import { EmailSelection } from '@/analyst/utilities'
import { useNavigate } from '@/router'
import { Button, ButtonGroup, Classes, Icon, Tag } from '@blueprintjs/core'
import type { IconName } from '@blueprintjs/icons'
import { css, cx } from '@emotion/css'
import type { SimplifiedActionLog } from '@inject/graphql/fragment-types'
import { useTypedQuery } from '@inject/graphql/graphql'
import { GetSingleActionLog, GetThreadTemplates } from '@inject/graphql/queries'
import { useClient } from '@inject/graphql/urql/client'
import { getColorScheme } from '@inject/shared/components/ColorBox'
import Timestamp from '@inject/shared/components/StyledTag/Timestamp'
import {
  forwardRef,
  Suspense,
  useCallback,
  useMemo,
  type CSSProperties,
  type FC,
} from 'react'
import { execTodoWrite, selectTodoCat } from './utils'

const GetTemplateCount: FC<{
  threadId: string
}> = ({ threadId }) => {
  const [{ data }] = useTypedQuery({
    query: GetThreadTemplates,
    variables: {
      threadId,
    },
    requestPolicy: 'cache-first',
  })
  const count = data?.threadTemplates.length

  return (
    !!count && (
      <Tag style={{ marginLeft: '0.25rem' }} round intent='warning'>
        {count} templates
      </Tag>
    )
  )
}

const GetContent: FC<{
  actionLog: SimplifiedActionLog
}> = ({ actionLog }) => {
  const { details } = actionLog
  const nav = useNavigate()

  switch (details.__typename) {
    case 'InjectDetailsType':
      return <p>New inject: {details.inject.name}</p>
    case 'EmailType':
      return (
        <p>
          Message from: <b>{details.sender.address}</b> in thread{' '}
          <Tag
            className={css`
              &:hover {
                cursor: pointer;
              }
            `}
            title='Click to enter the thread'
            onClick={() => {
              nav(
                '/instructor/:exerciseId/:teamId/:channelId/email/:tab/:threadId',
                {
                  params: {
                    threadId: details.thread.id,
                    channelId: actionLog.channel.id,
                    exerciseId: actionLog.team.exercise.id,
                    teamId: actionLog.team.id,
                    tab: actionLog.requiresAttention
                      ? EmailSelection.SENT
                      : EmailSelection.RECEIVED,
                  },
                }
              )
            }}
            round
            intent='primary'
          >
            {details.thread.subject}
          </Tag>
          <GetTemplateCount threadId={details.thread.id} />
        </p>
      )
    case 'QuestionnaireType':
      return <p>{details.title}</p>
    case 'ToolDetailsType':
      return (
        <p>
          {'Tool usage for tool '}
          <b>
            <code>{details.tool.name}</code>
          </b>
          {details.tool.requiresInput && (
            <>
              {' with argument '}
              <b>
                <code>{details.argument}</code>
              </b>
            </>
          )}
        </p>
      )
    case 'CustomInjectDetailsType':
      return <p>Custom inject: {details.content.raw.slice(0, 50)}</p>
  }
}

const getIcon = (channelType: SimplifiedActionLog['type']): IconName => {
  switch (channelType) {
    case 'CUSTOM_INJECT':
    case 'INJECT':
      return 'info-sign'
    case 'EMAIL':
      return 'envelope'
    case 'TOOL':
      return 'console'
    case 'FORM':
      return 'form'
    default:
      throw new Error(`unknown channel type: ${channelType}`)
  }
}

export const useNavTo = () => {
  const nav = useNavigate()
  return useCallback((actionLog: SimplifiedActionLog) => {
    switch (actionLog.details.__typename) {
      case 'InjectDetailsType':
        nav('/instructor/:exerciseId/:teamId/:channelId/info/:actionLogId', {
          params: {
            actionLogId: actionLog.id,
            channelId: actionLog.channel.id,
            exerciseId: actionLog.team.exercise.id,
            teamId: actionLog.team.id,
          },
        })
        break
      case 'CustomInjectDetailsType':
        nav('/instructor/:exerciseId/:teamId/:channelId/info/:actionLogId', {
          params: {
            actionLogId: actionLog.id,
            channelId: actionLog.channel.id,
            exerciseId: actionLog.team.exercise.id,
            teamId: actionLog.team.id,
          },
        })
        break
      case 'EmailType':
        nav('/instructor/:exerciseId/:teamId/:channelId/email/:tab/:threadId', {
          params: {
            threadId: actionLog.details.thread.id,
            channelId: actionLog.channel.id,
            exerciseId: actionLog.team.exercise.id,
            teamId: actionLog.team.id,
            tab: actionLog.requiresAttention
              ? EmailSelection.RECEIVED
              : EmailSelection.SENT,
          },
        })
        break
      case 'QuestionnaireType':
        nav('/instructor/:exerciseId/:teamId/:channelId/form/detail/:formId', {
          params: {
            formId: actionLog.details.id,
            channelId: actionLog.channel.id,
            exerciseId: actionLog.team.exercise.id,
            teamId: actionLog.team.id,
          },
        })
        break
      case 'ToolDetailsType':
        nav('/instructor/:exerciseId/:teamId/:channelId/tool/:actionLogId', {
          params: {
            actionLogId: actionLog.id,
            channelId: actionLog.channel.id,
            exerciseId: actionLog.team.exercise.id,
            teamId: actionLog.team.id,
          },
        })
        break
    }
  }, [])
}

const OverviewPillNav: React.FC<{
  actionLog: SimplifiedActionLog
}> = ({ actionLog }) => {
  const nav = useNavigate()
  return (
    <Tag
      className={css`
        margin-right: 0.25rem;
        &:hover {
          cursor: pointer;
        }
      `}
      title='Click to enter team overview'
      onClick={() => {
        nav('/instructor/:exerciseId/:teamId', {
          params: {
            teamId: actionLog.team.id,
            exerciseId: actionLog.team.exercise.id,
          },
        })
      }}
      style={{
        backgroundColor: getColorScheme(Number(actionLog.team.id)),
      }}
      round
    >
      {actionLog.team.name}
    </Tag>
  )
}

const RequiresAttention: React.FC = () => (
  <Tag
    intent='warning'
    round
    minimal
    title='This Action Log had been generated by an user and demands an appropriate reaction'
    className={css`
      margin-right: 0.25rem;
    `}
  >
    Requires attention
  </Tag>
)

const LogItemActions: React.FC<{
  actionLog: SimplifiedActionLog
}> = ({ actionLog }) => {
  const navTo = useNavTo()
  const client = useClient()
  return (
    <ButtonGroup vertical>
      <Button
        icon='changes'
        onClick={() => {
          execTodoWrite(client, actionLog)
        }}
      >
        {selectTodoCat(actionLog) ? 'Mark as undone' : 'Mark as done'}
      </Button>
      <Button icon='zoom-in' onClick={() => navTo(actionLog)}>
        Inspect
      </Button>
    </ButtonGroup>
  )
}

const LogSkeleton = forwardRef<
  HTMLDivElement,
  {
    style?: CSSProperties
  }
>(function LogSkeleton({ style }, ref) {
  return (
    <div
      ref={ref}
      className={cx(
        Classes.CALLOUT,
        Classes.CALLOUT_ICON,
        Classes.CALLOUT_HAS_BODY_CONTENT,
        css`
          display: flex;
          justify-content: space-between;
          width: 100%;
        `
      )}
      style={style}
    >
      <Icon icon={'time'} />
      <div style={{ flexGrow: 0 }}>
        <h5
          className={cx(Classes.HEADING, Classes.SKELETON)}
          style={{ width: 'max-content' }}
        >
          Loading content
        </h5>
        <Tag round minimal className={Classes.SKELETON}>
          Team Tag
        </Tag>
        <Tag
          round
          minimal
          className={Classes.SKELETON}
          style={{ marginLeft: '0.25rem' }}
        >
          Timestamp
        </Tag>

        <span
          className={Classes.SKELETON}
          style={{ display: 'inline-block', marginTop: '0.5rem' }}
        >
          Lorem ipsum dolor sit amet consectetur adipisicing elit.
        </span>
      </div>

      <div style={{ width: 'max-content' }}>
        <ButtonGroup vertical>
          <Button icon='changes' disabled>
            Mark as ...
          </Button>
          <Button icon='zoom-in' disabled>
            Inspect
          </Button>
        </ButtonGroup>
      </div>
    </div>
  )
})

const LogItemRender = forwardRef<
  HTMLDivElement,
  {
    style?: CSSProperties
    actionLog: SimplifiedActionLog
  }
>(function LogItem({ actionLog, style }, ref) {
  return (
    <div
      ref={ref}
      key={actionLog.id}
      className={cx(
        Classes.CALLOUT,
        Classes.CALLOUT_ICON,
        Classes.CALLOUT_HAS_BODY_CONTENT,
        css`
          display: flex;
          justify-content: space-between;
          width: 100%;
        `
      )}
      style={style}
    >
      <Icon icon={getIcon(actionLog.type)} />
      <div style={{ flexGrow: 0 }}>
        <h5 className={Classes.HEADING} style={{ width: 'max-content' }}>
          {getTitle(actionLog)}
        </h5>
        <OverviewPillNav actionLog={actionLog} />
        {actionLog && (
          <>
            {actionLog.requiresAttention && <RequiresAttention />}
            <Timestamp minimal datetime={new Date(actionLog.timestamp || 0)} />
          </>
        )}
        <GetContent actionLog={actionLog} />
      </div>

      <div style={{ width: 'max-content' }}>
        <LogItemActions actionLog={actionLog} />
      </div>
    </div>
  )
})

const LogItemSuspenseLoader = forwardRef<
  HTMLDivElement,
  {
    style?: CSSProperties
    actionLogId: string
  }
>(function LogItem({ actionLogId, style }, ref) {
  const [{ data }] = useTypedQuery({
    query: GetSingleActionLog,
    variables: {
      logId: actionLogId,
    },
    context: useMemo(
      () => ({
        suspense: true,
      }),
      []
    ),
  })

  if (!data) {
    return <></>
  }

  return (
    <LogItemRender
      key={actionLogId}
      actionLog={data.actionLog}
      ref={ref}
      style={style}
    />
  )
})

const LogItem = forwardRef<
  HTMLDivElement,
  {
    style?: CSSProperties
    actionLog: SimplifiedActionLog | null
    actionLogId: string
  }
>(function LogItem({ actionLog, actionLogId, style }, ref) {
  if (actionLog === null) {
    return (
      <Suspense fallback={<LogSkeleton style={style} />} key={actionLogId}>
        <LogItemSuspenseLoader
          ref={ref}
          style={style}
          actionLogId={actionLogId}
        />
      </Suspense>
    )
  } else {
    return (
      <LogItemRender
        ref={ref}
        style={style}
        actionLog={actionLog}
        key={actionLog.id}
      />
    )
  }
})

export default LogItem
