/**
 * Subdomain Routing Suite — pure logic, no Next.js Edge runtime required.
 *
 * Verifies the middleware contract in src/lib/routing/subdomain.ts and the
 * return-origin helper in src/lib/seo/site.ts:
 *  - admin.* → /admin route group, with /api/* and /dashboard* passing through
 *    unchanged (fixes the 404 the admin UI would otherwise hit on its own
 *    subdomain);
 *  - app.* → dashboard surface only; auth/admin/legal redirect to the main
 *    host, everything else is a dashboard sub-route;
 *  - boundary-aware admin path detection (no accidental admin-gating of
 *    /administrator or /api/adminx);
 *  - strict first-party host validation (app.evil.example is never routed);
 *  - originForFirstPartyHost returns the origin the user started on.
 */

import { describe, test, expect } from "./runner";
import { siteUrl, hostIsSiteFirstParty, originForFirstPartyHost } from "../../src/lib/seo/site";
import { decideSubdomain, isAdminPath, isDashboardPath, MAIN_HOST_ONLY_PREFIXES } from "../../src/lib/routing/subdomain";

const siteHost = new URL(siteUrl).host;
const appHost = `app.${siteHost}`;
const adminHost = `admin.${siteHost}`;

describe("Subdomain routing — admin path detection (boundary-aware)", () => {
  test("/admin and /admin/* are admin", () => {
    expect(isAdminPath("/admin")).toBe(true);
    expect(isAdminPath("/admin/")).toBe(true);
    expect(isAdminPath("/admin/users")).toBe(true);
  });

  test("/api/admin and /api/admin/* are admin", () => {
    expect(isAdminPath("/api/admin")).toBe(true);
    expect(isAdminPath("/api/admin/stats")).toBe(true);
    expect(isAdminPath("/api/admin/system/settings")).toBe(true);
  });

  test("prefix lookalikes are NOT admin — no boundary false-positives", () => {
    expect(isAdminPath("/administrator")).toBe(false);
    expect(isAdminPath("/api/adminx")).toBe(false);
    expect(isAdminPath("/api/administrator")).toBe(false);
    expect(isAdminPath("/admin_old")).toBe(false);
  });

  test("non-admin app routes are not admin", () => {
    expect(isAdminPath("/")).toBe(false);
    expect(isAdminPath("/dashboard")).toBe(false);
    expect(isAdminPath("/api/scan")).toBe(false);
    expect(isAdminPath("/api/auth/login")).toBe(false);
  });
});

describe("Subdomain routing — dashboard path detection (boundary-aware)", () => {
  test("/dashboard and /dashboard/* are dashboard", () => {
    expect(isDashboardPath("/dashboard")).toBe(true);
    expect(isDashboardPath("/dashboard/")).toBe(true);
    expect(isDashboardPath("/dashboard/export")).toBe(true);
    expect(isDashboardPath("/dashboard/onboarding")).toBe(true);
  });

  test("prefix lookalikes are NOT dashboard", () => {
    expect(isDashboardPath("/dashboarding")).toBe(false);
    expect(isDashboardPath("/api/dashboard")).toBe(false);
    expect(isDashboardPath("/")).toBe(false);
    expect(isDashboardPath("/auth/login")).toBe(false);
  });
});

describe("Subdomain routing — admin.* branch", () => {
  test("the admin host is first-party", () => {
    expect(hostIsSiteFirstParty(adminHost)).toBe(true);
  });

  test("/admin pages pass through unchanged", () => {
    const d1 = decideSubdomain(adminHost, "/admin");
    const d2 = decideSubdomain(adminHost, "/admin/users");
    expect(d1.kind).toBe("next");
    expect(d2.kind).toBe("next");
  });

  test("/api/* passes through unchanged — the admin UI data calls keep working", () => {
    const d1 = decideSubdomain(adminHost, "/api/admin/stats");
    const d2 = decideSubdomain(adminHost, "/api/admin/users");
    const d3 = decideSubdomain(adminHost, "/api/auth/login");
    expect(d1.kind).toBe("next");
    expect(d2.kind).toBe("next");
    expect(d3.kind).toBe("next");
  });

  test("/dashboard* passes through unchanged — admin shell links to the dashboard", () => {
    const d = decideSubdomain(adminHost, "/dashboard");
    expect(d.kind).toBe("next");
  });

  test("root and unknown paths rewrite under /admin", () => {
    const root = decideSubdomain(adminHost, "/");
    expect(root.kind).toBe("rewrite");
    if (root.kind === "rewrite") expect(root.pathname).toBe("/admin");

    const login = decideSubdomain(adminHost, "/login");
    expect(login.kind).toBe("rewrite");
    if (login.kind === "rewrite") expect(login.pathname).toBe("/admin/login");
  });

  test("a spoofed admin host is never routed", () => {
    expect(decideSubdomain("admin.evil.example", "/").kind).toBe("none");
    expect(decideSubdomain(`admin.${siteHost}.evil.example`, "/").kind).toBe("none");
  });
});

describe("Subdomain routing — app.* branch", () => {
  test("root serves the dashboard", () => {
    const d = decideSubdomain(appHost, "/");
    expect(d.kind).toBe("rewrite");
    if (d.kind === "rewrite") expect(d.pathname).toBe("/dashboard");
  });

  test("dashboard and API pass through unchanged", () => {
    expect(decideSubdomain(appHost, "/dashboard").kind).toBe("next");
    expect(decideSubdomain(appHost, "/dashboard/receipts").kind).toBe("next");
    expect(decideSubdomain(appHost, "/api/scan").kind).toBe("next");
    expect(decideSubdomain(appHost, "/api/admin/stats").kind).toBe("next");
  });

  test("unknown paths become dashboard sub-routes", () => {
    const d = decideSubdomain(appHost, "/projects");
    expect(d.kind).toBe("rewrite");
    if (d.kind === "rewrite") expect(d.pathname).toBe("/dashboard/projects");
  });

  test("main-host-only content redirects to the bare domain", () => {
    for (const prefix of MAIN_HOST_ONLY_PREFIXES) {
      const d = decideSubdomain(appHost, prefix);
      expect(d.kind).toBe("redirect");
      if (d.kind === "redirect") expect(d.pathname).toBe(prefix);
    }
    const login = decideSubdomain(appHost, "/auth/login");
    expect(login.kind).toBe("redirect");
    const termsDeep = decideSubdomain(appHost, "/terms/extra");
    expect(termsDeep.kind).toBe("redirect");
  });

  test("boundary: /authx and /administrator stay dashboard routes, not redirects", () => {
    const authx = decideSubdomain(appHost, "/authx");
    expect(authx.kind).toBe("rewrite");
    if (authx.kind === "rewrite") expect(authx.pathname).toBe("/dashboard/authx");

    const adminish = decideSubdomain(appHost, "/administrator");
    expect(adminish.kind).toBe("rewrite");
    if (adminish.kind === "rewrite") expect(adminish.pathname).toBe("/dashboard/administrator");
  });

  test("a spoofed app host is never routed", () => {
    expect(decideSubdomain("app.evil.example", "/").kind).toBe("none");
    expect(decideSubdomain(`app.${siteHost}.evil.example`, "/").kind).toBe("none");
  });
});

describe("Subdomain routing — non-subdomain hosts", () => {
  test("the bare site host and unrelated hosts get no subdomain decision", () => {
    expect(decideSubdomain(siteHost, "/dashboard").kind).toBe("none");
    expect(decideSubdomain(siteHost, "/").kind).toBe("none");
    expect(decideSubdomain("evil.example", "/").kind).toBe("none");
  });
});

describe("Origin helper — return to the host the user started on", () => {
  test("first-party app./admin. hosts resolve to their own origin", () => {
    const appUrl = originForFirstPartyHost(appHost);
    expect(new URL(appUrl).host).toBe(appHost);
    expect(hostIsSiteFirstParty(new URL(appUrl).host)).toBe(true);

    const adminUrl = originForFirstPartyHost(adminHost);
    expect(new URL(adminUrl).host).toBe(adminHost);
  });

  test("the bare site host keeps the site origin", () => {
    expect(originForFirstPartyHost(siteHost)).toBe(siteUrl);
  });

  test("non-first-party hosts fall back to siteUrl — no open redirect", () => {
    expect(originForFirstPartyHost("evil.example")).toBe(siteUrl);
    expect(originForFirstPartyHost(`app.${siteHost}.evil.example`)).toBe(siteUrl);
    expect(originForFirstPartyHost("")).toBe(siteUrl);
  });

  test("the origin keeps the site scheme but swaps the host", () => {
    const protocol = new URL(siteUrl).protocol;
    expect(originForFirstPartyHost(appHost)).toMatch(new RegExp(`^${protocol}\\/\\/`));
  });
});