import { useQuery } from '@tanstack/react-query'
import { AnimatePresence, motion } from 'framer-motion'
import { Fragment, useEffect, useLayoutEffect, useRef, useState } from 'react'
import { useLocation } from 'react-router-dom'

import Separator from '@/components/MenuSeparator'
import PrefetchLink from '@/components/PrefetchLink'
import TopNavUser from '@/components/TopNavUser'
import { WikiIdBus } from '@/lib/eventBus/WikiIdBus'
import { tagsKeys, wikiKeys } from '@/lib/queryKeys'
import { fetchTagByName } from '@/lib/tags'
import { cn } from '@/lib/utils'
import { fetchWikiPage } from '@/lib/wiki'

import type { FC, MouseEvent } from 'react'

import type { Menu, User } from '@/types'

type Props = { user: User | null }


export default (({ user }: Props) => {
  const location = useLocation ()

  const dirRef = useRef<(-1) | 1> (1)
  const itemsRef = useRef<(HTMLAnchorElement | null)[]> ([])
  const navRef = useRef<HTMLDivElement | null> (null)

  const measure = () => {
    const nav = navRef.current
    const el = itemsRef.current[activeIdx]
    if (!(nav) || !(el) || activeIdx < 0)
      return

    const navRect = nav.getBoundingClientRect ()
    const elRect = el.getBoundingClientRect ()

    setHl ({ left:    elRect.left - navRect.left,
	     width:   elRect.width,
	     visible: true })
  }

  const [hl, setHl] = useState<{ left: number; width: number; visible: boolean }> ({
      left:     0,
      width:    0,
      visible:  false })
  const [menuOpen, setMenuOpen] = useState (false)
  const [openItemIdx, setOpenItemIdx] = useState (-1)
  const [wikiId, setWikiId] = useState<number | null> (WikiIdBus.get ())

  const wikiIdStr = String (wikiId ?? '')

  const { data: wikiPage } = useQuery ({
      enabled: Boolean (wikiIdStr),
      queryKey: wikiKeys.show (wikiIdStr, { }),
      queryFn: () => fetchWikiPage (wikiIdStr, { }) })

  const effectiveTitle = wikiPage?.title ?? ''

  const { data: tag } = useQuery ({
      enabled: Boolean (effectiveTitle),
      queryKey: tagsKeys.show (effectiveTitle),
      queryFn: () => fetchTagByName (effectiveTitle) })

  const postCount = tag?.postCount ?? 0

  const wikiPageFlg = Boolean (/^\/wiki\/(?!new|changes)[^\/]+/.test (location.pathname) && wikiId)
  const wikiTitle = location.pathname.split ('/')[2] ?? ''
  const menu: Menu = [
      { name: '広場', to: '/posts', subMenu: [
	  { name: '一覧', to: '/posts' },
	  { name: '検索', to: '/posts/search' },
	  { name: '投稿追加', to: '/posts/new' },
	  { name: '履歴', to: '/posts/changes' },
	  { name: 'ヘルプ', to: '/wiki/ヘルプ:広場' }] },
      { name: 'タグ', to: '/tags', subMenu: [
	  { name: 'タグ一覧', to: '/tags', visible: true },
	  { name: '別名タグ', to: '/tags/aliases', visible: false },
	  { name: '上位タグ', to: '/tags/implications', visible: false },
	  { name: 'ニコニコ連携', to: '/tags/nico' },
	  { name: 'ヘルプ', to: '/wiki/ヘルプ:タグ' }] },
      // TODO: 本実装時に消す．
      // { name: '上映会', to: '/theatres/1', base: '/theatres', subMenu: [
      //     { name: '一覧', to: '/theatres' }] },
      { name: 'Wiki', to: '/wiki/ヘルプ:ホーム', base: '/wiki', subMenu: [
	  { name: '検索', to: '/wiki' },
	  { name: '新規', to: '/wiki/new' },
	  { name: '全体履歴', to: '/wiki/changes' },
	  { name: 'ヘルプ', to: '/wiki/ヘルプ:Wiki' },
	  { component: <Separator/>, visible: wikiPageFlg },
	  { name: `広場 (${ postCount || 0 })`, to: `/posts?tags=${ wikiTitle }`,
	    visible: wikiPageFlg },
	  { name: '履歴', to: `/wiki/changes?id=${ wikiId }`, visible: wikiPageFlg },
	  { name: '編輯', to: `/wiki/${ wikiId || wikiTitle }/edit`, visible: wikiPageFlg }] },
      { name: 'ユーザ', to: '/users', subMenu: [
	  { name: '一覧', to: '/users', visible: false },
	  { name: 'お前', to: `/users/${ user?.id }`, visible: false },
	  { name: '設定', to: '/users/settings', visible: Boolean (user) }] }]

  const activeIdx = menu.findIndex (item => location.pathname.startsWith (item.base || item.to))

  const prevActiveIdxRef = useRef<number> (activeIdx)

  if (activeIdx !== prevActiveIdxRef.current)
    {
      dirRef.current = activeIdx > prevActiveIdxRef.current ? 1 : -1
      prevActiveIdxRef.current = activeIdx
    }

  const dir = dirRef.current

  useLayoutEffect (() => {
    if (activeIdx < 0)
      return

    const raf = requestAnimationFrame (measure)
    const onResize = () => requestAnimationFrame (measure)

    addEventListener ('resize', onResize)
    return () => {
      cancelAnimationFrame (raf)
      removeEventListener ('resize', onResize)
    }
  }, [activeIdx])

  useEffect (() => {
    const unsubscribe = WikiIdBus.subscribe (setWikiId)
    return () => unsubscribe ()
  }, [])

  useEffect (() => {
    setMenuOpen (false)
    setOpenItemIdx (menu.findIndex (item => (
	location.pathname.startsWith (item.base || item.to))))
  }, [location])

  return (
      <>
	<nav className="px-3 flex justify-between items-center w-full min-h-[48px]
			bg-yellow-200 dark:bg-red-975 md:bg-yellow-50">
	  <div className="flex items-center gap-2 h-full">
	    <PrefetchLink
		to="/posts"
		className="mx-4 text-xl font-bold text-pink-600 hover:text-pink-400
			   dark:text-pink-300 dark:hover:text-pink-100"
		onClick={() => {
		  scroll (0, 0)
		}}>
	      ぼざクリ タグ広場
	    </PrefetchLink>

	    <div ref={navRef} className="relative hidden md:flex h-full items-center">
	      <div aria-hidden
		   className={cn ('absolute top-1/2 -translate-y-1/2 h-full',
				  'bg-yellow-200 dark:bg-red-950',
				  'transition-[transform,width] duration-200 ease-out')}
		   style={{ width:      hl.width,
			    transform:  `translate(${ hl.left }px, -50%)`,
			    opacity:    hl.visible ? 1 : 0 }}/>

	      {menu.map ((item, i) => (
		  <PrefetchLink
		      key={i}
		      to={item.to}
		      ref={(el: (HTMLAnchorElement | null)) => {
			itemsRef.current[i] = el
		      }}
		      className={cn ('relative z-10 flex h-full items-center px-5',
				     (i === openItemIdx) && 'font-bold')}>
		    {item.name}
		  </PrefetchLink>))}
	    </div>
	  </div>

	  <TopNavUser user={user}/>

	  <a href="#"
	     className="md:hidden ml-auto pr-4
			text-pink-600 hover:text-pink-400
			dark:text-pink-300 dark:hover:text-pink-100"
	     onClick={ev => {
	       ev.preventDefault ()
	       setMenuOpen (!(menuOpen))
	     }}>
	    {menuOpen ? '×' : 'Menu'}
	  </a>
	</nav>

	<div className="relative hidden md:flex bg-yellow-200 dark:bg-red-950
			items-center w-full min-h-[40px] overflow-hidden">
	  <AnimatePresence initial={false} custom={dir}>
	    <motion.div
		key={activeIdx}
		custom={dir}
		variants={{ enter: (d: -1 | 1) => ({ y: d * 24, opacity: 0 }),
			    centre: { y: 0, opacity: 1 },
			    exit: (d: -1 | 1) => ({ y: (-d) * 24, opacity: 0 }) }}
		className="absolute inset-0 flex items-center px-3"
		initial="enter"
		animate="centre"
		exit="exit"
		transition={{ duration: .2, ease: 'easeOut' }}>
	      {(menu[activeIdx]?.subMenu ?? [])
	       .filter (item => item.visible ?? true)
	       .map ((item, i) => (
		  'component' in item
		  ? <Fragment key={`c-${ i }`}>{item.component}</Fragment>
		  : (
		      <PrefetchLink
			  key={`l-${ i }`}
			  to={item.to}
			  className="h-full flex items-center px-3">
			{item.name}
		      </PrefetchLink>)))}
	    </motion.div>
	  </AnimatePresence>
	</div>

	<AnimatePresence initial={false}>
	  {menuOpen && (
	      <motion.div
		  key="spmenu"
		  className={cn ('flex flex-col md:hidden',
				 'bg-yellow-200 dark:bg-red-975 items-start')}
		  variants={{ closed: { clipPath: 'inset(0 0 100% 0)',
					height:   0 },
			      open:   { clipPath: 'inset(0 0 0% 0)',
					height:   'auto' } }}
		  initial="closed"
		  animate="open"
		  exit="closed"
		  transition={{ duration: .2, ease: 'easeOut' }}>
		<Separator/>
		{menu.map ((item, i) => (
		    <Fragment key={i}>
		      <PrefetchLink
			  to={i === openItemIdx ? item.to : '#'}
			  className={cn ('w-full min-h-[40px] flex items-center pl-8',
				     ((i === openItemIdx)
				      && 'font-bold bg-yellow-50 dark:bg-red-950'))}
			  onClick={(ev: MouseEvent<HTMLAnchorElement>) => {
			    if (i !== openItemIdx)
			      {
				ev.preventDefault ()
				setOpenItemIdx (i)
			      }
			  }}>
			{item.name}
		      </PrefetchLink>

		      <AnimatePresence initial={false}>
			{i === openItemIdx && (
			    <motion.div
				key={`sp-sub-${ i }`}
				className="w-full bg-yellow-50 dark:bg-red-950"
				variants={{ closed: { clipPath: 'inset(0 0 100% 0)',
						      height:   0,
						      opacity:  0 },
					    open:   { clipPath: 'inset(0 0 0% 0)',
						      height:   'auto',
						      opacity:  1 } }}
				initial="closed"
				animate="open"
				exit="closed"
				transition={{ duration: .2, ease: 'easeOut' }}>
			      {item.subMenu
			       .filter (subItem => subItem.visible ?? true)
			       .map ((subItem, j) => (
				  'component' in subItem
				  ? (
				      <Fragment key={`sp-c-${ i }-${ j }`}>
					{subItem.component}
				      </Fragment>)
				  : (
				      <PrefetchLink
					  key={`sp-l-${ i }-${ j }`}
					  to={subItem.to}
					  className="w-full min-h-[36px] flex items-center pl-12">
					{subItem.name}
				      </PrefetchLink>)))}
			    </motion.div>)}
		      </AnimatePresence>
		    </Fragment>))}
		<TopNavUser user={user} sp/>
		<Separator/>
	      </motion.div>)}
	</AnimatePresence>
      </>)
}) satisfies FC<Props>
