import { useEffect, useMemo, useRef, useState } from 'preact/hooks'
import { Link } from '../atoms/link.js'
import { Logo } from '../atoms/logo.js'
import { Menu } from '../atoms/menu.js'
import { NavLink } from '../atoms/nav-link.js'
import { Tree } from '../molecules/tree.js'
import getId from '../shared/get-id.js'
import h from '../shared/h.js'
import { NAVIGATION_BURGER, NAVIGATION_CLOSE } from '../shared/icons.js'
import markSelection from '../shared/mark-selection.js'
import splitLinks from '../shared/split-links.js'
import t from '../shared/t.js'

//
// Main navigation.
//

export function Navigation ({ _model, ...props }) {
  const [homeLink, mainLinks, toolLinks] = useMemo(() => getLinks(_model), [_model])
  markSelection(mainLinks)

  return h('div', {
    ...props,
    className: 'w-full relative border-txa/20 border-b text-txa bg-bga',
    children: h(NavigationMain, { title: _model.title, homeLink, mainLinks, toolLinks })
  })
}

function NavigationMain ({ title, homeLink, mainLinks, toolLinks }) {
  // Make sure only one link is expanded at once.
  const [expandedIndex, setExpandedIndex] = useState()
  const [getIndexedId] = useIndexedIds()
  const { isBurgerEmpty, closeBurger, ...burgerProps } = useBurger(mainLinks)

  // Temporary fix to force close the menu on route change if the instance is the same.
  useEffect(() => {
    const onRoute = () => {
      setExpandedIndex(null)
      closeBurger()
    }
    globalThis.addEventListener('route', onRoute)
    return () => globalThis.removeEventListener('route', onRoute)
  }, [])

  // Close on escape.
  const mainLinksWrapperProps = {
    className: 'flex',
    role: 'list',
    onKeyUp: event => event.key === 'Escape' && setExpandedIndex(null)
  }

  return h('nav', { className: 'max-w-page m-auto' },

    // Main links.
    h('div', { className: 'flex items-center' },
      // Logo.
      h(Link, {
        'aria-label': homeLink.value && homeLink.name,
        _model: { value: homeLink.value },
        children: h('div', { className: 'min-h-16 border-txa flex' },
          h('div', { className: 'flex shrink-0 grow-0' },
            h(Logo)
          ),
          title && h('div', { className: 'grow flex items-center tracking-tight font-bold max-w-52 pr-9 ' }, title)
        )
      }),

      // Main links - close on escape.
      mainLinks.length > 0 && h('div', mainLinksWrapperProps, mainLinks.map((link, index) => {
        const isExpanded = expandedIndex === index
        const hasMenu = Boolean(link.children)
        const menuId = (hasMenu && getIndexedId(index)) || undefined

        // Menu should be shown in the right variant when open,
        // and when closed it is just an empty div (so that id exists).
        let menu
        if (isExpanded) {
          menu = h(Menu, {
            id: menuId,
            children: h(Tree, {
              _model: {
                links: [{ name: t('Go to: {0}', link.name), value: link.value }, ...link.children ?? []]
              }
            })
          })
        } else {
          // Empty div is necessary for the aria-controls reference.
          menu = h('div', { id: menuId, className: 'hidden' })
        }

        return h('div', { className: 'shrink sm:hidden md:hidden tracking-tight relative', role: 'listitem' }, hasMenu
          ? [
              h(NavLink, {
                _selection: 'bottom',
                _size: 'lg',
                _highlighted: isExpanded,
                _expanded: false,
                _model: { ...link },
                role: 'button',
                'aria-expanded': isExpanded,
                'aria-controls': menuId,
                ...getAriaCurrentPage(link),
                onClick: event => {
                  event.preventDefault()
                  setExpandedIndex(expandedIndex === index ? null : index)
                }
              }),
              // Show menu.
              menu
            ]
          : h(NavLink, { _selection: 'bottom', _size: 'lg', _model: link, ...getAriaCurrentPage(link) })
        )
      })),

      // Spacer.
      h('div', { className: 'grow' }),

      // Tool links.
      toolLinks.length > 0 && h('div', { role: 'list', className: 'flex shrink' }, toolLinks.map(link =>
        h('div', { role: 'listitem' },
          h(NavLink, { _size: 'lg', _textless: true, _model: link })
        )
      )),

      // Burger button (mobile and tablet).
      !isBurgerEmpty && h('div', { className: 'lg:hidden' },
        h(BurgerMenuButton, burgerProps)
      )
    ),

    // Burger menu.
    h(BurgerMenu, burgerProps)
  )
}

// Helper indicating current page attribute.
function getAriaCurrentPage (link) {
  return link.selected && globalThis.location.href.includes(link.value) ? { 'aria-current': 'page' } : {}
}

//
// Menu handling.
//

// Helper hook: index-based generated element ids.
function useIndexedIds () {
  const indexedIds = useRef([])
  const getIndexedId = (index = 0) => {
    if (!indexedIds.current[index]) {
      indexedIds.current[index] = getId()
    }
    return indexedIds.current[index]
  }

  return [getIndexedId, indexedIds.current]
}

//
// Burger menu.
//

function useBurger (mainLinks) {
  const [isExpanded, setIsExpanded] = useState(false)
  const ids = useRef({ ownerId: getId(), ownedId: getId() })
  const isBurgerEmpty = useMemo(() => mainLinks.length === 0, [mainLinks])
  const escapeHandler = event => {
    if (event.key.startsWith('Esc')) {
      setIsExpanded(false)
      document.getElementById(ids.current.ownerId).focus()
    }
  }

  return {
    mainLinks,
    isExpanded,
    setIsExpanded,
    ids,
    isBurgerEmpty,
    closeBurger: () => setIsExpanded(false),
    escapeHandler
  }
}

export function BurgerMenuButton ({ isExpanded, setIsExpanded, ids, escapeHandler }) {
  return h(NavLink, {
    onClick: event => {
      setIsExpanded(!isExpanded)
      event.preventDefault()
    },
    onKeyUp: escapeHandler,
    id: ids.current.ownerId,
    'aria-controls': ids.current.ownedId,
    'aria-expanded': isExpanded.toString(),
    'aria-haspopup': true,
    _highlighted: isExpanded,
    _model: { name: 'Menu', icon: isExpanded ? NAVIGATION_CLOSE : NAVIGATION_BURGER },
    _textless: true,
    _size: 'lg'
  })
}

function BurgerMenu ({ mainLinks, isExpanded, ids, escapeHandler }) {
  const menuProps = {
    className: 'animate-expand bg-bga lg:hidden border-txa/20 border-t',
    id: ids.current.ownedId,
    'aria-labelledby': ids.current.ownerId,
    onKeyUp: escapeHandler
  }

  return isExpanded && h('div', menuProps,
    h(Tree, { _model: { links: mainLinks } })
  )
}

//
// Helpers.
//

function getLinks (_model) {
  const { links } = _model
  const nestedLinks = splitLinks(links)

  // Ensure length.
  while (nestedLinks.length < 4) {
    nestedLinks.push([])
  }

  const [homeLinks, mainLinks, toolLinks] = nestedLinks
  const homeLink = homeLinks.pop() || {}

  return [homeLink, mainLinks, toolLinks]
}
