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

import { canAccessPath, getDefaultPathForRole, parseSessionCookie, SESSION_COOKIE, type UserRole } from "@/lib/auth";
import { DEFAULT_LOCALE, isLocale, LOCALE_COOKIE } from "@/lib/i18n";
import { getRequestBaseUrl } from "@/lib/request-url";
import { getSessionCookieDomain } from "@/lib/auth";

const AUTH_DEBUG = process.env.AUTH_DEBUG === "true" || process.env.AUTH_DEBUG === "1";

const publicPaths = ["/login", "/forgot-password", "/reset-password", "/unauthorized", "/invite", "/auth"];

function isPublicPath(pathname: string) {
  return publicPaths.some((path) => pathname === path || pathname.startsWith(`${path}/`));
}

function shouldUseSecureCookies(request: NextRequest) {
  const explicit = process.env.COOKIE_SECURE?.toLowerCase() ?? "";
  if (explicit === "true" || explicit === "1") {
    return true;
  }

  if (explicit === "false" || explicit === "0") {
    return false;
  }

  const forwardedProto = request.headers.get("x-forwarded-proto");
  return request.nextUrl.protocol === "https:" || (forwardedProto?.split(",")[0]?.toLowerCase() === "https");
}

function debugAuth(message: string, details: Record<string, unknown> = {}) {
  if (!AUTH_DEBUG) {
    return;
  }

  console.warn(`[AUTH] ${message}`, details);
}

function setDebugHeaders(response: NextResponse, headers: Record<string, string>) {
  if (!AUTH_DEBUG) {
    return;
  }

  Object.entries(headers).forEach(([key, value]) => {
    response.headers.set(key, value);
  });
}

async function decodeSessionCookie(value: string) {
  const parsed = (await parseSessionCookie(value)) as null | { role: UserRole };
  if (!parsed) {
    debugAuth("invalid_session_cookie", {
      cookieLength: value.length,
      cookiePreview: `${value.slice(0, 28)}...${value.slice(-14)}`
    });
  }
  return parsed;
}

export async function middleware(request: NextRequest) {
  const { pathname } = request.nextUrl;
  const baseUrl = getRequestBaseUrl(request);
  const response = NextResponse.next();

  if (pathname.startsWith("/_next") || pathname.includes(".")) {
    return response;
  }

  const sessionCookie = request.cookies.get(SESSION_COOKIE)?.value;
  const session = sessionCookie ? await decodeSessionCookie(sessionCookie) : null;
  const localeCookie = request.cookies.get(LOCALE_COOKIE)?.value;
  const acceptLanguage = request.headers.get("accept-language")?.toLowerCase() || "";

  if (!localeCookie) {
    const detected = acceptLanguage.includes("id") ? "id" : acceptLanguage.includes("en") ? "en" : DEFAULT_LOCALE;
    response.cookies.set(LOCALE_COOKIE, detected, {
      path: "/",
      domain: getSessionCookieDomain(),
      maxAge: 365 * 24 * 60 * 60,
      secure: shouldUseSecureCookies(request),
      sameSite: "lax"
    });
  } else if (!isLocale(localeCookie)) {
    response.cookies.set(LOCALE_COOKIE, DEFAULT_LOCALE, {
      path: "/",
      domain: getSessionCookieDomain(),
      maxAge: 365 * 24 * 60 * 60,
      secure: shouldUseSecureCookies(request),
      sameSite: "lax"
    });
  }

  if (!session && !isPublicPath(pathname) && pathname !== "/") {
    const clientIp =
      request.headers.get("x-forwarded-for")?.split(",")[0]?.trim() ||
      request.headers.get("x-real-ip") ||
      "unknown";

    debugAuth("missing_or_invalid_session", {
      pathname,
      method: request.method,
      ip: clientIp,
      userAgent: request.headers.get("user-agent") || "unknown",
      hasSessionCookie: Boolean(sessionCookie),
      sessionCookieLength: sessionCookie?.length || 0,
      host: request.headers.get("host") || "unknown",
      protocol: request.nextUrl.protocol,
      forwardedProto: request.headers.get("x-forwarded-proto") || "unknown",
      secureCookies: shouldUseSecureCookies(request)
    });

    const loginUrl = new URL("/login", baseUrl);
    loginUrl.searchParams.set("next", pathname);
    return NextResponse.redirect(loginUrl);
  }

  if (session && (pathname === "/" || pathname === "/login")) {
    const requested = request.nextUrl.searchParams.get("next");
    const hasSafeNext = typeof requested === "string" && requested.startsWith("/") && !requested.startsWith("//");
    const nextPath = hasSafeNext ? requested : null;
    const destination = nextPath && canAccessPath(session.role, nextPath) ? nextPath : getDefaultPathForRole(session.role);
    const redirectResponse = NextResponse.redirect(new URL(destination, baseUrl));
    setDebugHeaders(redirectResponse, {
      "X-Auth-Session": "valid",
      "X-Auth-Session-Has-Cookie": String(Boolean(sessionCookie)),
      "X-Auth-Session-Role": session.role,
      "X-Auth-Path": pathname,
      "X-Auth-Base-Url": baseUrl.toString()
    });
    return redirectResponse;
  }

  if (session && !isPublicPath(pathname) && !canAccessPath(session.role, pathname)) {
    debugAuth("role_forbidden", {
      pathname,
      role: session.role
    });
    const forbiddenResponse = NextResponse.redirect(new URL("/unauthorized", baseUrl));
    setDebugHeaders(forbiddenResponse, {
      "X-Auth-Session": "forbidden",
      "X-Auth-Session-Has-Cookie": String(Boolean(sessionCookie)),
      "X-Auth-Session-Role": session.role,
      "X-Auth-Path": pathname,
      "X-Auth-Base-Url": baseUrl.toString()
    });
    return forbiddenResponse;
  }

  setDebugHeaders(response, {
    "X-Auth-Session": session ? "valid" : "missing",
    "X-Auth-Session-Has-Cookie": String(Boolean(sessionCookie)),
    "X-Auth-Session-Valid-Role": session?.role || "n/a",
    "X-Auth-Path": pathname,
    "X-Auth-Base-Url": baseUrl.toString(),
    "X-Auth-Host": request.headers.get("host") || "unknown"
  });

  return response;
}

export const config = {
  matcher: ["/((?!api).*)"]
};
