import { useMemo } from 'react'
import toast from 'react-hot-toast'
import {
  Outlet,
  RouterProvider,
  createBrowserRouter,
  createHashRouter,
  redirect,
} from 'react-router-dom'

import { App } from '@src/App'
import { Auth } from '@src/Auth'
import { CommandBar } from '@src/components/CommandBar/CommandBar'
import { ErrorPage } from '@src/components/ErrorPage'
import FileMachineProvider from '@src/components/FileMachineProvider'
import ModelingMachineProvider from '@src/components/ModelingMachineProvider'
import { NetworkContext } from '@src/hooks/useNetworkContext'
import { useNetworkStatus } from '@src/hooks/useNetworkStatus'
import { coreDump } from '@src/lang/wasm'
import {
  ASK_TO_OPEN_QUERY_PARAM,
  BROWSER_PROJECT_NAME,
} from '@src/lib/constants'
import { CoreDumpManager } from '@src/lib/coredump'
import useHotkeyWrapper from '@src/lib/hotkeyWrapper'
import { isDesktop } from '@src/lib/isDesktop'
import makeUrlPathRelative from '@src/lib/makeUrlPathRelative'
import { PATHS } from '@src/lib/paths'
import { fileLoader, homeLoader } from '@src/lib/routeLoaders'
import {
  codeManager,
  engineCommandManager,
  rustContext,
} from '@src/lib/singletons'
import { reportRejection } from '@src/lib/trap'
import { useToken } from '@src/lib/singletons'
import RootLayout from '@src/Root'
import Home from '@src/routes/Home'
import { OnboardingRootRoute, onboardingRoutes } from '@src/routes/Onboarding'
import { Settings } from '@src/routes/Settings'
import SignIn from '@src/routes/SignIn'
import { Telemetry } from '@src/routes/Telemetry'

const createRouter = isDesktop() ? createHashRouter : createBrowserRouter

const router = createRouter([
  {
    id: PATHS.INDEX,
    element: <RootLayout />,
    // Gotcha: declaring errorElement on the root will unmount the element causing our forever React components to unmount.
    // Leave errorElement on the child components, this allows for the entire react context on error pages as well.
    children: [
      {
        path: PATHS.INDEX,
        errorElement: <ErrorPage />,
        loader: async ({ request }) => {
          const onDesktop = isDesktop()
          const url = new URL(request.url)
          if (onDesktop) {
            return redirect(PATHS.HOME + (url.search || ''))
          } else {
            const searchParams = new URLSearchParams(url.search)
            if (!searchParams.has(ASK_TO_OPEN_QUERY_PARAM)) {
              return redirect(
                PATHS.FILE + '/%2F' + BROWSER_PROJECT_NAME + (url.search || '')
              )
            }
          }
          return null
        },
      },
      {
        loader: fileLoader,
        id: PATHS.FILE,
        path: PATHS.FILE + '/:id',
        errorElement: <ErrorPage />,
        element: (
          <Auth>
            <FileMachineProvider>
              <ModelingMachineProvider>
                <CoreDump />
                <Outlet />
                <App />
                <CommandBar />
              </ModelingMachineProvider>
            </FileMachineProvider>
          </Auth>
        ),
        children: [
          {
            id: PATHS.FILE + 'SETTINGS',
            children: [
              {
                path: makeUrlPathRelative(PATHS.SETTINGS),
                element: <Settings />,
              },
              {
                path: makeUrlPathRelative(PATHS.ONBOARDING),
                element: <OnboardingRootRoute />,
                children: onboardingRoutes,
              },
            ],
          },
          {
            id: PATHS.FILE + 'TELEMETRY',
            children: [
              {
                path: makeUrlPathRelative(PATHS.TELEMETRY),
                element: <Telemetry />,
              },
            ],
          },
        ],
      },
      {
        path: PATHS.HOME,
        errorElement: <ErrorPage />,
        element: (
          <Auth>
            <Outlet />
            <Home />
            <CommandBar />
          </Auth>
        ),
        id: PATHS.HOME,
        loader: homeLoader,
        children: [
          {
            index: true,
            element: <></>,
            id: PATHS.HOME + 'SETTINGS',
          },
          {
            path: makeUrlPathRelative(PATHS.SETTINGS),
            element: <Settings />,
          },
          {
            path: makeUrlPathRelative(PATHS.TELEMETRY),
            element: <Telemetry />,
          },
        ],
      },
      {
        path: PATHS.SIGN_IN,
        errorElement: <ErrorPage />,
        element: <SignIn />,
      },
    ],
  },
])

/**
 * All routes in the app, used in src/index.tsx
 * @returns RouterProvider
 */
export const Router = () => {
  const networkStatus = useNetworkStatus()

  return (
    <NetworkContext.Provider value={networkStatus}>
      <RouterProvider router={router} />
    </NetworkContext.Provider>
  )
}

function CoreDump() {
  const token = useToken()
  const coreDumpManager = useMemo(
    () =>
      new CoreDumpManager(
        engineCommandManager,
        codeManager,
        rustContext,
        token
      ),
    []
  )
  useHotkeyWrapper(['mod + shift + .'], () => {
    toast
      .promise(
        coreDump(coreDumpManager, true),
        {
          loading: 'Starting core dump...',
          success: 'Core dump completed successfully',
          error: 'Error while exporting core dump',
        },
        {
          success: {
            // Note: this extended duration is especially important for Playwright e2e testing
            // default duration is 2000 - https://react-hot-toast.com/docs/toast#default-durations
            duration: 6000,
          },
        }
      )
      .catch(reportRejection)
  })
  return null
}
