import { globSync } from "node:fs";
import { dirname, resolve } from "node:path";
import { fileURLToPath } from "node:url";

/**
 * Routen-Discovery aus dem Dateisystem für den Driftschutz (Definition of
 * Done #1). Reine Logik, offline lauffähig (kein Server/DB).
 */

const REPO_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), "..", "..");

/**
 * Öffentliche Präfixe (Middleware-Allowlist). Routen, die mit einem dieser
 * Präfixe beginnen, brauchen KEINEN Manifest-Eintrag, weil sie absichtlich
 * anonym erreichbar sind.
 */
export const PUBLIC_ALLOWLIST: readonly string[] = [
  "/login",
  "/api/auth",
  "/api/health",
  "/_next",
  "/favicon.ico",
  "/robots.txt",
];

/**
 * Wandelt einen Datei-Pfad (page.tsx | route.ts) in den zugehörigen URL-Pfad
 * um. Route-Groups `(name)` werden entfernt, dynamische Segmente `[id]`
 * bleiben als Platzhalter erhalten.
 */
export function filePathToRoute(filePath: string): string {
  const withoutPrefix = filePath
    .replace(/^.*src\/app\//, "")
    .replace(/^(page|route)\.(tsx?|jsx?)$/, "")
    .replace(/\/(page|route)\.(tsx?|jsx?)$/, "");
  const segments = withoutPrefix
    .split("/")
    .filter((seg) => seg.length > 0 && !/^\(.*\)$/.test(seg));
  return "/" + segments.join("/");
}

function isPublic(route: string): boolean {
  // Nur exakter Treffer oder echtes Unterpfad-Segment zählt als öffentlich.
  // KEIN reiner String-Präfix: sonst wären z. B. "/loginhelp" oder
  // "/api/healthz" fälschlich anonym erreichbar und entkämen dem Drift-Check.
  return PUBLIC_ALLOWLIST.some(
    (p) => route === p || route.startsWith(p + "/"),
  );
}

/**
 * Findet Routen, die im Dateisystem existieren, aber weder im Manifest
 * deklariert noch öffentlich sind.
 */
export function findUndeclaredRoutes(
  discovered: readonly string[],
  declared: ReadonlySet<string>,
): string[] {
  return discovered
    .filter((route) => !isPublic(route))
    .filter((route) => !declared.has(route));
}

/** Scannt src/app/** nach page.tsx und route.ts und liefert URL-Pfade. */
export function discoverAppRoutes(): string[] {
  const files = globSync("src/app/**/{page,route}.{ts,tsx}", {
    cwd: REPO_ROOT,
  });
  const routes = new Set<string>();
  for (const f of files) routes.add(filePathToRoute(f));
  return [...routes].sort();
}
