import { NextRequest, NextResponse } from "next/server"

import { pingServer, verifyToken } from "@shared/api/server"
import { ENTRY_PATHS_REGEXP, EXCLUDE_PATHS_REGEXP } from "@shared/lib/constants"

const redirector = async ({
	pathname,
	queryString,
	headers,
	initialPath,
	endPath,
	origin,
}: {
	initialPath: string
	endPath: string
	pathname: string
	queryString: string
	origin: string
	headers: Headers
}) => {
	if (pathname.includes(initialPath)) {
		const sp = queryString.length ? `?${queryString}` : ""
		const finalURL = `${origin}${pathname.replace(initialPath, endPath)}${sp}`
		return NextResponse.redirect(finalURL, { headers })
	}
}

const redirections: { from: string; to: string }[] = []

export async function proxy(req: NextRequest) {
	// Base URL data from nextjs
	const { origin, pathname, searchParams } = req.nextUrl
	// Headers data from nextjs
	const headers = new Headers(req.headers)
	// Token data from cookies
	const token = req.cookies.get("access_token")?.value
	// Server status check
	const isServerAlive = (await pingServer()) !== false
	// Entry paths checkers
	const isEntryPath = ENTRY_PATHS_REGEXP.test(pathname)
	const isExcludedPath = EXCLUDE_PATHS_REGEXP.test(pathname)
	// Stringified qs
	const queryParams = searchParams.toString()

	// Set current path header for analytics purposes
	headers.set("x-current-path", req.nextUrl.pathname)

	// Redirect to under_maintenance if server is down
	if (!isServerAlive && pathname !== "/under_maintenance") {
		return NextResponse.redirect(
			`${origin}/under_maintenance?path=${pathname}${queryParams}`,
			{ headers },
		)
	}

	// Verify user if page is not public
	if (!isExcludedPath && isServerAlive) {
		const isVerified =
			token &&
			(await verifyToken(token).catch((e) => {
				console.error(
					"Middleware verification error\n",
					String(e).toLowerCase(),
				)
				return null
			}))
		if (isVerified && isEntryPath) {
			return NextResponse.redirect(`${origin}/`, { headers })
		}
		if (!isVerified && !isEntryPath) {
			const returnParam = pathname === "/" ? "" : `?from=${pathname}`
			return NextResponse.redirect(`${origin}/login${returnParam}`, { headers })
		}
	}

	for (const redirection of redirections) {
		const redirect = await redirector({
			pathname,
			headers,
			origin,
			initialPath: redirection.from,
			endPath: redirection.to,
			queryString: searchParams.toString(),
		})
		if (redirect) {
			return redirect
		}
	}

	return NextResponse.next({ headers, request: { headers } })
}

export const config = {
	matcher: [
		{
			source: "/((?!api|_next/static|_next/image|favicon.ico).*)",
			missing: [
				{ type: "header", key: "next-router-prefetch" },
				{ type: "header", key: "next-action" },
				{ type: "header", key: "purpose", value: "prefetch" },
			],
		},
	],
}
