import { getTitle } from '@/actionlog/InjectMessage/utils'
import { EmailSelection } from '@/analyst/utilities'
import { useNavigate } from '@/router'
import type { BackLoc } from '@/views/InstructorView/useLeftNavbar'
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} template{count > 1 && 's'}
      </Tag>
    )
  )
}

const GetContent: FC<{
  actionLog: SimplifiedActionLog
  contextType: 'exercise' | 'team'
}> = ({ actionLog, contextType }) => {
  const { details } = actionLog
  const nav = useNavigate()

  switch (details.__typename) {
    case 'InjectDetailsType':
      return <p>New inject: {details.inject.name}</p>
    case 'EmailType': {
      const emailList = details.thread.participants
        .filter(({ address }) => address !== details.sender.address)
        .map(({ address }) => address)
      return (
        <>
          <p>
            Message from: <b>{details.sender.address}</b>
            <br />
            Message to: <b>{emailList.join(', ')}</b> in thread{' '}
            <Tag
              className={css`
                &:hover {
                  cursor: pointer;
                }
              `}
              title='Click to enter the thread'
              onClick={() => {
                nav('/instructor/:exerciseId/:teamId/email/:tab/:threadId', {
                  params: {
                    threadId: details.thread.id,
                    exerciseId: actionLog.team.exercise.id,
                    teamId: actionLog.team.id,
                    tab: EmailSelection.SENT,
                  },
                  state: {
                    autoRedirect: true,
                    backloc: constructBackUrl(actionLog, contextType),
                  } as BackLoc,
                })
              }}
              round
              intent='primary'
            >
              {details.thread.subject}
            </Tag>
            <GetTemplateCount threadId={details.thread.id} />
          </p>
        </>
      )
    }
    case 'TeamQuestionnaireStateType':
      return <p>{details.questionnaire.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}`)
  }
}

const constructBackUrl = (
  actionLog: SimplifiedActionLog,
  contextType: 'exercise' | 'team' | 'none'
) =>
  contextType !== 'none'
    ? {
        button: {
          text: 'Back to Overview',
          minimal: true,
          icon: 'double-chevron-left',
        },
        end: true,
        link:
          contextType === 'exercise'
            ? [
                '/instructor/:exerciseId/',
                {
                  params: {
                    exerciseId: actionLog.team.exercise.id,
                  },
                },
              ]
            : [
                '/instructor/:exerciseId/:teamId',
                {
                  params: {
                    exerciseId: actionLog.team.exercise.id,
                    teamId: actionLog.team.id,
                  },
                },
              ],
      }
    : undefined

export const useNavTo = (contextType: 'exercise' | 'team' | 'none') => {
  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,
          },
          state: {
            backloc: constructBackUrl(actionLog, contextType),
          } as BackLoc,
        })
        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,
          },
          state: {
            backloc: constructBackUrl(actionLog, contextType),
          } as BackLoc,
        })
        break
      case 'EmailType':
        nav('/instructor/:exerciseId/:teamId/email/:tab/:threadId', {
          params: {
            threadId: actionLog.details.thread.id,
            exerciseId: actionLog.team.exercise.id,
            teamId: actionLog.team.id,
            tab: EmailSelection.SENT,
          },
          state: {
            autoRedirect: true,
            backloc: constructBackUrl(actionLog, contextType),
          } as BackLoc,
        })
        break
      case 'TeamQuestionnaireStateType':
        nav('/instructor/:exerciseId/:teamId/:channelId/form/:actionLogId', {
          params: {
            actionLogId: actionLog.id,
            channelId: actionLog.channel.id,
            exerciseId: actionLog.team.exercise.id,
            teamId: actionLog.team.id,
          },
          state: {
            backloc: constructBackUrl(actionLog, contextType),
          } as BackLoc,
        })
        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,
          },
          state: {
            backloc: constructBackUrl(actionLog, contextType),
          } as BackLoc,
        })
        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
  contextType: 'exercise' | 'team'
}> = ({ actionLog, contextType }) => {
  const navTo = useNavTo(contextType)
  const client = useClient()
  const formDisabled =
    actionLog.details.__typename === 'TeamQuestionnaireStateType' &&
    actionLog.details.status === 'REVIEWED'
  return (
    <ButtonGroup vertical>
      <Button
        icon='changes'
        disabled={formDisabled}
        title={
          formDisabled ? 'This questionnaire had been reviewed' : undefined
        }
        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
    contextType: 'exercise' | 'team'
  }
>(function LogItem({ actionLog, contextType, 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} contextType={contextType} />
      </div>

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

const LogItemSuspenseLoader = forwardRef<
  HTMLDivElement,
  {
    style?: CSSProperties
    actionLogId: string
    contextType: 'exercise' | 'team'
  }
>(function LogItem({ actionLogId, contextType, 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}
      contextType={contextType}
      style={style}
    />
  )
})

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

export default LogItem
