import { useHideSidebarValue } from '@/clientsettings/vars/hidesidebar'
import ExitButton from '@/components/ExitButton'
import type { LinkType } from '@/components/LinkButton'
import LinkButton from '@/components/LinkButton'
import MoveTimeButton from '@/components/MoveTimeButton'
import type { Section } from '@/components/Sidebar'
import Sidebar from '@/components/Sidebar'
import HideButton from '@/components/Sidebar/HideSidebarButton'
import Status from '@/components/Status'
import { sidebarClass, sidebarClassCompact } from '@/views/classes'
import type { IconName } from '@blueprintjs/core'
import { css, cx } from '@emotion/css'
import useChannelTypeEnabled from '@inject/graphql/utils/useChannelTypeEnabled'
import CenteredSpinner from '@inject/shared/components/CenteredSpinner'
import type { FC, PropsWithChildren } from 'react'
import { Suspense, useContext, useMemo } from 'react'
import { generatePath, matchPath, useLocation } from 'react-router-dom'
import { useParams } from '../../router'
import ExerciseContext from '../ExerciseContext'
import ExerciseSelector from '../ExerciseSelector'
import SVGContext from '../SVGContext'
import TimeSwitch from './TimeSwitch'

const limitedHeight = css`
  overflow-y: auto;
`

type PathType = {
  link: LinkType
  icon: IconName
  text: string
}

interface NavigationBarProps extends PropsWithChildren {
  selectorOpenByDefault?: boolean
}

const NavigationBar: FC<NavigationBarProps> = ({
  children,
  selectorOpenByDefault,
}) => {
  const { pathname } = useLocation()
  const { exerciseId } = useParams('/analyst/:exerciseId')

  const emailsEnabled = useChannelTypeEnabled('EMAIL', {
    variables: { exerciseId },
    pause: !exerciseId,
  })
  const toolsEnabled = useChannelTypeEnabled('TOOL', {
    variables: { exerciseId },
    pause: !exerciseId,
  })
  const questionnairesEnabled = useChannelTypeEnabled('FORM', {
    variables: { exerciseId },
    pause: !exerciseId,
  })
  const hide = useHideSidebarValue()

  const { exercise } = useContext(ExerciseContext)

  const paths: PathType[] = useMemo(() => {
    const paths: PathType[] = []
    paths.push({
      link: ['/analyst/:exerciseId', { params: { exerciseId } }],
      icon: 'control',
      text: 'Dashboard',
    })
    paths.push({
      link: ['/analyst/:exerciseId/milestones', { params: { exerciseId } }],
      icon: 'flag',
      text: 'Milestones',
    })
    paths.push({
      link: [
        '/analyst/:exerciseId/learning-objectives',
        { params: { exerciseId } },
      ],
      icon: 'learning',
      text: 'Learning objectives',
    })
    if (emailsEnabled) {
      paths.push({
        link: ['/analyst/:exerciseId/emails', { params: { exerciseId } }],
        icon: 'envelope',
        text: 'Emails',
      })
    }
    if (toolsEnabled) {
      paths.push({
        link: ['/analyst/:exerciseId/tools', { params: { exerciseId } }],
        icon: 'console',
        text: 'Tools',
      })
    }
    if (questionnairesEnabled) {
      paths.push({
        link: [
          '/analyst/:exerciseId/questionnaires',
          { params: { exerciseId } },
        ],
        icon: 'form',
        text: 'Questionnaires',
      })
    }
    return paths
  }, [emailsEnabled, exerciseId, questionnairesEnabled, toolsEnabled])

  const stickySections: Section[] = useMemo(
    () => [
      {
        id: 'status',
        node: <Status key='header' small={hide} showTime hideLabel={hide} />,
      },
    ],
    [hide]
  )
  const sections: Section[] = useMemo(
    () => [
      {
        id: 'options',
        name: 'Options',
        node: (
          <>
            <HideButton />
            <ExerciseSelector
              className={limitedHeight}
              buttonProps={{
                icon: 'open-application',
                alignText: 'left',
                fill: true,
                minimal: true,
                text: !hide && 'Select an exercise',
                title: 'Select an exercise',
              }}
              openByDefault={selectorOpenByDefault}
            />
            {/* if the exercise hasn't started yet, the time should be shown relative to the start of time */}
            {exerciseId && exercise.exerciseStart && (
              <TimeSwitch hideLabel={hide} />
            )}
            <MoveTimeButton
              hideLabel={hide}
              exerciseId={exerciseId}
              disabledTitle='The exercise has to be running to move time'
            />
            <ExitButton hideLabel={hide} />
          </>
        ),
      },

      ...(exerciseId
        ? [
            {
              id: 'pages',
              name: 'Pages',
              node: (
                <>
                  {paths.map(path => (
                    <LinkButton
                      key={path.text}
                      link={path.link}
                      button={{
                        fill: true,
                        minimal: true,
                        alignText: 'left',
                        icon: path.icon,
                        text: !hide && path.text,
                        active:
                          matchPath(
                            {
                              path: generatePath(
                                path.link[0].toString(),
                                path.link[1]?.params
                              ),
                              // allow subpaths for all but the Dashboard page
                              end: path.text === 'Dashboard',
                            },
                            pathname
                          ) !== null,
                      }}
                    />
                  ))}
                </>
              ),
            },
          ]
        : []),
    ],
    [
      exercise.exerciseStart,
      exerciseId,
      hide,
      pathname,
      paths,
      selectorOpenByDefault,
    ]
  )

  const SVGContextValue = useMemo(() => ({ dependencies: [hide] }), [hide])

  return (
    <div style={{ height: '100%', display: 'flex' }}>
      <Sidebar
        className={cx({
          [sidebarClass]: !hide,
          [sidebarClassCompact]: hide,
        })}
        position='left'
        stickySections={stickySections}
        sections={sections}
        hideNames={hide}
        showLogo
      />

      <div
        style={{
          overflowX: 'hidden',
          overflowY: 'auto',
          flex: 1,
          padding: '0.5rem',
        }}
      >
        {exerciseId ? (
          <SVGContext.Provider value={SVGContextValue}>
            <Suspense fallback={<CenteredSpinner />}>{children}</Suspense>
          </SVGContext.Provider>
        ) : (
          children
        )}
      </div>
    </div>
  )
}

export default NavigationBar
