import { expect, test } from "bun:test";
import type { Context } from "@wrnexus/core";
import { createAuthEngine } from "../src/engine.ts";
import { setDefaultAuthEngine } from "../src/runtime.ts";
import { MemoryAuthStore } from "../src/stores/memory.ts";
import { POST as requestPasswordReset } from "../src/routes/api/password-request.ts";
import { GET as listSessions } from "../src/routes/api/sessions.ts";
import { POST as passkeyLoginOptions } from "../src/routes/api/passkeys-login-options.ts";

function context(request: Request): Context {
  const values = new Map<string, unknown>();
  return {
    req: request,
    // Deliberately use unrelated values. A route-specific package entry must
    // not infer its endpoint from ctx.url, ctx.req.url, params, or locals.
    url: new URL("https://example.test/__wrnexus/rewritten"),
    params: {},
    locals: {},
    lang: "en",
    t: (key: string) => key,
    ip: "127.0.0.1",
    user: null,
    cookies: {
      get: (name: string) => (name === "wire-csrf" ? "route-csrf-token" : undefined),
    } as Context["cookies"],
    localStorage: {} as Context["localStorage"],
    session: {
      id: () => "route-test-session",
      get: <T>(key: string) => values.get(key) as T | undefined,
      getAll: () => Object.fromEntries(values),
      set: (key: string, value: unknown) => {
        values.set(key, value);
      },
      delete: (key: string) => {
        values.delete(key);
      },
      regenerate: () => {},
      clear: () => {
        values.clear();
      },
    },
  } as Context;
}

test("route-specific password recovery entry cannot fall through to Not Found", async () => {
  setDefaultAuthEngine(
    createAuthEngine({
      store: new MemoryAuthStore(),
      secret: "route-specific-entry-secret-longer-than-thirty-two-characters",
    }),
  );

  const request = new Request("https://example.test/completely/unrelated", {
    method: "POST",
    headers: {
      "content-type": "application/json",
      "x-csrf-token": "route-csrf-token",
    },
    body: JSON.stringify({ identifier: "missing@example.test" }),
  });

  const response = await requestPasswordReset(context(request));
  expect(response.status).toBe(200);
  expect(await response.json()).toEqual({ ok: true });
});

test("package auth routes reject unsafe requests without CSRF verification", async () => {
  const request = new Request("https://example.test/api/auth/password/request", {
    method: "POST",
    headers: { "content-type": "application/json" },
    body: JSON.stringify({ identifier: "missing@example.test" }),
  });
  const response = await requestPasswordReset(context(request));
  expect(response.status).toBe(403);
  expect(await response.json()).toMatchObject({ ok: false, error: "Invalid CSRF token" });
});

test("safe package auth routes do not require a CSRF token", async () => {
  const engine = createAuthEngine({
    store: new MemoryAuthStore(),
    secret: "safe-route-secret-that-is-longer-than-thirty-two-characters",
  });
  const registered = await engine.register({
    email: "sessions@example.test",
    password: "StrongPassword123",
  });
  setDefaultAuthEngine(engine);
  const ctx = context(new Request("https://example.test/api/auth/sessions", { method: "GET" }));
  ctx.user = registered.user!;
  ctx.locals.authUser = registered.user!;

  const response = await listSessions(ctx);
  expect(response.status).toBe(200);
  expect(await response.json()).toMatchObject({ ok: true, sessions: [] });
});

test("passkey routes return a controlled response when no provider is configured", async () => {
  setDefaultAuthEngine(
    createAuthEngine({
      store: new MemoryAuthStore(),
      secret: "missing-passkey-provider-secret-longer-than-thirty-two-characters",
    }),
  );
  const request = new Request("https://example.test/api/auth/passkeys/login/options", {
    method: "POST",
    headers: {
      "content-type": "application/json",
      "x-csrf-token": "route-csrf-token",
    },
    body: JSON.stringify({}),
  });

  const response = await passkeyLoginOptions(context(request));
  expect(response.status).toBe(503);
  expect(await response.json()).toMatchObject({
    ok: false,
    code: "passkey-provider-not-configured",
  });
});
