import { describe, expect, it } from "vitest";
import {
  PUBLIC_ALLOWLIST,
  filePathToRoute,
  findUndeclaredRoutes,
  discoverAppRoutes,
} from "../support/route-scan";
import {
  DECLARED_ROUTE_TEMPLATES,
  ROUTES,
} from "../e2e/routes.manifest";

/**
 * Driftschutz für das Routen-Manifest (Definition of Done #1): jede neue Route
 * unter src/app/** muss im Manifest geführt (oder explizit öffentlich) sein,
 * sonst bleibt sie ungetestet im Auth-Gating. Reine Logik, offline lauffähig.
 */
describe("filePathToRoute", () => {
  it("mappt page.tsx einer Route-Group auf den URL-Pfad ohne Gruppe", () => {
    expect(filePathToRoute("src/app/(app)/fahrzeuge/page.tsx")).toBe(
      "/fahrzeuge",
    );
  });

  it("mappt die Wurzel-Page der (app)-Gruppe auf /", () => {
    expect(filePathToRoute("src/app/(app)/page.tsx")).toBe("/");
  });

  it("ersetzt dynamische Segmente durch einen Platzhalter", () => {
    expect(filePathToRoute("src/app/(app)/fahrzeuge/[id]/page.tsx")).toBe(
      "/fahrzeuge/[id]",
    );
  });

  it("mappt route.ts auf den API-Pfad", () => {
    expect(filePathToRoute("src/app/api/health/route.ts")).toBe("/api/health");
  });

  it("mappt die Root-Page src/app/page.tsx auf /", () => {
    expect(filePathToRoute("src/app/page.tsx")).toBe("/");
  });

  it("löst catch-all-Segmente auf", () => {
    expect(filePathToRoute("src/app/api/auth/[...nextauth]/route.ts")).toBe(
      "/api/auth/[...nextauth]",
    );
  });
});

describe("findUndeclaredRoutes", () => {
  it("flaggt eine Route, die weder im Manifest noch öffentlich ist", () => {
    const declared = new Set(["/fahrzeuge"]);
    const discovered = ["/fahrzeuge", "/leak"];
    expect(findUndeclaredRoutes(discovered, declared)).toEqual(["/leak"]);
  });

  it("ignoriert Routen mit öffentlichem Präfix", () => {
    const declared = new Set<string>();
    const discovered = ["/login", "/api/health", "/api/auth/[...nextauth]"];
    expect(findUndeclaredRoutes(discovered, declared)).toEqual([]);
  });

  it("flaggt Routen mit reinem String-Präfix als ungetestet (kein Pfadsegment)", () => {
    const declared = new Set<string>();
    // "/loginhelp" beginnt zwar mit "/login" und "/api/healthz" mit
    // "/api/health", sind aber KEINE Unterpfade -> müssen gegated werden.
    const discovered = ["/loginhelp", "/api/healthz", "/api/authentication"];
    expect(findUndeclaredRoutes(discovered, declared)).toEqual([
      "/loginhelp",
      "/api/healthz",
      "/api/authentication",
    ]);
  });

  it("PUBLIC_ALLOWLIST enthält /api/health und /login", () => {
    expect(PUBLIC_ALLOWLIST).toContain("/api/health");
    expect(PUBLIC_ALLOWLIST).toContain("/login");
  });
});

describe("discoverAppRoutes (echtes Repo)", () => {
  it("findet die bekannten Seiten", () => {
    const routes = discoverAppRoutes();
    expect(routes).toContain("/fahrzeuge");
    expect(routes).toContain("/admin");
    expect(routes).toContain("/api/health");
  });
});

describe("Driftschutz: Manifest deckt alle Routen ab (Definition of Done #1)", () => {
  it("KEINE Route unter src/app/** fehlt im Manifest oder in der Allowlist", () => {
    const discovered = discoverAppRoutes();
    const undeclared = findUndeclaredRoutes(
      discovered,
      DECLARED_ROUTE_TEMPLATES,
    );
    expect(
      undeclared,
      `Ungetestete Routen (im Manifest ergänzen oder als öffentlich markieren):\n${undeclared.join(
        "\n",
      )}`,
    ).toEqual([]);
  });

  it("jeder ROUTES-Eintrag entspricht einer existierenden Route-Vorlage", () => {
    const discovered = new Set(discoverAppRoutes());
    for (const template of DECLARED_ROUTE_TEMPLATES) {
      expect(discovered, `Manifest-Eintrag ${template} fehlt im Dateisystem`).toContain(
        template,
      );
    }
    // Sanity: jede konkrete ROUTES-Zeile ist nicht leer.
    for (const r of ROUTES) expect(r.path.startsWith("/")).toBe(true);
  });
});
