import React, { useEffect, useState } from 'react'
import useBodyScroll from '../use-body-scroll'
import useTheme from '../use-theme'
import Burger from './burger'
import { HeaderContext } from './header-context'

const Background: React.FC<
  React.PropsWithChildren<{
    openPanel?: boolean
    onClick?: React.MouseEventHandler<HTMLElement>
  }>
> = ({ children, openPanel, onClick }) => {
  const theme = useTheme()

  return (
    <div className="background" onClick={onClick}>
      {children}
      <style jsx>{`
        .background {
          position: absolute;
          left: 0;
          right: 0;
          top: 0;
          z-index: 8;
          height: 100vh;
          background-color: ${openPanel ? theme.palette.accents_10 : 'transparent'};
          opacity: 0.8;
        }
      `}</style>
    </div>
  )
}

const LogoWrapper: React.FC<React.PropsWithChildren<unknown>> = ({ children }) => {
  const theme = useTheme()

  return (
    <div className="logo-wrapper">
      {children}
      <style jsx>{`
        .logo-wrapper > :global(*:first-child) {
          line-height: 1;
          height: 30px;
          width: 44px;
          display: block;
        }
        @media (min-width: ${theme.layout.breakpointMobile}) {
          .logo-wrapper > :global(*:first-child) {
            margin-right: 32px;
          }
        }
        @media (min-width: ${theme.layout.breakpointTablet}) {
          .logo-wrapper > :global(*:first-child) {
            margin-right: 24px;
          }
        }
      `}</style>
    </div>
  )
}

const Links: React.FC<React.PropsWithChildren<{ open?: boolean }>> = ({ children, open }) => {
  const theme = useTheme()

  return (
    <div className="links">
      {children}
      <style jsx>{`
        .links {
          background-color: ${theme.palette.background};
          height: calc(100vh - 64px);
          left: 0;
          opacity: ${open === true ? 1 : 0};
          padding-bottom: 140px;
          position: fixed;
          top: 63px;
          width: 100%;
          z-index: 8;
          visibility: ${open ? 'visible' : 'hidden'};
        }

        @media (min-width: ${theme.layout.breakpointMobile}) {
          .links {
            background: transparent;
            display: flex;
            font-size: 0.925rem;
            height: auto;
            justify-content: space-between;
            line-height: 1.6em;
            opacity: 1;
            padding: 0;
            position: static;
            visibility: visible;
          }
        }
      `}</style>
    </div>
  )
}

const Nav: React.FC<React.PropsWithChildren<{ openPanel?: boolean }>> = ({
  children,
  openPanel
}) => {
  const theme = useTheme()

  return (
    <nav>
      {children}
      <style jsx>{`
        nav {
          padding: 0 ${theme.layout.gap};
          display: flex;
          align-items: center;
          flex: 1 1 100%;
          justify-content: space-between;
          background-color: ${openPanel ? theme.palette.background : 'transparent'};
          height: 64px;
          position: relative;
          z-index: 9;
        }
        nav.active {
          bottom: 0;
          left: 0;
          position: fixed;
          right: 0;
          top: 0;
        }

        @media (min-width: ${theme.layout.breakpointMobile}) {
          nav {
            width: 100%;
          }
          nav.active {
            position: relative;
          }
        }
      `}</style>
    </nav>
  )
}

interface Props {
  logo?: JSX.Element
  className?: string
}

const defaultProps = {
  className: ''
}

type NativeAttrs = Omit<React.HTMLAttributes<any>, keyof Props>
export type HeaderProps = Props & typeof defaultProps & NativeAttrs

const Header: React.FC<React.PropsWithChildren<HeaderProps>> = ({ logo, children, ...props }) => {
  const [openPanel, setOpenPanel] = useState<string | null>(null)
  const [, setHidden] = useBodyScroll(null, { scrollLayer: true })
  const isPanelOpen = openPanel !== null

  // disable body scroll on active header state
  useEffect(() => {
    setHidden(isPanelOpen)
  }, [isPanelOpen])

  const toggleSmallNav = () => {
    if (!openPanel) {
      setOpenPanel('root')
    } else {
      setOpenPanel(null)
    }
  }
  const toggleNavItem = (newPanel: any) => {
    if (!openPanel || (openPanel === 'root' && openPanel !== newPanel)) {
      setOpenPanel(newPanel)
    } else {
      setOpenPanel(openPanel === newPanel ? null : newPanel)
    }
  }

  const store = {
    openPanel: {
      get: openPanel,
      set: setOpenPanel
    },
    toggleNavItem
  }

  return (
    <header {...props}>
      <HeaderContext.Provider value={store}>
        {openPanel && <Background openPanel={isPanelOpen} onClick={() => setOpenPanel(null)} />}
        <Nav openPanel={isPanelOpen}>
          <LogoWrapper>{logo}</LogoWrapper>
          <Burger toggled={isPanelOpen} onClick={() => toggleSmallNav()} />
          <Links open={isPanelOpen}>{children}</Links>
        </Nav>
      </HeaderContext.Provider>
      <style jsx>{`
        header {
          box-sizing: border-box;
        }
      `}</style>
    </header>
  )
}

type MemoPageComponent<P> = React.NamedExoticComponent<P> & {
  Context: typeof HeaderContext
}
type ComponentProps = Partial<typeof defaultProps> &
  Omit<Props, keyof typeof defaultProps> &
  NativeAttrs

Header.defaultProps = defaultProps

export default React.memo(Header) as MemoPageComponent<ComponentProps>
