import { AnimatePresence, LayoutGroup, MotionConfig, motion } from 'framer-motion'
import { Fragment, useEffect, useMemo, useState } from 'react'
import { BrowserRouter,
	 Navigate,
	 Route,
	 Routes,
	 useLocation } from 'react-router-dom'

import DevModeWatermark from '@/components/DevModeWatermark'
import RouteBlockerOverlay from '@/components/RouteBlockerOverlay'
import TopNav from '@/components/TopNav'
import DialogueProvider from '@/components/dialogues/DialogueProvider'
import { Toaster } from '@/components/ui/toaster'
import { apiPost, isApiError } from '@/lib/api'
import { applyClientAnimationMode,
	 applyClientAppearance,
	 type ClientAnimationMode,
	 fetchUserThemeSlots,
	 fetchUserSettings,
	 getClientThemeMode,
	 hasStoredClientThemeSelection,
	 normaliseUserThemeSlots,
	 setCachedUserThemeSlots,
	 seedClientThemeMode } from '@/lib/settings'
import { useClientBehaviourSettings } from '@/lib/useClientBehaviourSettings'
import { UnsavedChangesGuardProvider } from '@/lib/useUnsavedChangesGuard'

import { KeyboardShortcutsProvider } from '@/lib/useKeyboardShortcuts'
import DeerjikistDetailPage from '@/pages/deerjikists/DeerjikistDetailPage'
import MaterialBasePage from '@/pages/materials/MaterialBasePage'
import MaterialDetailPage from '@/pages/materials/MaterialDetailPage'
import MaterialHistoryPage from '@/pages/materials/MaterialHistoryPage'
import MaterialListPage from '@/pages/materials/MaterialListPage'
import MaterialNewPage from '@/pages/materials/MaterialNewPage'
import MaterialSyncSuppressionsPage from '@/pages/materials/MaterialSyncSuppressionsPage'
// import MaterialSearchPage from '@/pages/materials/MaterialSearchPage'
import MorePage from '@/pages/MorePage'
import GekanatorPage from '@/pages/GekanatorPage'
import NicoTagListPage from '@/pages/tags/NicoTagListPage'
import NotFound from '@/pages/NotFound'
import TOSPage from '@/pages/TOSPage.mdx'
import PostDetailPage from '@/pages/posts/PostDetailPage'
import PostHistoryPage from '@/pages/posts/PostHistoryPage'
import PostListPage from '@/pages/posts/PostListPage'
import PostNewPage from '@/pages/posts/PostNewPage'
import PostSearchPage from '@/pages/posts/PostSearchPage'
import ServiceUnavailable from '@/pages/ServiceUnavailable'
import SettingPage from '@/pages/users/SettingPage'
import TagDetailPage from '@/pages/tags/TagDetailPage'
import TagHistoryPage from '@/pages/tags/TagHistoryPage'
import TagListPage from '@/pages/tags/TagListPage'
import TheatreDetailPage from '@/pages/theatres/TheatreDetailPage'
import WikiDetailPage from '@/pages/wiki/WikiDetailPage'
import WikiDiffPage from '@/pages/wiki/WikiDiffPage'
import WikiEditPage from '@/pages/wiki/WikiEditPage'
import WikiHistoryPage from '@/pages/wiki/WikiHistoryPage'
import WikiNewPage from '@/pages/wiki/WikiNewPage'
import WikiSearchPage from '@/pages/wiki/WikiSearchPage'

import type { Dispatch, FC, SetStateAction } from 'react'

import type { User } from '@/types'


const RouteTransitionWrapper = ({ animationMode, user, setUser }: {
    animationMode: ClientAnimationMode
    user:     User | null
    setUser:  Dispatch<SetStateAction<User | null>> }) => {
  const location = useLocation ()

  if (animationMode === 'off')
    {
      return (
	  <Routes location={location}>
	    <Route path="/" element={<Navigate to="/posts" replace/>}/>
	    <Route path="/posts" element={<PostListPage/>}/>
	    <Route path="/posts/new" element={<PostNewPage user={user}/>}/>
	    <Route path="/posts/search" element={<PostSearchPage/>}/>
	    <Route path="/posts/:id" element={<PostDetailRoute user={user}/>}/>
	    <Route path="/posts/changes" element={<PostHistoryPage/>}/>
	    <Route path="/tags" element={<TagListPage/>}/>
	    <Route path="/tags/:id" element={<TagDetailPage/>}/>
	    <Route path="/tags/:id/deerjikists" element={<DeerjikistDetailPage/>}/>
	    <Route path="/tags/nico" element={<NicoTagListPage user={user}/>}/>
	    <Route path="/nico/tags" element={<NicoTagListPage user={user}/>}/>
	    <Route path="/tags/changes" element={<TagHistoryPage/>}/>
	    <Route path="/theatres/:id" element={<TheatreDetailPage user={user}/>}/>
	    <Route path="/materials" element={<MaterialBasePage/>}>
	      <Route index element={<MaterialListPage/>}/>
	      <Route path="changes" element={<MaterialHistoryPage/>}/>
	      <Route path="new" element={<MaterialNewPage/>}/>
	      <Route path="suppressions" element={<MaterialSyncSuppressionsPage/>}/>
	      <Route path=":id" element ={<MaterialDetailPage/>}/>
	    </Route>
	    <Route path="/wiki" element={<WikiSearchPage/>}/>
	    <Route path="/wiki/:title" element={<WikiDetailPage/>}/>
	    <Route path="/wiki/new" element={<WikiNewPage user={user}/>}/>
	    <Route path="/wiki/:id/edit" element={<WikiEditPage user={user}/>}/>
	    <Route path="/wiki/:id/diff" element={<WikiDiffPage/>}/>
	    <Route path="/wiki/changes" element={<WikiHistoryPage/>}/>
	    <Route path="/users/settings" element={<SettingPage user={user} setUser={setUser}/>}/>
	    <Route path="/settings" element={<Navigate to="/users/settings" replace/>}/>
	    <Route path="/tos" element={<TOSPage/>}/>
	    <Route path="/gekanator" element={<GekanatorPage user={user}/>}/>
	    <Route path="/more" element={<MorePage/>}/>
	    <Route path="*" element={<NotFound/>}/>
	  </Routes>)
    }

  return (
      <AnimatePresence mode="wait">
	<Routes location={location}>
	  <Route path="/" element={<Navigate to="/posts" replace/>}/>
	  <Route path="/posts" element={<PostListPage/>}/>
	  <Route path="/posts/new" element={<PostNewPage user={user}/>}/>
	  <Route path="/posts/search" element={<PostSearchPage/>}/>
	  <Route path="/posts/:id" element={<PostDetailRoute user={user}/>}/>
	  <Route path="/posts/changes" element={<PostHistoryPage/>}/>
	  <Route path="/tags" element={<TagListPage/>}/>
	  <Route path="/tags/:id" element={<TagDetailPage/>}/>
	  <Route path="/tags/:id/deerjikists" element={<DeerjikistDetailPage/>}/>
	  <Route path="/tags/nico" element={<NicoTagListPage user={user}/>}/>
	  <Route path="/nico/tags" element={<NicoTagListPage user={user}/>}/>
	  <Route path="/tags/changes" element={<TagHistoryPage/>}/>
	  <Route path="/theatres/:id" element={<TheatreDetailPage user={user}/>}/>
	  <Route path="/materials" element={<MaterialBasePage/>}>
	    <Route index element={<MaterialListPage/>}/>
	    <Route path="changes" element={<MaterialHistoryPage/>}/>
	    <Route path="new" element={<MaterialNewPage/>}/>
	    <Route path="suppressions" element={<MaterialSyncSuppressionsPage/>}/>
	    <Route path=":id" element ={<MaterialDetailPage/>}/>
	  </Route>
	  <Route path="/wiki" element={<WikiSearchPage/>}/>
	  <Route path="/wiki/:title" element={<WikiDetailPage/>}/>
	  <Route path="/wiki/new" element={<WikiNewPage user={user}/>}/>
	  <Route path="/wiki/:id/edit" element={<WikiEditPage user={user}/>}/>
	  <Route path="/wiki/:id/diff" element={<WikiDiffPage/>}/>
	  <Route path="/wiki/changes" element={<WikiHistoryPage/>}/>
	  <Route path="/users/settings" element={<SettingPage user={user} setUser={setUser}/>}/>
	  <Route path="/settings" element={<Navigate to="/users/settings" replace/>}/>
	  <Route path="/tos" element={<TOSPage/>}/>
	  <Route path="/gekanator" element={<GekanatorPage user={user}/>}/>
	  <Route path="/more" element={<MorePage/>}/>
	  <Route path="*" element={<NotFound/>}/>
	</Routes>
      </AnimatePresence>)
}


const PostDetailRoute = ({ user }: { user: User | null }) => {
  const location = useLocation ()
  const key = location.pathname
  return <PostDetailPage key={key} user={user}/>
}


const App: FC = () => {
  const [user, setUser] = useState<User | null> (null)
  const [status, setStatus] = useState (200)
  const behaviourSettings = useClientBehaviourSettings ()
  const animationMode = behaviourSettings.animation ?? 'normal'
  const LayoutWrapper = animationMode === 'off' ? Fragment : LayoutGroup

  const appLayoutTransition = useMemo (
      () => (
	  animationMode === 'off'
	  ? { duration: 0 }
	  : animationMode === 'reduced'
	    ? { duration: .08, ease: 'linear' as const }
	    : { duration: .2, ease: 'easeOut' as const }
      ),
      [animationMode],
  )

  useEffect (() => {
    applyClientAppearance ()

    const mediaQuery = window.matchMedia ('(prefers-color-scheme: dark)')
    const handleThemeChange = () => {
      if (getClientThemeMode () === 'system')
	applyClientAppearance ()
    }

    mediaQuery.addEventListener ('change', handleThemeChange)
    return () => mediaQuery.removeEventListener ('change', handleThemeChange)
  }, [])

  useEffect (() => {
    applyClientAnimationMode (animationMode)
  }, [animationMode])

  useEffect (() => {
    const createUser = async () => {
      const data = await apiPost<{ code: string; user: User }> ('/users')
      if (data.code)
	{
	  localStorage.setItem ('user_code', data.code)
	  setUser (data.user)
	}
    }

    const code = localStorage.getItem ('user_code')
    if (code)
      {
	void (async () => {
	  try
	  {
	    const data = await apiPost<{ valid: boolean; user: User }> ('/users/verify', { code })
	    if (data.valid)
	      setUser (data.user)
	    else
	      await createUser ()
	  }
	  catch (err)
	  {
	    if (isApiError (err))
	      setStatus (err.response?.status ?? 200)
	  }
	}) ()
      }
    else
      createUser ()
  }, [])

  useEffect (() => {
    if (!(user))
      return

    void (async () => {
	try
	{
	  const [settings, themeSlots] = await Promise.all ([
	      fetchUserSettings (),
	      fetchUserThemeSlots (),
	  ])
	  const storedThemeSlots = normaliseUserThemeSlots (themeSlots)
	  setCachedUserThemeSlots (storedThemeSlots)
	  if (!(hasStoredClientThemeSelection ()))
	    seedClientThemeMode (settings.theme)
	  applyClientAppearance ()
	}
      catch
      {
	return
      }
    }) ()
  }, [user])

  switch (status)
  {
  case 503:
    return <ServiceUnavailable/>
  }

  return (
      <>
	<RouteBlockerOverlay/>
	{import.meta.env.DEV && <DevModeWatermark/>}

	<BrowserRouter>
	  <DialogueProvider>
	    <UnsavedChangesGuardProvider>
	      <KeyboardShortcutsProvider>
	      <MotionConfig
		  reducedMotion={
		      animationMode === 'normal'
		      ? 'never'
		      : animationMode === 'reduced'
			? 'user'
			: 'always'
		  }>
		<LayoutWrapper>
		  <motion.div
		      layout={animationMode === 'off' ? false : 'position'}
		      transition={{ layout: appLayoutTransition }}
		      className="relative flex flex-col h-dvh w-full overflow-y-hidden">
		    <TopNav user={user}/>
		    <RouteTransitionWrapper
			animationMode={animationMode}
			user={user}
			setUser={setUser}/>
		  </motion.div>
		</LayoutWrapper>
	      </MotionConfig>

	      <Toaster/>
	      </KeyboardShortcutsProvider>
	    </UnsavedChangesGuardProvider>
	  </DialogueProvider>
	</BrowserRouter>
      </>)
}

export default App
