import type { ReactNode } from 'react'
import { dehydrate, HydrationBoundary } from '@tanstack/react-query'
import { makeQueryClient } from '@/context/query-client-server'
import { serverUserProfileQueryOptions } from '@/features/account-profile/server'
import { headers } from '@/next/headers'
import { redirect } from '@/next/navigation'
import {
  getServerConsoleClientContext,
  resolveServerConsoleApiUrl,
  serverConsoleQuery,
} from '@/service/server'

const CURRENT_PATHNAME_HEADER = 'x-dify-pathname'
const CURRENT_SEARCH_HEADER = 'x-dify-search'
const ACCOUNT_PROFILE_PATH = '/account/profile'
const AUTH_REFRESH_PATH = '/auth/refresh'

type ConsoleErrorPayload = {
  code?: string
}

const isConsoleErrorPayload = (value: unknown): value is ConsoleErrorPayload =>
  Boolean(value) && typeof value === 'object' && !Array.isArray(value)

const parseConsoleErrorPayload = async (error: Response): Promise<ConsoleErrorPayload | null> => {
  try {
    const payload: unknown = await error.clone().json()
    return isConsoleErrorPayload(payload) ? payload : null
  } catch {
    return null
  }
}

const getCurrentPath = async () => {
  const requestHeaders = await headers()
  const pathname = requestHeaders.get(CURRENT_PATHNAME_HEADER) || '/'
  const search = requestHeaders.get(CURRENT_SEARCH_HEADER) || ''
  return `${pathname}${search}`
}

const redirectToAuthRefresh = async () => {
  const currentPath = await getCurrentPath()
  redirect(`${AUTH_REFRESH_PATH}?redirect_url=${encodeURIComponent(currentPath)}`)
}

const handleProfileError = async (error: unknown) => {
  if (!(error instanceof Response)) throw error

  const errorData = await parseConsoleErrorPayload(error)
  if (errorData?.code === 'not_setup') redirect('/install')
  if (errorData?.code === 'not_init_validated') redirect('/init')
  if (error.status === 401) await redirectToAuthRefresh()

  throw error
}

export async function CommonLayoutHydrationBoundary({ children }: { children: ReactNode }) {
  const queryClient = makeQueryClient()
  const accountProfileUrl = resolveServerConsoleApiUrl(ACCOUNT_PROFILE_PATH)

  if (accountProfileUrl) {
    try {
      const context = await getServerConsoleClientContext()

      await Promise.all([
        queryClient.fetchQuery(serverUserProfileQueryOptions()),
        queryClient.prefetchQuery(
          serverConsoleQuery.workspaces.current.post.queryOptions({
            context,
            retry: false,
          }),
        ),
        queryClient.prefetchQuery(
          serverConsoleQuery.workspaces.current.rbac.myPermissions.get.queryOptions({
            context,
            retry: false,
          }),
        ),
      ])
    } catch (error) {
      await handleProfileError(error)
    }
  }

  return <HydrationBoundary state={dehydrate(queryClient)}>{children}</HydrationBoundary>
}
