import {
  createRootRouteWithContext,
  createRoute,
  createRouter,
  redirect,
} from '@tanstack/react-router'
import type { QueryClient } from '@tanstack/react-query'
import { AppShell } from '@/components/layout/AppShell'
import { NotFound } from '@/components/NotFound'
import { RouteError } from '@/components/RouteError'
import { LoginPage } from '@/pages/LoginPage'
import { RegisterPage } from '@/pages/RegisterPage'
import { GardensPage } from '@/pages/GardensPage'
import { GardenEditorPage } from '@/pages/GardenEditorPage'
import { PublicGardenPage } from '@/pages/PublicGardenPage'
import { PlantsPage } from '@/pages/PlantsPage'
import { meQueryOptions } from '@/lib/auth'
import { queryClient } from '@/lib/queryClient'
import { safeRedirectPath } from '@/lib/redirect'

interface RouterContext {
  queryClient: QueryClient
}

const rootRoute = createRootRouteWithContext<RouterContext>()({
  component: AppShell,
  // A beforeLoad/loader failure that isn't a redirect (e.g. /auth/me errors with
  // a 500 or the network drops) lands here instead of a blank screen.
  errorComponent: RouteError,
  // Unknown paths render inside the app shell rather than a blank screen.
  notFoundComponent: NotFound,
})

// requireAuth: resolve the current user (shared cache with useMe); send anyone
// unauthenticated to /login, remembering the path they were headed to.
async function requireAuth(context: RouterContext, path: string) {
  const me = await context.queryClient.ensureQueryData(meQueryOptions)
  if (!me) {
    throw redirect({ to: '/login', search: { redirect: path } })
  }
}

// requireGuest: keep already-authenticated users off /login and /register.
async function requireGuest(context: RouterContext, redirectTo: string) {
  const me = await context.queryClient.ensureQueryData(meQueryOptions)
  if (me) {
    throw redirect({ to: redirectTo })
  }
}

const indexRoute = createRoute({
  getParentRoute: () => rootRoute,
  path: '/',
  beforeLoad: () => {
    throw redirect({ to: '/gardens' })
  },
})

interface LoginSearch {
  redirect?: string
  error?: string
}

const loginRoute = createRoute({
  getParentRoute: () => rootRoute,
  path: 'login',
  // Optional properties (not `key: undefined`) so links to /login don't have to
  // pass a search object.
  validateSearch: (search: Record<string, unknown>): LoginSearch => {
    const out: LoginSearch = {}
    if (typeof search.redirect === 'string') out.redirect = search.redirect
    if (typeof search.error === 'string') out.error = search.error
    return out
  },
  beforeLoad: ({ context, search }) => requireGuest(context, safeRedirectPath(search.redirect)),
  component: LoginPage,
})

const registerRoute = createRoute({
  getParentRoute: () => rootRoute,
  path: 'register',
  beforeLoad: ({ context }) => requireGuest(context, '/gardens'),
  component: RegisterPage,
})

const gardensRoute = createRoute({
  getParentRoute: () => rootRoute,
  path: 'gardens',
  beforeLoad: ({ context, location }) => requireAuth(context, location.href),
  component: GardensPage,
})

const gardenEditorRoute = createRoute({
  getParentRoute: () => rootRoute,
  path: 'gardens/$gardenId',
  // ?focus=<objectId> frames that object on load.
  validateSearch: (search: Record<string, unknown>): { focus?: number } => {
    const f = Number(search.focus)
    return Number.isInteger(f) && f > 0 ? { focus: f } : {}
  },
  beforeLoad: ({ context, location }) => requireAuth(context, location.href),
  component: GardenEditorPage,
})

const plantsRoute = createRoute({
  getParentRoute: () => rootRoute,
  path: 'plants',
  beforeLoad: ({ context, location }) => requireAuth(context, location.href),
  component: PlantsPage,
})

// Public read-only garden by share token. Deliberately has NO beforeLoad auth
// guard, so a logged-out visitor viewing a shared link is never redirected to
// /login or OIDC.
const publicGardenRoute = createRoute({
  getParentRoute: () => rootRoute,
  path: 'g/$token',
  component: PublicGardenPage,
})

const routeTree = rootRoute.addChildren([
  indexRoute,
  loginRoute,
  registerRoute,
  gardensRoute,
  gardenEditorRoute,
  plantsRoute,
  publicGardenRoute,
])

export const router = createRouter({
  routeTree,
  defaultPreload: 'intent',
  context: { queryClient },
})

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