import InjectMessage from '@/actionlog/InjectMessage'
import type { NonIdealStateProps } from '@blueprintjs/core'
import { Button, NonIdealState } from '@blueprintjs/core'
import { css } from '@emotion/css'
import { useGetTeamChannelLogsSuspenseQuery } from '@inject/graphql/queries/GetTeamChannelLogs.generated'
import useInInstructor from '@inject/shared/hooks/useInInstructor'
import notEmpty from '@inject/shared/utils/notEmpty'
import {
  memo,
  useEffect,
  useMemo,
  useRef,
  type FC,
  type MouseEventHandler,
} from 'react'

const view = css`
  height: 100%;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
`

const bottom = css`
  display: flex;
  justify-content: flex-end;
`

interface ActionLogProps {
  teamId: string
  exerciseId: string
  channelId: string
  getOnInspect: (actionLogId: string) => MouseEventHandler
  noDataProps?: NonIdealStateProps
}

/**
 * The component expects that the subscription is active for the given teamId
 */
const ActionLog: FC<ActionLogProps> = ({
  teamId,
  exerciseId,
  channelId,
  getOnInspect,
  noDataProps,
}) => {
  const inInstructor = useInInstructor()

  const { data } = useGetTeamChannelLogsSuspenseQuery({
    variables: { teamId, channelId },
    fetchPolicy: 'cache-first',
  })
  const actionLogs = (data.teamChannelLogs || [])?.filter(notEmpty)

  const unreadLogsStartRef = useRef<HTMLDivElement | null>(null)
  const bottomRef = useRef<HTMLDivElement | null>(null)

  /**
   * this prevents the change of actionLogs from triggering a
   * re-calculation of the firstUnreadIndex
   *
   * actionLog.readReceipt will update as soon as the InjectMessage is
   * rendered, but the actionLog needs to still be rendered as unread
   */
  const firstUnreadIndex = useMemo(() => {
    const index = actionLogs.findIndex(actionLog => !actionLog.readReceipt)
    return index === -1 ? actionLogs.length : index
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [])
  // TODO: we should change it to a proper look-ahead algorithm and use actionLog's ID for indicating the read state, or use something better

  useEffect(() => {
    if (bottomRef.current) {
      bottomRef.current.scrollIntoView({ behavior: 'instant' })
    }
  }, [])
  useEffect(() => {
    if (bottomRef.current) {
      bottomRef.current.scrollIntoView({ behavior: 'smooth' })
    }
  }, [actionLogs])
  const handleTopOfUnread = () => {
    if (unreadLogsStartRef.current) {
      unreadLogsStartRef.current.scrollIntoView({ behavior: 'smooth' })
    }
  }

  return (
    <div className={view}>
      {actionLogs.length > 0 && (
        <>
          <div>
            {actionLogs.map((actionLog, index) => (
              <div key={actionLog.id}>
                {index === firstUnreadIndex && <div ref={unreadLogsStartRef} />}
                {
                  <InjectMessage
                    key={actionLog.id}
                    exerciseId={exerciseId}
                    teamId={teamId}
                    actionLog={actionLog}
                    onInspect={getOnInspect(actionLog.id)}
                    inInstructor={inInstructor}
                  />
                }
              </div>
            ))}
          </div>

          <div ref={bottomRef} className={bottom}>
            <Button
              icon='arrow-up'
              minimal
              onClick={handleTopOfUnread}
              disabled={firstUnreadIndex === actionLogs.length}
            >
              See all unread
            </Button>
          </div>
        </>
      )}
      {actionLogs.length === 0 && (
        <NonIdealState
          icon='low-voltage-pole'
          title='No injects found'
          description='There are no injects in this channel yet'
          {...noDataProps}
        />
      )}
    </div>
  )
}

export default memo(ActionLog)
