import { type NextMiddleware, NextResponse } from "next/server"

import { findLang } from "@/constants/languages"
import {
  loyaltyPagesWebviews,
  myPagesWebviews,
  refreshWebviews,
  webviews,
} from "@/constants/routes/webviews"
import { env } from "@/env/server"
import { badRequest, notFound } from "@/server/errors/next"

import { decryptData } from "@/utils/aes"
import { resolve as resolveEntry } from "@/utils/entry"

import { getDefaultRequestHeaders } from "./utils"

import type { MiddlewareMatcher } from "@/types/middleware"

export const middleware: NextMiddleware = async (request) => {
  const { nextUrl } = request
  const lang = findLang(nextUrl.pathname)

  // If user is redirected to /lang/webview/refresh/, the webview token is invalid and we remove the cookie
  if (refreshWebviews.includes(nextUrl.pathname)) {
    return NextResponse.rewrite(
      new URL(
        `/${lang}/webview/refresh?${nextUrl.searchParams.toString()}`,
        nextUrl
      ),
      {
        headers: {
          "Set-Cookie": `webviewToken=0; Max-Age=0; Secure; HttpOnly; Path=/; SameSite=Strict;`,
        },
      }
    )
  }

  const pathNameWithoutLang = nextUrl.pathname.replace(`/${lang}/webview`, "")

  const { uid } = await resolveEntry(pathNameWithoutLang, lang)
  if (!uid) {
    throw notFound(
      `Unable to resolve CMS entry for locale "${lang}": ${pathNameWithoutLang}`
    )
  }
  const headers = getDefaultRequestHeaders(request)
  headers.set("x-uid", uid)

  const webviewToken = request.cookies.get("webviewToken")
  if (webviewToken) {
    // since the token exists, this is a subsequent visit
    // we're done, allow it
    if (myPagesWebviews.includes(nextUrl.pathname)) {
      return NextResponse.rewrite(
        new URL(`/${lang}/webview/account-page/${uid}`, nextUrl),
        {
          request: {
            headers,
          },
        }
      )
    } else if (loyaltyPagesWebviews.includes(nextUrl.pathname)) {
      return NextResponse.rewrite(
        new URL(`/${lang}/webview/loyalty-page/${uid}`, nextUrl),
        {
          request: {
            headers,
          },
        }
      )
    } else {
      return notFound()
    }
  }

  try {
    // Authorization header is required for webviews
    // It should be base64 encoded
    const authorization = request.headers.get("X-Authorization")!
    if (!authorization) {
      console.error("Authorization header is missing")
      return badRequest("Authorization header is missing")
    }

    // Initialization vector header is required for webviews
    // It should be base64 encoded
    const initializationVector = request.headers.get("X-AES-IV")!
    if (!initializationVector) {
      console.error("initializationVector header is missing")
      return badRequest("initializationVector header is missing")
    }

    const decryptedData = await decryptData(
      env.WEBVIEW_ENCRYPTION_KEY,
      initializationVector,
      authorization
    )

    headers.append("Cookie", `webviewToken=${decryptedData}`)

    if (myPagesWebviews.includes(nextUrl.pathname)) {
      return NextResponse.rewrite(
        new URL(`/${lang}/webview/account-page/${uid}`, nextUrl),
        {
          headers: {
            "Set-Cookie": `webviewToken=${decryptedData}; Secure; HttpOnly; Path=/; SameSite=Strict;`,
          },
          request: {
            headers,
          },
        }
      )
    } else if (loyaltyPagesWebviews.includes(nextUrl.pathname)) {
      return NextResponse.rewrite(
        new URL(`/${lang}/webview/loyalty-page/${uid}`, nextUrl),
        {
          headers: {
            "Set-Cookie": `webviewToken=${decryptedData}; Secure; HttpOnly; Path=/; SameSite=Strict;`,
          },
          request: {
            headers,
          },
        }
      )
    }
  } catch (e) {
    if (e instanceof Error) {
      console.error(`${e.name}: ${e.message}`)
    }

    return badRequest()
  }
}

export const matcher: MiddlewareMatcher = (request) => {
  const { nextUrl } = request

  return webviews.includes(nextUrl.pathname)
}
