import {
  Badge,
  Box,
  Button,
  DialogTitle,
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuGroup,
  DropdownMenuItem,
  DropdownMenuPortal,
  DropdownMenuTrigger,
  elevationVariants,
  Flex,
  Link,
  NavigationLink,
  SidePanel,
  styled,
  Text,
  Tooltip,
  VisuallyHidden,
} from '@traefiklabs/faency'
import { useContext, useEffect, useMemo, useState } from 'react'
import { BsChevronDoubleRight, BsChevronDoubleLeft } from 'react-icons/bs'
import { FiBookOpen, FiGithub, FiHelpCircle } from 'react-icons/fi'
import { matchPath, useHref } from 'react-router'
import { useLocation } from 'react-router-dom'
import { useWindowSize } from 'usehooks-ts'

import Container from './Container'
import { DARK_PRIMARY_COLOR, LIGHT_PRIMARY_COLOR } from './Page'

import IconButton from 'components/buttons/IconButton'
import Logo from 'components/icons/Logo'
import { PluginsIcon } from 'components/icons/PluginsIcon'
import ThemeSwitcher from 'components/ThemeSwitcher'
import TooltipText from 'components/TooltipText'
import { VersionContext } from 'contexts/version'
import useTotals from 'hooks/use-overview-totals'
import { useIsDarkMode } from 'hooks/use-theme'
import { Route, ROUTES } from 'routes'

export const LAPTOP_BP = 1025

const NavigationDrawer = styled(Flex, {
  width: '100%',
  maxWidth: '100%',
  height: 64,
  p: 0,
  variants: {
    elevation: elevationVariants,
  },
  defaultVariants: {
    elevation: 1,
  },
})

const BasicNavigationItem = ({
  route,
  count,
  isSmallScreen,
  isExpanded,
}: {
  route: Route
  count?: number
  isSmallScreen: boolean
  isExpanded: boolean
}) => {
  const { pathname } = useLocation()
  const href = useHref(route.path)

  const isActiveRoute = useMemo(() => {
    const mainPath = matchPath(route.path, pathname)

    if (mainPath) return true

    if (route.activeMatches) {
      return route.activeMatches.some((path) => matchPath(path, pathname))
    }
  }, [pathname, route.activeMatches, route.path])

  if (isSmallScreen && !isExpanded) {
    return (
      <Tooltip content={<Text css={{ color: '$tooltipText' }}>{route.label}</Text>} side="right">
        <Box>
          <NavigationLink active={isActiveRoute} startAdornment={route?.icon} href={href} />
        </Box>
      </Tooltip>
    )
  }

  return (
    <NavigationLink active={isActiveRoute} startAdornment={route?.icon} css={{ whiteSpace: 'nowrap' }} href={href}>
      {route.label}
      {!!count && (
        <Badge variant={isActiveRoute ? 'green' : undefined} css={{ ml: '$2' }}>
          {count}
        </Badge>
      )}
    </NavigationLink>
  )
}

export const SideBarPanel = ({
  isOpen,
  onOpenChange,
}: {
  isOpen: boolean
  onOpenChange: (isOpen: boolean) => void
}) => {
  const windowSize = useWindowSize()

  return (
    <SidePanel
      open={isOpen && windowSize.width < LAPTOP_BP}
      onOpenChange={onOpenChange}
      side="left"
      css={{ width: 264, p: 0 }}
      description="Expanded side navigation"
      noCloseIcon
    >
      <VisuallyHidden>
        <DialogTitle>side navigation</DialogTitle>
      </VisuallyHidden>
      <SideNav isExpanded={isOpen} onSidePanelToggle={() => onOpenChange(false)} />
    </SidePanel>
  )
}

export const SideNav = ({
  isExpanded,
  onSidePanelToggle,
  isResponsive = false,
}: {
  isExpanded: boolean
  onSidePanelToggle: () => void
  isResponsive?: boolean
}) => {
  const windowSize = useWindowSize()
  const { version } = useContext(VersionContext)

  const { http, tcp, udp } = useTotals()

  const [isSmallScreen, setIsSmallScreen] = useState(false)

  useEffect(() => {
    setIsSmallScreen(isResponsive && windowSize.width < LAPTOP_BP)
  }, [isExpanded, isResponsive, windowSize.width])

  const totalValueByPath = useMemo<{ [key: string]: number }>(
    () => ({
      '/http/routers': http?.routers,
      '/http/services': http?.services,
      '/http/middlewares': http?.middlewares as number,
      '/tcp/routers': tcp?.routers,
      '/tcp/services': tcp?.services,
      '/tcp/middlewares': tcp?.middlewares as number,
      '/udp/routers': udp?.routers,
      '/udp/services': udp?.services,
    }),
    [http, tcp, udp],
  )

  return (
    <NavigationDrawer
      data-collapsed={isExpanded && isResponsive && isSmallScreen}
      css={{
        width: 264,
        height: '100vh',
        position: 'fixed',
        [`@media (max-width:${LAPTOP_BP}px)`]: isResponsive
          ? {
              width: 64,
              'a > div:nth-child(1)': {
                marginLeft: 0,
                paddingRight: 0,
              },
            }
          : undefined,
        transition: '150ms cubic-bezier(0.22, 1, 0.36, 1)',
        '&[data-collapsed="true"]': {
          marginLeft: -32,
        },
      }}
    >
      <IconButton
        ghost
        icon={isExpanded ? <BsChevronDoubleLeft size={16} /> : <BsChevronDoubleRight size={16} />}
        onClick={onSidePanelToggle}
        css={{
          display: 'none',
          position: 'absolute',
          top: 3,
          right: isExpanded ? 12 : 4,
          color: '$hiContrast',
          [`@media (max-width:${LAPTOP_BP}px)`]: { display: 'inherit' },
          p: '$1',
          '&:before, &:after': { borderRadius: '10px' },
          height: 16,
        }}
      />
      <Container
        css={{
          overflow: 'auto',
          p: '$3',
          m: 0,
          flexDirection: 'column',
          [`@media (max-width:${LAPTOP_BP}px)`]: isResponsive ? { p: '$2' } : undefined,
        }}
        data-testid="nav-container"
      >
        <Flex
          as="a"
          gap={2}
          css={{
            color: '$primary',
            mt: '$3',
            mb: '$6',
            textDecoration: 'none',
            height: 'fit-content',
            pl: '$3',
            [`@media (max-width:${LAPTOP_BP}px)`]: isResponsive
              ? { mt: '$4', px: 0, justifyContent: 'center' }
              : undefined,
          }}
          href="https://github.com/traefik/traefik/"
          target="_blank"
          data-testid="proxy-main-nav"
        >
          <Logo height={isSmallScreen ? 36 : 56} isSmallScreen={isSmallScreen} />
          {!!version && !isSmallScreen && (
            <TooltipText text={version} css={{ maxWidth: 50, fontWeight: '$semiBold' }} isTruncated />
          )}
        </Flex>
        {ROUTES.map((section, index) => (
          <Flex
            key={`nav-section-${index}`}
            direction="column"
            gap="1"
            css={{
              '&:not(:last-child)': {
                borderTop: section.sectionLabel ? '1px solid $colors$tableRowBorder' : undefined,
                mb: '$3',
                pt: section.sectionLabel ? '$3' : undefined,
              },
            }}
          >
            {section.sectionLabel && (
              <Text
                css={{
                  fontWeight: 600,
                  color: '$grayBlue9',
                  mb: '$2',
                  textTransform: 'uppercase',
                  letterSpacing: 0.2,
                  ml: 15,
                  [`@media (max-width:${LAPTOP_BP}px)`]: isResponsive ? { display: 'none' } : undefined,
                }}
              >
                {section.sectionLabel}
              </Text>
            )}
            {section.items.map((item, idx) => (
              <BasicNavigationItem
                key={`nav-section-${index}-${idx}`}
                route={item}
                count={totalValueByPath[item.path]}
                isSmallScreen={isSmallScreen}
                isExpanded={isExpanded}
              />
            ))}
          </Flex>
        ))}
        <Flex direction="column" css={{ borderTop: '1px solid $colors$tableRowBorder', borderRadius: 0, pt: '$3' }}>
          <NavigationLink
            startAdornment={<PluginsIcon />}
            css={{
              mt: '$3',
              whiteSpace: 'nowrap',
            }}
            href="https://plugins.traefik.io/"
            target="_blank"
          >
            {!isSmallScreen || isExpanded ? 'Plugins' : ''}
          </NavigationLink>
        </Flex>
      </Container>
    </NavigationDrawer>
  )
}

export const TopNav = () => {
  const [hasHubButtonComponent, setHasHubButtonComponent] = useState(false)
  const { showHubButton, version } = useContext(VersionContext)
  const isDarkMode = useIsDarkMode()

  const parsedVersion = useMemo(() => {
    if (!version) {
      return 'master'
    }
    if (version === 'dev') {
      return 'master'
    }
    const matches = version.match(/^(v?\d+\.\d+)/)
    return matches ? 'v' + matches[1] : 'master'
  }, [version])

  useEffect(() => {
    if (!showHubButton) {
      setHasHubButtonComponent(false)
      return
    }

    if (customElements.get('hub-button-app')) {
      setHasHubButtonComponent(true)
      return
    }

    const scripts: HTMLScriptElement[] = []
    const createScript = (scriptSrc: string): HTMLScriptElement => {
      const script = document.createElement('script')
      script.src = scriptSrc
      script.async = true
      script.onload = () => {
        setHasHubButtonComponent(customElements.get('hub-button-app') !== undefined)
      }
      scripts.push(script)
      return script
    }

    // Source: https://github.com/traefik/traefiklabs-hub-button-app
    document.head.appendChild(createScript('traefiklabs-hub-button-app/main-v1.js'))

    return () => {
      // Remove the scripts on unmount.
      scripts.forEach((script) => {
        if (script.parentNode) {
          script.parentNode.removeChild(script)
        }
      })
    }
  }, [showHubButton])

  return (
    <Flex as="nav" role="navigation" justify="end" align="center" css={{ gap: '$2', mb: '$6' }}>
      {hasHubButtonComponent && (
        <Box css={{ fontFamily: '$rubik', fontWeight: '500 !important' }}>
          <hub-button-app
            key={`dark-mode-${isDarkMode}`}
            style={{ backgroundColor: isDarkMode ? DARK_PRIMARY_COLOR : LIGHT_PRIMARY_COLOR, fontWeight: 'inherit' }}
          />
        </Box>
      )}
      <ThemeSwitcher />

      <DropdownMenu>
        <DropdownMenuTrigger asChild>
          <Button ghost variant="secondary" css={{ px: '$2', boxShadow: 'none' }} data-testid="help-menu">
            <FiHelpCircle size={20} />
          </Button>
        </DropdownMenuTrigger>
        <DropdownMenuPortal>
          <DropdownMenuContent align="end" css={{ zIndex: 9999 }}>
            <DropdownMenuGroup>
              <DropdownMenuItem css={{ height: '$6', cursor: 'pointer' }}>
                <Link
                  href={`https://doc.traefik.io/traefik/${parsedVersion}`}
                  target="_blank"
                  css={{ textDecoration: 'none', '&:hover': { textDecoration: 'none' } }}
                >
                  <Flex align="center" gap={2}>
                    <FiBookOpen size={20} />
                    <Text>Documentation</Text>
                  </Flex>
                </Link>
              </DropdownMenuItem>
              <DropdownMenuItem css={{ height: '$6', cursor: 'pointer' }}>
                <Link
                  href="https://github.com/traefik/traefik/"
                  target="_blank"
                  css={{ textDecoration: 'none', '&:hover': { textDecoration: 'none' } }}
                >
                  <Flex align="center" gap={2}>
                    <FiGithub size={20} />
                    <Text>Github Repository</Text>
                  </Flex>
                </Link>
              </DropdownMenuItem>
            </DropdownMenuGroup>
          </DropdownMenuContent>
        </DropdownMenuPortal>
      </DropdownMenu>
    </Flex>
  )
}
