import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { RouterProvider, createRouter } from '@tanstack/react-router'
import { AuthKitProvider } from '@workos-inc/authkit-react'
import { StrictMode, Suspense, lazy } from 'react'
import { createRoot } from 'react-dom/client'
import { AppErrorBoundary } from './components/ErrorBoundary'
import { resolveClientConfig } from './lib/config'
import { logger } from './lib/logger'
import { initRouteInstrumentation } from './lib/route-instrumentation'
import { initSentry } from './lib/sentry'
import { routeTree } from './routeTree.gen'
import './index.css'

const authKitDevMode = import.meta.env.DEV && import.meta.env.VITE_WORKOS_DEV_MODE === 'true'
const showReactQueryDevtools =
  import.meta.env.DEV && import.meta.env.VITE_REACT_QUERY_DEVTOOLS === 'true'
const ReactQueryDevtoolsPanel = import.meta.env.DEV
  ? lazy(() =>
      import('@tanstack/react-query-devtools').then(({ ReactQueryDevtools }) => ({
        default: ReactQueryDevtools,
      }))
    )
  : null

const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      staleTime: 5 * 60 * 1000,
      retry: 1,
    },
  },
})

const router = createRouter({
  routeTree,
  context: { queryClient },
})

declare module '@tanstack/react-router' {
  interface Register {
    router: typeof router
  }
}

// Optional MSW dev-mock layer: serves representative API data with no backend.
// Gated by VITE_ENABLE_MOCKS so it is never started (or bundled) in production.
async function enableMocking() {
  if (import.meta.env.VITE_ENABLE_MOCKS !== 'true') return
  const { startMockWorker } = await import('./mocks/browser')
  await startMockWorker()
}

function getRoot() {
  return document.getElementById('root')!
}

// Renders a plain error page,
// deliberately without any of the app's own components:
// this runs when we don't yet know whether the app can boot at all,
// so it must not depend on anything the runtime config might affect.
function renderBootFailure(message: string) {
  createRoot(getRoot()).render(
    <div
      style={{
        display: 'flex',
        flexDirection: 'column',
        alignItems: 'center',
        justifyContent: 'center',
        minHeight: '100vh',
        padding: '2rem',
        fontFamily: 'system-ui, sans-serif',
      }}
    >
      <h1 style={{ fontSize: '1.5rem', fontWeight: 600, marginBottom: '0.5rem' }}>
        Unable to start Bookshelf
      </h1>
      <p style={{ color: '#6b7280', textAlign: 'center' }}>{message}</p>
    </div>
  )
}

export async function boot() {
  const config = resolveClientConfig()

  if (!config.workos_client_id) {
    renderBootFailure('Bookshelf is misconfigured (no WorkOS client id). Please contact support.')
    return
  }

  // Sentry is initialised once the config has resolved.
  // This means the very earliest boot errors are not captured by Sentry.
  initSentry({ dsn: config.sentry_dsn, environment: config.sentry_environment })
  logger.setEnvironment(config.sentry_environment)

  initRouteInstrumentation(router)

  await enableMocking()

  createRoot(getRoot()).render(
    <StrictMode>
      <AppErrorBoundary>
        <QueryClientProvider client={queryClient}>
          <AuthKitProvider
            clientId={config.workos_client_id}
            // Tokens carry the issuer of the minting domain and the backend pins
            // the custom-domain issuer, so api.workos.com would fail verification.
            apiHostname={
              import.meta.env.VITE_WORKOS_API_HOSTNAME || 'auth-api.climateresource.com.au'
            }
            redirectUri={
              import.meta.env.VITE_WORKOS_REDIRECT_URI || `${window.location.origin}/auth/callback`
            }
            devMode={authKitDevMode}
          >
            <RouterProvider router={router} />
            {showReactQueryDevtools && ReactQueryDevtoolsPanel ? (
              <Suspense fallback={null}>
                <ReactQueryDevtoolsPanel initialIsOpen={false} />
              </Suspense>
            ) : null}
          </AuthKitProvider>
        </QueryClientProvider>
      </AppErrorBoundary>
    </StrictMode>
  )
}

// Anything that rejects during boot (Sentry init, the mock worker, the initial render)
// would otherwise surface only as an unhandled rejection,
// which is a blank page with the reason visible in the console alone.
boot().catch((err: unknown) => {
  console.error('Bookshelf failed to start', err)
  renderBootFailure('Bookshelf failed to start. Please try reloading the page.')
})
