import {
  COOKIE_MAX_AGE,
  DEFAULT_LANGUAGE,
  LANGUAGE_COOKIE_NAME,
  LANGUAGE_HEADER_KEY,
  SUPPORTED_LANGUAGES,
} from "@/lib/i18n/config";
import { NextRequest, NextResponse } from "next/server";
import {
  AUTH_COOKIE_NAME,
  ADMIN_ROLES,
  SUPPLIER_ROLES,
} from "@/lib/constants/auth";
import { checkAccessForRole } from "@/lib/constants/permissions";
import { resolveRouteAccess } from "@/lib/constants/route-access";
import { UserRole } from "@/types/auth";
import { ROUTES } from "@/lib/constants/routes";

// ─── Route Lists ──────────────────────────────────────────────────────────────

const PUBLIC_ROUTES: string[] = [
  ROUTES.HOME,
  // Provider self-registration is a public entry point — reachable without a
  // session and never redirected away (even an authenticated user may open it).
  ROUTES.AUTH.REGISTER_PROVIDER,
];

/** Auth routes — redirect to dashboard if already logged in */
const AUTH_ROUTES = [ROUTES.AUTH.LOGIN, ROUTES.AUTH.FORGOT_PASSWORD];

/** Routes accessible only to Admin roles (prefix match) */
const ADMIN_ROUTE_PREFIXES = ["/admin"];

/** Routes accessible only to Supplier roles (prefix match) */
const SUPPLIER_ROUTE_PREFIXES = ["/provider"];

// ─── Helpers ──────────────────────────────────────────────────────────────────

/** Lightweight JWT payload decode — no signature verification, optimistic only */
function decodeTokenRole(token: string): UserRole | null {
  try {
    const payloadBase64 = token.split(".")[1];
    if (!payloadBase64) return null;
    const padded = payloadBase64.replace(/-/g, "+").replace(/_/g, "/");
    const json = Buffer.from(padded, "base64").toString("utf-8");
    const payload = JSON.parse(json) as { role?: string };
    return (payload.role as UserRole) ?? null;
  } catch {
    return null;
  }
}

function getDashboardForRole(role: UserRole): string {
  if (ADMIN_ROLES.includes(role as any)) return ROUTES.ADMIN.DASHBOARD;
  if (SUPPLIER_ROLES.includes(role as any)) return ROUTES.SUPPLIER.DASHBOARD;
  return ROUTES.AUTH.LOGIN;
}

// ─── Proxy ────────────────────────────────────────────────────────────────────

export default function proxy(request: NextRequest) {
  const { pathname } = request.nextUrl;

  // ── 1. Language handling ───────────────────────────────────────────────────
  // Resolve language from the cookie (single source of truth) and forward it to
  // the render as a request header. This MUST run for every route — including
  // public ones — so the server always renders the correct language on the very
  // first paint (no client-side flip / flash on reload or new-tab open).
  const langCookie = request.cookies.get(LANGUAGE_COOKIE_NAME);
  const language =
    langCookie?.value && SUPPORTED_LANGUAGES.includes(langCookie.value as any)
      ? langCookie.value
      : DEFAULT_LANGUAGE;

  const requestHeaders = new Headers(request.headers);
  requestHeaders.set(LANGUAGE_HEADER_KEY, language);

  /** Allow the request through while forwarding the language header + refreshing the cookie. */
  const allowWithLanguage = () => {
    const response = NextResponse.next({
      request: { headers: requestHeaders },
    });
    response.cookies.set(LANGUAGE_COOKIE_NAME, language, {
      path: "/",
      maxAge: COOKIE_MAX_AGE,
      sameSite: "lax",
    });
    return response;
  };

  // Public routes skip auth logic but still get the language header + cookie.
  if (PUBLIC_ROUTES.includes(pathname)) {
    return allowWithLanguage();
  }

  // ── 2. Read JWT from cookie ────────────────────────────────────────────────
  const token = request.cookies.get(AUTH_COOKIE_NAME)?.value ?? null;
  const role = token ? decodeTokenRole(token) : null;
  const isAuthenticated = !!token && !!role;

  // ── 3. Route guard logic ───────────────────────────────────────────────────

  // Root "/" — landing page: if authenticated redirect to own dashboard
  if (pathname === ROUTES.HOME && isAuthenticated) {
    return NextResponse.redirect(
      new URL(getDashboardForRole(role!), request.url),
    );
  }

  // Auth routes (login, forgot-password): if authenticated redirect to dashboard
  if (AUTH_ROUTES.some((r) => pathname === r || pathname.startsWith(r + "/"))) {
    if (isAuthenticated) {
      return NextResponse.redirect(
        new URL(getDashboardForRole(role!), request.url),
      );
    }
    // Not authenticated → allow, but still apply language header + cookie
    return allowWithLanguage();
  }

  // Admin routes: must be authenticated + have admin role
  if (ADMIN_ROUTE_PREFIXES.some((prefix) => pathname.startsWith(prefix))) {
    if (!isAuthenticated) {
      return NextResponse.redirect(new URL(ROUTES.AUTH.LOGIN, request.url));
    }
    if (!ADMIN_ROLES.includes(role! as any)) {
      // Supplier trying to access admin routes → redirect to supplier dashboard
      return NextResponse.redirect(
        new URL(ROUTES.SUPPLIER.DASHBOARD, request.url),
      );
    }
  }

  // Supplier routes: must be authenticated + have supplier role
  if (SUPPLIER_ROUTE_PREFIXES.some((prefix) => pathname.startsWith(prefix))) {
    if (!isAuthenticated) {
      return NextResponse.redirect(new URL(ROUTES.AUTH.LOGIN, request.url));
    }
    if (!SUPPLIER_ROLES.includes(role! as any)) {
      // Admin trying to access supplier routes → redirect to admin dashboard
      return NextResponse.redirect(
        new URL(ROUTES.ADMIN.DASHBOARD, request.url),
      );
    }
  }

  // ── 4. Fine-grained route (view) access ────────────────────────────────────
  // Panel access is settled above; this enforces the per-route permission/role
  // from ROUTE_ACCESS. Only the role is known here (the JWT has no permission
  // list), so permissions are derived from the role via the interim map — the
  // dashboards are ungated, so the redirect target is always reachable.
  if (isAuthenticated) {
    const routeAccess = resolveRouteAccess(pathname);
    if (routeAccess && !checkAccessForRole(role!, routeAccess)) {
      return NextResponse.redirect(
        new URL(getDashboardForRole(role!), request.url),
      );
    }
  }

  // ── 5. Allow request — apply language header + refresh cookie ──────────────
  return allowWithLanguage();
}

// ─── Matcher ──────────────────────────────────────────────────────────────────
// Run on all routes except Next.js internals, API routes, and static assets
export const config = {
  matcher: [
    "/((?!api|_next/static|_next/image|.*\\.(?:png|jpg|jpeg|gif|svg|ico|webp|woff|woff2|ttf)$).*)",
  ],
};
