import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";
import { jwtVerify } from "jose";
import { departmentForPath, firstAllowedPath, mayView } from "@/lib/access";
import type { Operation } from "@/lib/format";

function secret() {
  return new TextEncoder().encode(process.env.AUTH_SECRET ?? "absher-local-dev-secret");
}

export async function proxy(request: NextRequest) {
  const { pathname } = request.nextUrl;
  // The sign-in page, the framework's own files and the tab icon are served to everyone.
  if (
    pathname.startsWith("/login") ||
    pathname.startsWith("/_next") ||
    pathname === "/favicon.ico" ||
    pathname.startsWith("/icon") ||
    pathname.startsWith("/apple-icon")
  ) {
    return NextResponse.next();
  }
  const session = request.cookies.get("absher_session")?.value;
  if (!session) {
    const url = request.nextUrl.clone();
    url.pathname = "/login";
    return NextResponse.redirect(url);
  }

  // A page hidden from the sidebar must also be refused when the URL is typed by hand.
  const department = departmentForPath(pathname);
  if (!department) return NextResponse.next();
  try {
    const { payload } = await jwtVerify(session, secret());
    const permissions = JSON.parse(String(payload.permissions ?? "{}")) as Record<string, Operation[]>;
    const roleName = String(payload.roleName ?? "");
    if (mayView(permissions, roleName, department)) return NextResponse.next();
    const fallback = firstAllowedPath(permissions, roleName);
    const url = request.nextUrl.clone();
    url.search = "";
    url.pathname = fallback ?? "/login";
    if (fallback) url.searchParams.set("denied", department);
    return NextResponse.redirect(url);
  } catch {
    // Unreadable token: treat as signed out.
    const url = request.nextUrl.clone();
    url.pathname = "/login";
    return NextResponse.redirect(url);
  }
}

export const config = {
  matcher: ["/((?!_next/static|_next/image|favicon.ico).*)"],
};
