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

import { applyCorsHeadersForRequest } from "./cors";

type UpstreamSource =
  | "ATHENA_AUTH_ORIGIN"
  | "NEXT_PUBLIC_AUTH_BASE_URL"
  | "ATHENA_AUTH_ORIGIN_MAP"
  | "development_default";

export type UpstreamResolution =
  | {
      ok: true;
      source: UpstreamSource;
      upstreamBase: URL;
    }
  | {
      ok: false;
      response: Response;
    };

export type UpstreamInspection =
  | {
      ok: true;
      source: UpstreamSource;
      upstreamBase: URL;
      upstreamRaw: string;
    }
  | {
      ok: false;
      code: string;
      message: string;
      details: Record<string, unknown>;
    };

export type OriginMapInspection = {
  configured: boolean;
  requestHost: string | null;
  entries: Array<{
    hostPattern: string;
    upstream: string;
    matchesRequestHost: boolean;
  }>;
  matched: {
    hostPattern: string;
    upstream: string;
  } | null;
};

function configErrorResponse(
  request: NextRequest,
  code: string,
  message: string,
  details: Record<string, unknown> = {},
): Response {
  const headers = new Headers();
  applyCorsHeadersForRequest(request, headers);
  return Response.json(
    {
      code,
      message,
      ...details,
    },
    {
      status: 500,
      headers,
    },
  );
}

function resolveRawUpstream(): { raw: string | null; source: UpstreamSource | null } {
  const fromAthenaOrigin = process.env.ATHENA_AUTH_ORIGIN?.trim();
  if (fromAthenaOrigin) {
    return { raw: fromAthenaOrigin, source: "ATHENA_AUTH_ORIGIN" };
  }

  const fromPublicBase = process.env.NEXT_PUBLIC_AUTH_BASE_URL?.trim();
  if (fromPublicBase) {
    return { raw: fromPublicBase, source: "NEXT_PUBLIC_AUTH_BASE_URL" };
  }

  if (process.env.NODE_ENV !== "production") {
    return {
      raw: "http://localhost:3000",
      source: "development_default",
    };
  }

  return { raw: null, source: null };
}

function escapeRegex(value: string): string {
  return value.replace(/[|\\{}()[\]^$+?.]/g, "\\$&");
}

function globToRegExp(pattern: string): RegExp {
  const source = pattern
    .split("*")
    .map((part) => escapeRegex(part))
    .join(".*");
  return new RegExp(`^${source}$`, "i");
}

function normalizeHostname(value: string): string {
  return value.trim().toLowerCase();
}

function stripPathQueryFragment(value: string): string {
  const schemeIndex = value.indexOf("://");
  const authorityStart = schemeIndex >= 0 ? schemeIndex + 3 : 0;
  const suffix = value.slice(authorityStart);
  const authorityEnd = suffix.search(/[/?#]/);
  if (authorityEnd < 0) return value;
  return value.slice(0, authorityStart + authorityEnd);
}

function normalizeHostPattern(value: string): string {
  const trimmed = stripPathQueryFragment(value.trim());
  const withoutScheme = trimmed.includes("://") ? trimmed.slice(trimmed.indexOf("://") + 3) : trimmed;
  const withoutUserInfo = withoutScheme.split("@").pop() ?? withoutScheme;
  return normalizeHostname(withoutUserInfo);
}

function getRequestHostname(request: NextRequest): string {
  const forwardedHost = request.headers.get("x-forwarded-host")?.trim();
  const hostHeader = request.headers.get("host")?.trim();
  const raw = forwardedHost || hostHeader || "";
  return normalizeHostname(raw.split(",")[0] || "");
}

function parseOriginMap(raw: string | undefined): Array<{ hostPattern: string; upstream: string }> {
  if (!raw) return [];
  return raw
    .split(",")
    .map((entry) => entry.trim())
    .filter(Boolean)
    .map((entry) => {
      const separatorIndex = entry.indexOf("=");
      if (separatorIndex <= 0 || separatorIndex >= entry.length - 1) return null;
      const hostPattern = normalizeHostPattern(entry.slice(0, separatorIndex));
      const upstream = entry.slice(separatorIndex + 1).trim();
      if (!hostPattern || !upstream) return null;
      return { hostPattern, upstream };
    })
    .filter((entry): entry is { hostPattern: string; upstream: string } => entry !== null);
}

function resolveUpstreamFromMap(request: NextRequest): string | null {
  const originMap = parseOriginMap(process.env.ATHENA_AUTH_ORIGIN_MAP);
  if (originMap.length === 0) return null;

  const requestHost = getRequestHostname(request);
  if (!requestHost) return null;

  for (const mapping of originMap) {
    if (globToRegExp(mapping.hostPattern).test(requestHost)) {
      return mapping.upstream;
    }
  }
  return null;
}

export function inspectOriginMapForRequest(request?: NextRequest): OriginMapInspection {
  const originMap = parseOriginMap(process.env.ATHENA_AUTH_ORIGIN_MAP);
  const requestHost = request ? getRequestHostname(request) : null;
  const entries = originMap.map((mapping) => ({
    ...mapping,
    matchesRequestHost: requestHost
      ? globToRegExp(mapping.hostPattern).test(requestHost)
      : false,
  }));
  const matched = entries.find((entry) => entry.matchesRequestHost) ?? null;

  return {
    configured: Boolean(process.env.ATHENA_AUTH_ORIGIN_MAP?.trim()),
    requestHost: requestHost || null,
    entries,
    matched: matched
      ? {
          hostPattern: matched.hostPattern,
          upstream: matched.upstream,
        }
      : null,
  };
}

function isPrivateIpv4Host(hostname: string): boolean {
  const parts = hostname.split(".").map((value) => Number.parseInt(value, 10));
  if (parts.length !== 4 || parts.some((value) => Number.isNaN(value) || value < 0 || value > 255)) {
    return false;
  }

  const [a, b] = parts;
  if (a === 10) return true;
  if (a === 127) return true;
  if (a === 192 && b === 168) return true;
  if (a === 169 && b === 254) return true;
  if (a === 172 && b >= 16 && b <= 31) return true;
  return false;
}

function isLoopbackOrPrivateHost(hostname: string): boolean {
  const host = hostname.trim().toLowerCase();
  if (!host) return false;

  if (host === "localhost" || host === "0.0.0.0" || host === "::1") return true;
  if (host.endsWith(".localhost") || host.endsWith(".local")) return true;
  if (host === "host.docker.internal") return true;
  if (host.startsWith("127.")) return true;
  if (isPrivateIpv4Host(host)) return true;
  if (host.startsWith("fc") || host.startsWith("fd")) return true;
  if (host.startsWith("fe80:")) return true;

  return false;
}

export function resolveUpstreamForProxy(request: NextRequest): UpstreamResolution {
  const inspection = inspectUpstreamConfig(request);
  if (inspection.ok) {
    return {
      ok: true,
      source: inspection.source,
      upstreamBase: inspection.upstreamBase,
    };
  }

  return {
    ok: false,
    response: configErrorResponse(
      request,
      inspection.code,
      inspection.message,
      inspection.details,
    ),
  };
}

export function inspectUpstreamConfig(request?: NextRequest): UpstreamInspection {
  const mappedUpstream = request ? resolveUpstreamFromMap(request) : null;
  const resolved = mappedUpstream
    ? { raw: mappedUpstream, source: "ATHENA_AUTH_ORIGIN_MAP" as UpstreamSource }
    : resolveRawUpstream();
  if (!resolved.raw || !resolved.source) {
    return {
      ok: false,
      code: "UPSTREAM_CONFIG_MISSING",
      message:
        "Missing upstream auth origin. Set ATHENA_AUTH_ORIGIN (preferred), NEXT_PUBLIC_AUTH_BASE_URL, or ATHENA_AUTH_ORIGIN_MAP to a concrete auth server URL.",
      details: {
        hint: "TRUSTED_ORIGINS only controls origin allowlisting and does not set proxy upstream.",
      },
    };
  }

  if (resolved.raw.includes("*")) {
    return {
      ok: false,
      code: "UPSTREAM_CONFIG_INVALID",
      message: `Invalid upstream URL in ${resolved.source}: wildcard '*' is not allowed for proxy upstream.`,
      details: {
        upstream: resolved.raw,
        hint: "Use wildcard patterns only in TRUSTED_ORIGINS. ATHENA_AUTH_ORIGIN must be a concrete URL, for example https://auth.example.com.",
      },
    };
  }

  let parsed: URL;
  try {
    parsed = new URL(resolved.raw);
  } catch (error) {
    return {
      ok: false,
      code: "UPSTREAM_CONFIG_INVALID",
      message: `Invalid upstream URL in ${resolved.source}.`,
      details: {
        upstream: resolved.raw,
        detail: error instanceof Error ? error.message : "Invalid URL value",
      },
    };
  }

  if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
    return {
      ok: false,
      code: "UPSTREAM_CONFIG_INVALID",
      message: `Unsupported upstream URL protocol '${parsed.protocol}' in ${resolved.source}.`,
      details: {
        upstream: resolved.raw,
        hint: "Only http:// and https:// are supported.",
      },
    };
  }

  const isProductionRuntime = process.env.NODE_ENV === "production";
  if (isProductionRuntime && isLoopbackOrPrivateHost(parsed.hostname)) {
    return {
      ok: false,
      code: "UPSTREAM_CONFIG_INVALID",
      message: `Invalid upstream host '${parsed.hostname}' in ${resolved.source} for production runtime.`,
      details: {
        upstream: resolved.raw,
        hint: "Use a publicly reachable auth host in deployment. Loopback/private hosts (localhost, 127.*, 10.*, 192.168.*, etc.) are not reachable from Vercel functions.",
      },
    };
  }

  return {
    ok: true,
    source: resolved.source,
    upstreamBase: parsed,
    upstreamRaw: resolved.raw,
  };
}
