import { NextResponse } from "next/server";
import { JWT } from "@/utils/jwt";
import { requestNewToken } from "@/services/auth";
import { verifyRoute } from "./verify.route";
import { postBeingEditing } from "./post.lock";
import { revokeCheck } from "./token.check";
import { headers } from "next/headers";
import { getGeneralSettings } from "@/models/settings";
import { Encryption } from "@/utils/encryption";

export async function validateAdmin(request) {
  const loginUrl = new URL("/login", request.url);
  let response = NextResponse.next();
  const header = await headers();
  const authorization = header.get("authorization")?.split("Bearer ")[1];

  const cookies = Object.fromEntries(
    request.cookies.getAll().map((c) => Object.values(c))
  );
  const jwt = new JWT();

  // add search params
  loginUrl.searchParams.set("redirect", request.nextUrl.pathname);
  loginUrl.searchParams.set("reason", "unauthorized");

  let redirect = NextResponse.redirect(loginUrl);

  // if use API KEY
  if (authorization) {
    const generalSettings = await getGeneralSettings();
    const decryptedData = new Encryption().decrypt(authorization);
    const data = JSON.parse(decryptedData);

    if (data?.OWN_ID !== generalSettings?.CMS_ID) {
      if (request.nextUrl.pathname.startsWith("/api")) {
        return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
      } else return redirect;
    }

    return response;
  }

  // prevent xss
  if (request.nextUrl.pathname.startsWith("/api")) {
    response.headers.set("X-Content-Type-Options", "nosniff");

    redirect = NextResponse.json({ error: "Unauthorized" }, { status: 401 });
  }

  if (!cookies.accessToken && !cookies.refreshToken) return redirect;

  const isValidToken = [
    jwt.checkToken({ access: cookies.accessToken }),
    jwt.checkToken({ refresh: cookies.refreshToken }),
  ].some((item) => item);

  if (!isValidToken) {
    ["accessToken", "refreshToken"].forEach((cookie) =>
      redirect.cookies.delete(cookie)
    );
    return redirect;
  }

  // verify route
  const isAuthorized = verifyRoute(request, cookies.accessToken);
  if (!isAuthorized) {
    const forbidden = NextResponse.redirect(new URL("/forbidden", request.url));

    forbidden.cookies.set("reason", "403");
    return forbidden;
  }

  try {
    const verifyAccessToken = jwt.verifyToken({ access: cookies.accessToken });

    // revoke check
    await revokeCheck(request);

    response = postBeingEditing(request, response, cookies.accessToken);

    return response;
  } catch (error) {
    // console.log({ error: error.name });
    if (error.name === "Forbidden") {
      const forbiddenUrl = new URL("/forbidden", request.url);
      // set search params
      forbiddenUrl.searchParams.set("redirect", request.nextUrl.pathname);

      const forbidden = NextResponse.redirect(forbiddenUrl);
      forbidden.cookies.set("reason", "403");

      return forbidden;
    }

    [("accessToken", "refreshToken")].forEach((cookie) => {
      response.cookies.delete(cookie);
      redirect.cookies.delete(cookie);
    });

    if (error.name === "TokenExpiredError") {
      // rotate refresh token
      try {
        const newToken = await requestNewToken(cookies.refreshToken);

        newToken.cookie.forEach((cookie) => {
          response.cookies.set(cookie);
        });
        response = postBeingEditing(
          request,
          response,
          newToken.token.accessToken
        );

        return response;
      } catch (error) {
        if (Object.keys(cookies).includes("reason"))
          redirect.cookies.delete("reason");
        return redirect;
      }
    }

    return redirect;
  }
}
