import { expect, test } from "bun:test";
import { createPluginRunner } from "@wrnexus/plugin";
import { authPlugin } from "../src/plugin.ts";
import { AUTH_ROUTE_DEFINITIONS } from "../src/routes/definitions.ts";
import { readFileSync } from "node:fs";
import { getDefaultAuthRouteOptions } from "../src/runtime.ts";

test("plugin contributes components, runtime, styles, migration, and toolbar", async () => {
  const metadata = new Map<string, unknown>();
  const runner = createPluginRunner(
    authPlugin({ includeRoutes: true, includeMigrations: true, includeMiddleware: true }),
    {
      root: process.cwd(),
      mode: "development",
      command: "dev",
      metadata,
      warn() {},
    },
  );
  const contributions = await runner.contributions();
  expect(contributions.componentDirs).toHaveLength(1);
  expect(contributions.clientRuntimes[0]).toMatchObject({ id: "auth", singleton: true });
  expect(contributions.styles[0]?.id).toBe("auth-components");
  expect(contributions.migrations.map((migration) => migration.id)).toEqual([
    "wrnexus-auth-001",
    "wrnexus-auth-002-otp-purpose",
  ]);
  expect(contributions.routes.length).toBeGreaterThanOrEqual(30);
  expect(contributions.middleware).toHaveLength(1);
});

test("unconfigured automatic discovery fails closed for routes, middleware, and migrations", async () => {
  const metadata = new Map<string, unknown>();
  const runner = createPluginRunner(authPlugin(), {
    root: process.cwd(),
    mode: "development",
    command: "dev",
    metadata,
    warn() {},
  });

  const contributions = await runner.contributions();
  expect(contributions.routes).toHaveLength(0);
  expect(contributions.middleware).toHaveLength(0);
  expect(contributions.migrations).toHaveLength(0);
  expect(contributions.componentDirs).toHaveLength(1);
  expect(contributions.clientRuntimes).toHaveLength(1);
});

test("config.auth controls route groups and migrations without explicit plugin options", async () => {
  const metadata = new Map<string, unknown>();
  const runner = createPluginRunner(authPlugin(), {
    root: process.cwd(),
    mode: "development",
    command: "dev",
    metadata,
    warn() {},
  });
  await runner.configure({
    auth: {
      engine: {} as never,
      migrations: false,
      routes: { enabled: true, registration: false, passkeys: false },
    },
  });
  const contributions = await runner.contributions();
  expect(contributions.migrations).toHaveLength(0);
  expect(contributions.routes.some((route) => route.path === "/api/auth/register")).toBe(false);
  expect(contributions.routes.some((route) => route.path.includes("/passkeys/"))).toBe(false);
  expect(contributions.routes.some((route) => route.path === "/api/auth/login")).toBe(true);
});

test("config.auth resolves navigation hooks from the configured engine", async () => {
  const onSignedIn = () => new Response(null, { status: 204 });
  const onSignedOut = () => new Response(null, { status: 204 });
  const runner = createPluginRunner(authPlugin(), {
    root: process.cwd(),
    mode: "development",
    command: "dev",
    metadata: new Map(),
    warn() {},
  });

  await runner.configure({
    auth: {
      engine: { onSignedIn, onSignedOut } as never,
      routes: true,
    },
  });

  expect(getDefaultAuthRouteOptions().onSignedIn).toBe(onSignedIn);
  expect(getDefaultAuthRouteOptions().onSignedOut).toBe(onSignedOut);
});

test("auth runtime contains built-in browser schemas", async () => {
  const metadata = new Map<string, unknown>();
  const runner = createPluginRunner(authPlugin({ includeMigrations: false }), {
    root: process.cwd(),
    mode: "development",
    command: "dev",
    metadata,
    warn() {},
  });
  const contributions = await runner.contributions();
  expect(contributions.clientRuntimes[0]?.source).toContain("auth-password-request");
  expect(contributions.clientRuntimes[0]?.source).toContain("auth-register");
});

test("each package auth route uses a route-specific entry module", async () => {
  const metadata = new Map<string, unknown>();
  const runner = createPluginRunner(
    authPlugin({
      includeRoutes: true,
      includeMigrations: false,
      includeMiddleware: false,
    }),
    {
      root: process.cwd(),
      mode: "development",
      command: "dev",
      metadata,
      warn() {},
    },
  );

  const contributions = await runner.contributions();
  const entries = contributions.routes.map((route) => route.entry);
  const passwordRequest = contributions.routes.find(
    (route) => route.path === "/api/auth/password/request",
  );

  expect(new Set(entries).size).toBe(entries.length);
  expect(
    passwordRequest?.entry.replace(/\\/g, "/").endsWith("/src/routes/api/password-request.ts"),
  ).toBe(true);
  for (const definition of AUTH_ROUTE_DEFINITIONS) {
    const route = contributions.routes.find((item) => item.path === definition.path);
    expect(route).toBeDefined();
    const source = readFileSync(route!.entry, "utf8");
    expect(source).toContain(`invokeAuthHandler("${definition.handler}"`);
    expect(source.includes("dispatchAuthRoute")).toBe(false);
    for (const method of definition.methods) {
      expect(source).toContain(`export function ${method}`);
    }
  }
});
test("config.auth registers package routes", async () => {
  const metadata = new Map<string, unknown>();

  const runner = createPluginRunner(authPlugin(), {
    root: process.cwd(),
    mode: "development",
    command: "dev",
    metadata,
    warn() {},
  });

  await runner.configure({
    auth: {
      engine: {} as never,
      routes: true,
      middleware: true,
      migrations: false,
    },
  });

  const contributions = await runner.contributions();

  expect(contributions.routes.some((route) => route.path === "/api/auth/password/request")).toBe(
    true,
  );

  expect(contributions.routes.some((route) => route.path === "/api/auth/register")).toBe(true);

  expect(contributions.routes.some((route) => route.path === "/api/auth/login")).toBe(true);

  expect(contributions.middleware).toHaveLength(1);

  expect(contributions.migrations).toHaveLength(0);
});
test("config.auth contributes a reusable forward-auth verification route", async () => {
  const runner = createPluginRunner(authPlugin(), {
    root: process.cwd(),
    mode: "development",
    command: "dev",
    metadata: new Map<string, unknown>(),
    warn() {},
  });
  await runner.configure({
    auth: {
      engine: {} as never,
      routes: true,
    },
  });
  const contributions = await runner.contributions();
  expect(contributions.routes.some((route) => route.path === "/api/auth/session/verify")).toBe(
    true,
  );
});

test("config.auth can disable route groups", async () => {
  const metadata = new Map<string, unknown>();

  const runner = createPluginRunner(authPlugin(), {
    root: process.cwd(),
    mode: "development",
    command: "dev",
    metadata,
    warn() {},
  });

  await runner.configure({
    auth: {
      engine: {} as never,
      migrations: false,
      routes: {
        enabled: true,
        password: true,
        passkeys: false,
      },
    },
  });

  const contributions = await runner.contributions();

  expect(contributions.routes.some((route) => route.path === "/api/auth/password/request")).toBe(
    true,
  );

  expect(contributions.routes.some((route) => route.path.includes("/passkeys/"))).toBe(false);
});
