import { NextResponse } from "next/server";
import { findUserByEmail, issueVerificationLink } from "@/lib/auth/accounts";
import { normaliseEmail } from "@/lib/auth/email";
import { equaliseTiming } from "@/lib/auth/password";
import { clientIp, rateLimit } from "@/lib/auth/rateLimit";
import { authError, rateLimited, requireDatabase } from "@/lib/auth/http";
import { requireCsrf } from "@/lib/auth/csrf";
import { isProduction, smtp } from "@/lib/auth/config";
import {
  logSecurityEventAsync,
  SecurityEventType,
} from "@/lib/auth/securityEvents";
import { MAX_JSON_BODY_BYTES } from "@/lib/limits";
import { readJsonSized } from "@/lib/http/requestSize";

export const runtime = "nodejs";
export const dynamic = "force-dynamic";

interface ResendBody {
  email?: string;
  lang?: "de" | "en";
}

/**
 * Re-sends a confirmation link. Always answers "sent" regardless of whether the
 * address exists or is already verified — this endpoint is reachable without
 * credentials, so it must not become an account-existence oracle or a way to
 * spray mail at arbitrary inboxes.
 */
export async function POST(request: Request) {
  const dbDown = await requireDatabase();
  if (dbDown) return dbDown;

  const csrfBlocked = requireCsrf(request);
  if (csrfBlocked) return csrfBlocked;

  // Same refusal for every address, before any lookup: without SMTP the mailer
  // throws in production, and a throw here would be reachable only for existing
  // unverified accounts — turning the endpoint into an existence oracle.
  if (isProduction && !smtp.isConfigured) {
    console.error("[auth] resend blocked — SMTP is not configured in production");
    return authError("mail_failed", 503);
  }

  const parsed = await readJsonSized<ResendBody>(request, MAX_JSON_BODY_BYTES);
  if (!parsed.ok) {
    // Oversized bodies are rejected with 413 before anything is buffered;
    // parse failures keep the existing invalid_request/400 contract.
    return parsed.response.status === 413
      ? parsed.response
      : authError("invalid_request", 400);
  }
  const body = parsed.body;
  if (!body?.email) return authError("invalid_request", 400);

  const ip = clientIp(request);
  const ipLimit = rateLimit(`resend:ip:${ip}`, 5, 60 * 60 * 1000);
  if (!ipLimit.allowed) return rateLimited(ipLimit.retryAfter);

  const emailKey = normaliseEmail(body.email);
  if (!emailKey) return NextResponse.json({ status: "verification_sent" });

  const emailLimit = rateLimit(`resend:email:${emailKey}`, 3, 60 * 60 * 1000);
  if (!emailLimit.allowed) return rateLimited(emailLimit.retryAfter);

  const lang = body.lang === "de" ? "de" : "en";

  try {
    const user = await findUserByEmail(body.email);
    if (!user || user.emailVerifiedAt) {
      // Missing or already-verified account: same shape, and burn one
      // scrypt's worth of time so this faster branch cannot be timed apart
      // from the mail-issuing path below (same pattern as signup/login/forgot-password).
      await equaliseTiming();
      return NextResponse.json({ status: "verification_sent" });
    }

    const mail = await issueVerificationLink(user, lang);
    logSecurityEventAsync({
      type: SecurityEventType.EMAIL_VERIFICATION_RESENT,
      userId: user.id,
      email: user.email ?? emailKey,
      ip,
      userAgent: request.headers.get("user-agent"),
      metadata: { route: "/api/auth/resend-verification" },
    });
    return NextResponse.json({ status: "verification_sent", devLink: mail.devLink });
  } catch (error) {
    console.error("[auth] resend verification failed", error);
    return authError("mail_failed", 500);
  }
}
