import { verifyCsrf, type Context } from "@wrnexus/core";
import { createAuthHttpHandlers } from "../http/index.ts";
import {
  getDefaultAuthEngine,
  getDefaultAuthRouteOptions,
  getDefaultAuthSchemas,
  hasDefaultAuthEngine,
} from "../runtime.ts";

export type AuthHttpHandlers = ReturnType<typeof createAuthHttpHandlers>;
import { AUTH_ROUTE_DEFINITIONS, type AuthHandlerName } from "./definitions.ts";

const ROUTES = Object.fromEntries(
  AUTH_ROUTE_DEFINITIONS.map((definition) => [definition.path, definition]),
) as Readonly<Record<string, (typeof AUTH_ROUTE_DEFINITIONS)[number]>>;

function unavailable(): Response {
  return Response.json(
    {
      ok: false,
      error: "WRN-AUTH-NOT-CONFIGURED",
      message: "Configure auth.engine before serving package auth routes.",
    },
    { status: 503, headers: { "cache-control": "no-store" } },
  );
}

function normalizeRoutePath(value: string): string {
  try {
    return (decodeURIComponent(value).replace(/\/+$/, "") || "/").toLowerCase();
  } catch {
    return (value.replace(/\/+$/, "") || "/").toLowerCase();
  }
}

function requestRoutePath(ctx: Context): string {
  const matchedRoute = ctx.locals.__wrnexusRoute;
  if (typeof matchedRoute === "string" && matchedRoute.startsWith("/api/auth/")) {
    return normalizeRoutePath(matchedRoute);
  }
  try {
    return normalizeRoutePath(new URL(ctx.req.url).pathname);
  } catch {
    return normalizeRoutePath(ctx.url.pathname);
  }
}

function methodNotAllowed(allowed: readonly string[]): Response {
  return Response.json(
    { ok: false, error: "Method Not Allowed" },
    { status: 405, headers: { allow: allowed.join(", "), "cache-control": "no-store" } },
  );
}

function handlersFor(ctx: Context): AuthHttpHandlers | undefined {
  if (!hasDefaultAuthEngine()) return undefined;
  const routeOptions = getDefaultAuthRouteOptions();
  return createAuthHttpHandlers({
    engine: getDefaultAuthEngine(),
    schemas: getDefaultAuthSchemas(),
    baseUrl: routeOptions.baseUrl ?? ctx.url.origin,
    passkey: routeOptions.passkey,
    onSignedIn: routeOptions.onSignedIn,
    onSignedOut: routeOptions.onSignedOut,
    onSessionVerification: routeOptions.onSessionVerification,
  });
}

/** Invoke one concrete handler. Route-specific entry modules use this path. */
export async function invokeAuthHandler(name: AuthHandlerName, ctx: Context): Promise<Response> {
  const handlers = handlersFor(ctx);
  if (!handlers) return unavailable();

  const method = ctx.req.method.toUpperCase();
  const unsafeMethod = method !== "GET" && method !== "HEAD" && method !== "OPTIONS";
  if (unsafeMethod && getDefaultAuthRouteOptions().csrf !== false && !verifyCsrf(ctx)) {
    return Response.json(
      { ok: false, error: "Invalid CSRF token" },
      { status: 403, headers: { "cache-control": "no-store" } },
    );
  }

  try {
    return await handlers[name](ctx);
  } catch (error) {
    const message = error instanceof Error ? error.message : String(error);
    if (message.startsWith("WRN-AUTH-PASSKEY-PROVIDER:")) {
      return Response.json(
        {
          ok: false,
          error: "Passkeys are unavailable",
          code: "passkey-provider-not-configured",
        },
        { status: 503, headers: { "cache-control": "no-store" } },
      );
    }
    console.error(`[wrnexus:auth] ${name} failed`, error);
    return Response.json(
      { ok: false, error: "Authentication request failed" },
      { status: 500, headers: { "cache-control": "no-store" } },
    );
  }
}

/** Backward-compatible dispatcher for applications importing the shared route. */
export async function dispatchAuthRoute(routePath: string, ctx: Context): Promise<Response> {
  const definition = ROUTES[normalizeRoutePath(routePath)];
  if (!definition) {
    return Response.json(
      { ok: false, error: "Not Found" },
      { status: 404, headers: { "cache-control": "no-store" } },
    );
  }
  const method = ctx.req.method.toUpperCase();
  if (!definition.methods.some((allowed) => allowed === method)) {
    return methodNotAllowed(definition.methods);
  }
  return invokeAuthHandler(definition.handler, ctx);
}

export default async function authApi(ctx: Context): Promise<Response> {
  return dispatchAuthRoute(requestRoutePath(ctx), ctx);
}

export const GET = authApi;
export const POST = authApi;
