import { NextRequest } from "next/server"
import { z } from "zod"

import { Lang } from "@/constants/languages"
import { env } from "@/env/server"

export const langInput = z.object({
  lang: z.nativeEnum(Lang),
})

/**
 * Helper function to convert Lang enum to API lang enum.
 */
export const toApiLang = (lang: Lang): string => {
  const result = toApiLangMap[lang]
  if (!result) {
    throw new Error("Invalid language")
  }
  return result
}

const toApiLangMap: { [key in Lang]: string } = {
  [Lang.en]: "En",
  [Lang.sv]: "Sv",
  [Lang.no]: "No",
  [Lang.fi]: "Fi",
  [Lang.da]: "Da",
  [Lang.de]: "De",
}

/**
 * Helper function to convert lang string to Lang enum.
 */
export function toLang(lang: string): Lang | undefined {
  const lowerCaseLang = lang.toLowerCase()
  return Object.values(Lang).find((l) => l === lowerCaseLang)
}

/**
 * Use this function when you want to create URLs that are public facing, for
 * example for redirects or redirectTo query parameters.
 * Dedicated environments are behind Akamai (test, stage, production). They have
 * env.PUBLIC_URL set.
 * All other environment like deploy previews and branch deployments are not
 * behind Akamai and therefore do not have env.PUBLIC_URL set.
 * We need this approach because Netlify uses x-forwarded-host internally and
 * strips it from ever reaching our code.
 * TODO: Replace this approach with custom header in Akamai that mirrors the
 * value in x-forwarded-host which would not get stripped by Netlify.
 * @param request The incoming request.
 * @returns NextURL The public facing URL instance for the given request.
 */
export function getPublicNextURL(request: NextRequest) {
  if (env.PUBLIC_URL) {
    const publicNextURL = request.nextUrl.clone()
    // Akamai in front of Netlify for dedicated environments
    // require us to rewrite the incoming host and hostname
    // to match the public URL used to visit Akamai.
    const url = new URL(env.PUBLIC_URL)
    publicNextURL.host = url.host
    publicNextURL.hostname = url.hostname
    return publicNextURL
  }
  return request.nextUrl
}

/**
 * Use this function when you want the public facing URL for the given request.
 * Read about the motivation in getPublicNextURL above.
 * @see getPublicNextURL
 * @param request The incoming request.
 * @returns string The public facing origin for the given request.
 */
export function getPublicURL(request: NextRequest) {
  if (env.PUBLIC_URL) {
    return env.PUBLIC_URL
  }
  return request.nextUrl.origin
}

/**
 * Use this function when you want to create URLs that are internal (behind Akamai),
 * for example for rewrites. Mainly used for middleware wrapped in auth().
 * The auth() function overrides the origin of the incoming request. It sets it
 * to the origin of AUTH_URL/NEXTAUTH_URL. This means we cannot use the augmented
 * request from auth() for rewrites, as those will point to auth url origin
 * (in front of Akamai) and not the origin of the incoming request (behind Akamai).
 * This results in rewrites going over the internet instead of going through the
 * internal routing at Netlify.
 * For dedicated environments (test, stage and production) we are behind Akamai.
 * For those we have set a value for AUTH_URL/NEXTAUTH_URL, they point to the
 * PUBLIC_URL. For rewrites we need the internal origin inside Netlify.
 * In middleware.ts we copy the incoming origin to a header 'x-sh-origin'. We try
 * and use that first, if not present we assume the internal origin is the value
 * of the host header, as that is what Netlify used for routing to this deployment.
 * @param request The incoming request.
 * @returns NextURL The internal request, in Netlify behind Akamai.
 */
export function getInternalNextURL(request: NextRequest) {
  const { href, origin } = request.nextUrl

  const originHeader = request.headers.get("x-sh-origin")
  if (originHeader) {
    console.log(`[internalNextUrl] using x-sh-origin header`, {
      origin,
      originHeader,
      newOrigin: href.replace(origin, originHeader),
    })
    return new NextRequest(href.replace(origin, originHeader), request).nextUrl
  }

  const hostHeader = request.headers.get("host")
  if (hostHeader) {
    const inputHostOrigin = `${request.nextUrl.protocol}//${hostHeader}`
    console.log(`[internalNextUrl] using host header`, {
      origin,
      hostHeader,
      hostOrigin: inputHostOrigin,
      newOrigin: href.replace(origin, inputHostOrigin),
    })
    const { origin: hostOrigin } = new URL(inputHostOrigin)
    return new NextRequest(href.replace(origin, hostOrigin), request).nextUrl
  }

  console.log(`[internalNextUrl] falling back to incoming request`)
  return request.nextUrl
}
