import { NextResponse } from "next/server";
import { z } from "zod";
import { userLoginSchema } from "@open-mercato/core/modules/auth/data/validators";
import { createRequestContainer } from "@open-mercato/shared/lib/di/container";
import { signJwt } from "@open-mercato/shared/lib/auth/jwt";
import { resolveTranslations } from "@open-mercato/shared/lib/i18n/server";
import { parseBooleanToken } from "@open-mercato/shared/lib/boolean";
import { emitAuthEvent } from "@open-mercato/core/modules/auth/events";
import { rateLimitErrorSchema } from "@open-mercato/shared/lib/ratelimit/helpers";
import { readEndpointRateLimitConfig } from "@open-mercato/shared/lib/ratelimit/config";
import { checkAuthRateLimit, resetAuthRateLimit } from "@open-mercato/core/modules/auth/lib/rateLimitCheck";
import { runCustomRouteAfterInterceptors } from "@open-mercato/shared/lib/crud/custom-route-interceptor";
import { sanitizeRedirectPath } from "@open-mercato/core/modules/auth/lib/safeRedirect";
import { getAppBaseUrl } from "@open-mercato/shared/lib/url";
const loginRateLimitConfig = readEndpointRateLimitConfig("LOGIN", {
  points: 5,
  duration: 60,
  blockDuration: 60,
  keyPrefix: "login"
});
const loginIpRateLimitConfig = readEndpointRateLimitConfig("LOGIN_IP", {
  points: 20,
  duration: 60,
  blockDuration: 60,
  keyPrefix: "login-ip"
});
const metadata = { requireAuth: false };
function parseRequiredRoles(rawValue) {
  return rawValue.split(",").map((value) => value.trim()).filter(Boolean);
}
async function parseLoginForm(req) {
  const rawContentType = req.headers.get("content-type") ?? "";
  const contentType = rawContentType.split(";")[0].trim().toLowerCase();
  try {
    if (contentType === "application/x-www-form-urlencoded") {
      const body = await req.text();
      const params = new URLSearchParams(body);
      const requireRoleRaw2 = String(params.get("requireRole") ?? params.get("role") ?? "").trim();
      return {
        email: String(params.get("email") ?? ""),
        password: String(params.get("password") ?? ""),
        remember: parseBooleanToken(params.get("remember")) === true,
        tenantIdRaw: String(params.get("tenantId") ?? params.get("tenant") ?? "").trim(),
        requiredRoles: requireRoleRaw2 ? parseRequiredRoles(requireRoleRaw2) : [],
        redirectTo: String(params.get("redirect") ?? "")
      };
    }
    const form = await req.formData();
    const requireRoleRaw = String(form.get("requireRole") ?? form.get("role") ?? "").trim();
    return {
      email: String(form.get("email") ?? ""),
      password: String(form.get("password") ?? ""),
      remember: parseBooleanToken(form.get("remember")?.toString()) === true,
      tenantIdRaw: String(form.get("tenantId") ?? form.get("tenant") ?? "").trim(),
      requiredRoles: requireRoleRaw ? parseRequiredRoles(requireRoleRaw) : [],
      redirectTo: String(form.get("redirect") ?? "")
    };
  } catch {
    return {
      email: "",
      password: "",
      remember: false,
      tenantIdRaw: "",
      requiredRoles: [],
      redirectTo: ""
    };
  }
}
async function POST(req) {
  const { translate } = await resolveTranslations();
  const { email, password, remember, tenantIdRaw, requiredRoles, redirectTo } = await parseLoginForm(req);
  const { error: rateLimitError, compoundKey: rateLimitCompoundKey } = await checkAuthRateLimit({
    req,
    ipConfig: loginIpRateLimitConfig,
    compoundConfig: loginRateLimitConfig,
    compoundIdentifier: email
  });
  if (rateLimitError) return rateLimitError;
  const parsed = userLoginSchema.pick({ email: true, password: true, tenantId: true }).safeParse({
    email,
    password,
    tenantId: tenantIdRaw || void 0
  });
  if (!parsed.success) {
    return NextResponse.json({ ok: false, error: translate("auth.login.errors.invalidCredentials", "Invalid credentials") }, { status: 400 });
  }
  const container = await createRequestContainer();
  const auth = container.resolve("authService");
  const tenantId = parsed.data.tenantId ?? null;
  let user = null;
  if (tenantId) {
    user = await auth.findUserByEmailAndTenant(parsed.data.email, tenantId);
  } else {
    const users = await auth.findUsersByEmail(parsed.data.email);
    user = users.length === 1 ? users[0] : null;
  }
  const ok = await auth.verifyPassword(user, parsed.data.password);
  if (!user || !ok) {
    const reason = user?.passwordHash ? "invalid_password" : "invalid_credentials";
    void emitAuthEvent("auth.login.failed", { email: parsed.data.email, reason }).catch(() => void 0);
    return NextResponse.json({ ok: false, error: translate("auth.login.errors.invalidCredentials", "Invalid email or password") }, { status: 401 });
  }
  if (requiredRoles.length) {
    const userRoleNames2 = await auth.getUserRoles(user, tenantId ?? (user.tenantId ? String(user.tenantId) : null));
    const authorized = requiredRoles.some((r) => userRoleNames2.includes(r));
    if (!authorized) {
      return NextResponse.json({ ok: false, error: translate("auth.login.errors.permissionDenied", "Not authorized for this area") }, { status: 403 });
    }
  }
  await auth.updateLastLoginAt(user);
  if (rateLimitCompoundKey) {
    await resetAuthRateLimit(rateLimitCompoundKey, loginRateLimitConfig);
  }
  const resolvedTenantId = tenantId ?? (user.tenantId ? String(user.tenantId) : null);
  const userRoleNames = await auth.getUserRoles(user, resolvedTenantId);
  try {
    const eventBus = container.resolve("eventBus");
    void eventBus.emitEvent("query_index.coverage.warmup", {
      tenantId: resolvedTenantId
    }).catch(() => void 0);
  } catch {
  }
  const rememberMeDays = Number(process.env.REMEMBER_ME_DAYS || "30");
  const accessTokenMaxAgeSeconds = 60 * 60 * 8;
  const sessionExpiresAt = remember ? new Date(Date.now() + rememberMeDays * 24 * 60 * 60 * 1e3) : new Date(Date.now() + accessTokenMaxAgeSeconds * 1e3);
  const { session: loginSession, token: sessionRefreshToken } = await auth.createSession(user, sessionExpiresAt);
  const token = signJwt({
    sub: String(user.id),
    sid: String(loginSession.id),
    tenantId: resolvedTenantId,
    orgId: user.organizationId ? String(user.organizationId) : null,
    email: user.email,
    roles: userRoleNames
  });
  void emitAuthEvent("auth.login.success", { id: String(user.id), email: user.email, tenantId: resolvedTenantId, organizationId: user.organizationId ? String(user.organizationId) : null }).catch(() => void 0);
  const responseData = {
    ok: true,
    token,
    redirect: sanitizeRedirectPath(redirectTo, getAppBaseUrl(req), "/backend")
  };
  if (remember) {
    responseData.refreshToken = sessionRefreshToken;
  }
  const em = container.resolve("em");
  const interceptedResponse = await runCustomRouteAfterInterceptors({
    routePath: "auth/login",
    method: "POST",
    request: {
      method: "POST",
      url: req.url,
      body: {
        email: parsed.data.email,
        tenantId: parsed.data.tenantId ?? void 0,
        remember,
        requireRole: requiredRoles.length > 0 ? requiredRoles : void 0
      },
      headers: Object.fromEntries(req.headers.entries())
    },
    response: {
      statusCode: 200,
      body: responseData,
      headers: {}
    },
    context: {
      em,
      container
    }
  });
  if (!interceptedResponse.ok) {
    return NextResponse.json(interceptedResponse.body, { status: interceptedResponse.statusCode });
  }
  const interceptedBody = interceptedResponse.body;
  const authTokenForCookie = typeof interceptedBody.token === "string" && interceptedBody.token.length > 0 ? interceptedBody.token : token;
  const refreshTokenForCookie = typeof interceptedBody.refreshToken === "string" ? interceptedBody.refreshToken : void 0;
  const res = NextResponse.json(interceptedBody, { status: interceptedResponse.statusCode });
  res.cookies.set("auth_token", authTokenForCookie, { httpOnly: true, path: "/", sameSite: "lax", secure: process.env.NODE_ENV === "production", maxAge: accessTokenMaxAgeSeconds });
  if (remember && refreshTokenForCookie) {
    const expiresAt = new Date(Date.now() + rememberMeDays * 24 * 60 * 60 * 1e3);
    res.cookies.set("session_token", refreshTokenForCookie, { httpOnly: true, path: "/", sameSite: "lax", secure: process.env.NODE_ENV === "production", expires: expiresAt });
  } else if (!remember && authTokenForCookie === token) {
    res.cookies.set("session_token", sessionRefreshToken, { httpOnly: true, path: "/", sameSite: "lax", secure: process.env.NODE_ENV === "production", maxAge: accessTokenMaxAgeSeconds });
  }
  return res;
}
const loginRequestSchema = userLoginSchema.extend({
  password: z.string().min(6).describe("User password"),
  remember: z.enum(["on", "1", "true"]).optional().describe("Persist the session (submit `on`, `1`, or `true`).")
}).describe("Login form payload");
const loginSuccessSchema = z.object({
  ok: z.literal(true),
  token: z.string().describe("JWT token issued for subsequent API calls"),
  redirect: z.string().nullable().describe("Next location the client should navigate to"),
  refreshToken: z.string().optional().describe("Long-lived refresh token for obtaining new access tokens (only present when remember=true)")
});
const loginErrorSchema = z.object({
  ok: z.literal(false),
  error: z.string()
});
const loginMethodDoc = {
  summary: "Authenticate user credentials",
  description: "Validates the submitted credentials and issues a bearer token cookie for subsequent API calls.",
  tags: ["Authentication & Accounts"],
  requestBody: {
    contentType: "application/x-www-form-urlencoded",
    schema: loginRequestSchema,
    description: "Form-encoded payload captured from the login form."
  },
  responses: [
    {
      status: 200,
      description: "Authentication succeeded",
      schema: loginSuccessSchema
    }
  ],
  errors: [
    { status: 400, description: "Validation failed", schema: loginErrorSchema },
    { status: 401, description: "Invalid credentials", schema: loginErrorSchema },
    { status: 403, description: "User lacks required role", schema: loginErrorSchema },
    { status: 429, description: "Too many login attempts", schema: rateLimitErrorSchema }
  ]
};
const openApi = {
  summary: "Authenticate user credentials",
  description: "Accepts login form submissions and manages cookie/session issuance.",
  methods: {
    POST: loginMethodDoc
  }
};
export {
  POST,
  metadata,
  openApi
};
//# sourceMappingURL=login.js.map
