import { NextRequest, NextResponse } from "next/server";
import { prisma } from "@/lib/prisma";

const MAIN_DOMAIN = process.env.MAIN_DOMAIN || "alfrednobel.edudeploy.com";

async function proxyRequest(req: NextRequest) {
  const host = req.headers.get("host") || "";
  const cleanHost = host.split(":")[0];

  if (!cleanHost.endsWith(`.${MAIN_DOMAIN}`)) {
    return NextResponse.json({ error: "Invalid host" }, { status: 404 });
  }

  const subdomain = cleanHost.replace(`.${MAIN_DOMAIN}`, "");

  const instance = await prisma.instance.findUnique({
    where: { id: subdomain },
    include: { node: true },
  });

  if (!instance || !instance.node?.tailscaleIp) {
    return NextResponse.json(
      { error: "Instance not found or not connected" },
      { status: 404 }
    );
  }

  const publicUrl = `https://${cleanHost}`;
  const targetUrl = new URL(req.url);
  // The middleware rewrites /foo to /api/proxy/foo; strip the prefix before forwarding
  let pathname = targetUrl.pathname;
  if (pathname.startsWith("/api/proxy")) {
    pathname = pathname.slice("/api/proxy".length) || "/";
  }
  const upstream = `http://${instance.node.tailscaleIp}:${instance.port}${pathname}${targetUrl.search}`;

  const headers = new Headers(req.headers);
  headers.delete("host");
  headers.set("host", cleanHost);
  headers.set("x-forwarded-host", cleanHost);
  headers.set("x-forwarded-proto", "https");
  headers.set("x-forwarded-port", "443");
  headers.set("x-forwarded-for", req.headers.get("x-forwarded-for") || "unknown");

  const needsBody = req.method !== "GET" && req.method !== "HEAD";
  const upstreamRes = await fetch(upstream, {
    method: req.method,
    headers,
    body: needsBody ? (req.body as BodyInit) : undefined,
    // Node.js fetch requires duplex when forwarding a request body stream
    ...(needsBody ? { duplex: "half" } : {}),
    redirect: "manual",
  });

  const responseHeaders = new Headers(upstreamRes.headers);
  // Remove content-encoding because Next.js/fetch handles decompression automatically
  responseHeaders.delete("content-encoding");
  responseHeaders.delete("content-length");

  // Rewrite any header values that point to localhost or the internal Tailscale address
  const localPatterns = [
    `http://${instance.node.tailscaleIp}:${instance.port}`,
    `https://${instance.node.tailscaleIp}:${instance.port}`,
    `http://localhost:${instance.port}`,
    `https://localhost:${instance.port}`,
    `http://localhost`,
    `https://localhost`,
  ];
  responseHeaders.forEach((value, key) => {
    let newValue = value;
    for (const pattern of localPatterns) {
      newValue = newValue.replaceAll(pattern, publicUrl);
    }
    if (newValue !== value) {
      responseHeaders.set(key, newValue);
    }
  });

  // Sanitize Set-Cookie headers so sessions work through the public domain.
  // WordPress may issue cookies for "localhost" or without Secure flag; fix it.
  const setCookies = responseHeaders.getSetCookie();
  if (setCookies.length > 0) {
    responseHeaders.delete("set-cookie");
    for (let cookie of setCookies) {
      // Drop any Domain=... set for localhost/internal domains
      cookie = cookie.replace(/;\s*Domain=[^;]+/gi, "");
      // Ensure Secure is present for HTTPS public URLs
      if (!/;\s*Secure\b/i.test(cookie)) {
        cookie += "; Secure";
      }
      // Make sure cookies are sent on sub-domain navigations
      if (!/;\s*SameSite\b/i.test(cookie)) {
        cookie += "; SameSite=Lax";
      }
      responseHeaders.append("set-cookie", cookie);
    }
  }

  const contentType = responseHeaders.get("content-type") || "";
  const shouldRewriteBody =
    contentType.includes("text/html") ||
    contentType.includes("text/css") ||
    contentType.includes("application/javascript") ||
    contentType.includes("application/json");

  if (!shouldRewriteBody) {
    return new Response(upstreamRes.body, {
      status: upstreamRes.status,
      statusText: upstreamRes.statusText,
      headers: responseHeaders,
    });
  }

  // For text responses, rewrite localhost/internal URLs to the public URL.
  // Also handle protocol-relative URLs that some WordPress plugins/themes use.
  let body = await upstreamRes.text();
  const localBase = `http://${instance.node.tailscaleIp}:${instance.port}`;
  const localBaseHttps = `https://${instance.node.tailscaleIp}:${instance.port}`;
  const localLocalhostHttp = `http://localhost:${instance.port}`;
  const localLocalhostHttps = `https://localhost:${instance.port}`;
  const localLocalhostPlainHttp = `http://localhost`;
  const localLocalhostPlainHttps = `https://localhost`;
  const localLocalhostProtocolRelative = `//localhost`;
  const localTailscaleProtocolRelative = `//${instance.node.tailscaleIp}:${instance.port}`;

  body = body
    .replaceAll(localBase, publicUrl)
    .replaceAll(localBaseHttps, publicUrl)
    .replaceAll(localLocalhostHttp, publicUrl)
    .replaceAll(localLocalhostHttps, publicUrl)
    .replaceAll(localLocalhostPlainHttp, publicUrl)
    .replaceAll(localLocalhostPlainHttps, publicUrl)
    .replaceAll(localTailscaleProtocolRelative, publicUrl.replace(/^https?:/, ""))
    .replaceAll(localLocalhostProtocolRelative, publicUrl.replace(/^https?:/, ""));

  return new Response(body, {
    status: upstreamRes.status,
    statusText: upstreamRes.statusText,
    headers: responseHeaders,
  });
}

export const GET = proxyRequest;
export const POST = proxyRequest;
export const PUT = proxyRequest;
export const PATCH = proxyRequest;
export const DELETE = proxyRequest;
export const HEAD = proxyRequest;
export const OPTIONS = proxyRequest;
