import { NextRequest, NextResponse } from "next/server";
import { cookies } from "next/headers";
import {
  handleDevAuth,
  handleDevAuthMe,
  shouldFallbackToDevMock,
} from "@/lib/dev-api-mock";
import {
  AUTH_COOKIE_NAME,
  isLegacyDevMockToken,
  setAuthCookie,
  unauthorizedWithClearedCookie,
} from "@/lib/auth-cookie";
import { apiErrorResponse } from "@/lib/api-errors";
import { getOrCreateCorrelationId, withCorrelationId } from "@/lib/correlation-id";
import { checkOtpRateLimit } from "@/lib/otp-rate-limit";
import { fetchUpstream, type UpstreamOptions } from "@/lib/proxy-upstream";

function clientIp(req: NextRequest): string {
  return (
    req.headers.get("x-forwarded-for")?.split(",")[0]?.trim() ||
    req.headers.get("x-real-ip") ||
    "unknown"
  );
}

async function proxyRequest(req: NextRequest, subPath: string) {
  const correlationId = getOrCreateCorrelationId(req);

  if (subPath === "otp" && req.method === "POST") {
    const rateLimit = checkOtpRateLimit(clientIp(req));
    if (!rateLimit.allowed) {
      const response = apiErrorResponse(
        429,
        "RATE_LIMITED",
        "Too many OTP requests. Please try again later.",
        correlationId,
      );
      if (rateLimit.retryAfter) {
        response.headers.set("Retry-After", String(rateLimit.retryAfter));
      }
      return response;
    }
  }

  const jar = await cookies();
  const token = jar.get(AUTH_COOKIE_NAME)?.value;

  if (token && !shouldFallbackToDevMock() && isLegacyDevMockToken(token)) {
    return unauthorizedWithClearedCookie(
      apiErrorResponse(
        401,
        "TOKEN_LEGACY",
        "Your session used the old dev mock. Please sign in again.",
        correlationId,
      ),
    );
  }

  const headers: Record<string, string> = { "Content-Type": "application/json" };
  if (token) {
    headers.Authorization = `Bearer ${token}`;
  }

  const fetchOptions: UpstreamOptions = {
    method: req.method,
    headers,
    correlationId,
  };

  let bodyText = "";
  if (req.method !== "GET" && req.method !== "HEAD") {
    bodyText = await req.text();
    fetchOptions.body = bodyText;
  }

  if (shouldFallbackToDevMock()) {
    if (subPath === "me" && req.method === "GET") {
      if (!token) {
        return apiErrorResponse(401, "UNAUTHORIZED", "Unauthorized", correlationId);
      }
      const result = handleDevAuthMe(token);
      return withCorrelationId(NextResponse.json(result.data, { status: result.status }), correlationId);
    }

    const body = bodyText ? JSON.parse(bodyText) : null;
    const result = handleDevAuth(subPath, req.method, body);

    if (subPath === "verify" && result.status === 200) {
      const data = result.data as { accessToken?: string; user?: unknown };
      if (data.accessToken) {
        const response = NextResponse.json({ user: data.user });
        setAuthCookie(response, data.accessToken);
        return withCorrelationId(response, correlationId);
      }
    }

    return withCorrelationId(NextResponse.json(result.data, { status: result.status }), correlationId);
  }

  const upstream = await fetchUpstream(`/auth/${subPath}`, fetchOptions);

  if (!upstream) {
    return apiErrorResponse(
      503,
      "SERVICE_UNAVAILABLE",
      "Backend service unavailable",
      correlationId,
    );
  }

  const data = await upstream.json().catch(() => ({}));

  if (subPath === "verify" && upstream.ok && data.accessToken) {
    const response = NextResponse.json({
      user: data.user,
    });
    setAuthCookie(response, data.accessToken);
    return withCorrelationId(response, correlationId);
  }

  if (subPath === "me" && upstream.status === 401) {
    return unauthorizedWithClearedCookie(
      withCorrelationId(NextResponse.json(data, { status: upstream.status }), correlationId),
    );
  }

  return withCorrelationId(NextResponse.json(data, { status: upstream.status }), correlationId);
}

export async function GET(req: NextRequest, { params }: { params: Promise<{ path: string[] }> }) {
  const { path } = await params;
  return proxyRequest(req, path.join("/"));
}

export async function POST(req: NextRequest, { params }: { params: Promise<{ path: string[] }> }) {
  const { path } = await params;
  return proxyRequest(req, path.join("/"));
}
