/**
 * Language Detection with Static Page Preservation
 *
 * This catch-all route provides browser-based language detection while maintaining static
 * MDX page generation. Since we want to keep all content pages static for performance,
 * we cannot use Astro middleware (which would require server-side rendering and has no
 * access to request headers for static pages).
 *
 * Implementation:
 * - Used with a dynamic route with prerender=false to access request headers for language detection
 * - Implements custom request forwarding since Astro.rewrite() doesn't properly route to static pages
 * - Acts as a transparent proxy that fetches the appropriate localized content and forwards the response
 *
 * This approach preserves static generation benefits while enabling dynamic language routing.
 */
import config from '../../gt.config.json'

const defaultLocale = config.defaultLocale
const allLocales = [defaultLocale, ...config.locales]

/**
 * Detects preferred language from Accept-Language header
 */
export function getPreferredLanguage(request: Request): string {
  const acceptLanguage = request.headers.get('accept-language')
  if (!acceptLanguage) return defaultLocale

  const supportedLocales = [defaultLocale, ...config.locales]
  const languages = acceptLanguage
    .split(',')
    .map(lang => lang.split(';')[0].trim().toLowerCase())
    .map(lang => lang.split('-')[0]) // Convert en-US to en

  for (const lang of languages) {
    if (supportedLocales.includes(lang)) {
      return lang
    }
  }

  return defaultLocale // fallback
}

/**
 * Checks if a path segment contains a locale prefix to prevent infinite loops
 */
export function isLocalizedPath(slug: string | undefined): boolean {
  if (!slug) return false
  const firstSegment = slug.split('/')[0]
  return allLocales.includes(firstSegment)
}

/**
 * Origin for server-side self-fetch. Incoming request.url often reflects the
 * internal listener; using that origin causes ECONNREFUSED when nothing listens on localhost:443.
 *
 * In dev, always use the incoming request origin. If PUBLIC_WEB_URL points at
 * production, preferring it would fetch prod HTML while the browser URL stays
 * on localhost; root-relative asset paths would then hit the dev server wrong
 */
function originFromSiteConfig(site: string): string | null {
  const trimmed = site.trim()
  if (!trimmed) return null
  try {
    return new URL(trimmed).origin
  } catch {
    return null
  }
}

function getProxyOrigin(request: Request): string {
  const reqUrl = new URL(request.url)
  const host = reqUrl.hostname
  const isLoopback =
    host === 'localhost' || host === '127.0.0.1' || host === '::1'

  if (import.meta.env.DEV) {
    return reqUrl.origin
  }

  const functionsPort =
    typeof process !== 'undefined' && process.env.FUNCTIONS_PORT
  if (functionsPort) {
    return `http://localhost:${functionsPort}`
  }

  const site =
    import.meta.env.PUBLIC_WEB_URL ||
    import.meta.env.SITE ||
    (typeof process !== 'undefined' && process.env.PUBLIC_WEB_URL) ||
    ''

  if (isLoopback && typeof site === 'string') {
    const fromSite = originFromSiteConfig(site)
    if (fromSite) return fromSite
  }

  return reqUrl.origin
}

/**
 * Headers safe to forward when fetching another path on the public site.
 * Leave request headers empty to avoid accessing `Astro.request.headers`
 * when proxying into prerendered static routes.
 */
function headersForProxyFetch(): Headers {
  return new Headers()
}

/**
 * Creates a transparent proxy request to serve localized content
 * This bypasses Astro.rewrite() limitations with static routes
 */
export async function proxyLocalizedContent(
  targetPath: string,
  originalRequest: Request
): Promise<Response> {
  try {
    const targetUrl = new URL(targetPath, getProxyOrigin(originalRequest))

    const response = await fetch(targetUrl.toString(), {
      method: originalRequest.method,
      headers: headersForProxyFetch(),
      body:
        originalRequest.method === 'GET' || originalRequest.method === 'HEAD'
          ? undefined
          : originalRequest.body,
    })

    // Filter out headers that cause compression issues
    const filteredHeaders = new Headers()
    for (const [key, value] of response.headers.entries()) {
      // Skip headers that can cause decoding issues when proxying
      if (
        !['content-encoding', 'content-length', 'transfer-encoding'].includes(
          key.toLowerCase()
        )
      ) {
        filteredHeaders.set(key, value)
      }
    }

    // Create a new response with filtered headers
    return new Response(response.body, {
      status: response.status,
      statusText: response.statusText,
      headers: filteredHeaders,
    })
  } catch (error) {
    console.error('Proxy request failed:', error)
    return new Response('Internal Server Error', { status: 500 })
  }
}

/**
 * Main routing handler for language detection and content serving
 */
export async function handleLanguageRouting(
  request: Request,
  slug = '',
  redirectFn: (path: string) => Response
): Promise<Response> {
  const normalizedSlug = slug.replace(/^\/+|\/+$/g, '')

  const serveCustom404 = async (): Promise<Response> => {
    const response = await proxyLocalizedContent('/docs/404', request)
    return new Response(response.body, {
      status: 404,
      statusText: 'Not Found',
      headers: response.headers,
    })
  }

  if (isLocalizedPath(normalizedSlug)) return await serveCustom404()

  const preferredLanguage = getPreferredLanguage(request)

  if (normalizedSlug) {
    // For unprefixed docs paths, use the default locale canonical URL.
    // This avoids SSR self-fetch loops that can incorrectly produce 404.
    return redirectFn(`/docs/${defaultLocale}/${normalizedSlug}`)
  }

  return redirectFn(`/docs/${preferredLanguage}`)
}
