"use server";

import { redirect } from "next/navigation";
import { prisma } from "@/lib/prisma";
import { createCustomerSession, destroyCustomerSession } from "@/lib/customer-auth";
import { isRateLimited, recordFailedAttempt, clearAttempts } from "@/lib/rate-limit";
import { isValidEmail, linkGuestOrders, sendLoginCode } from "@/lib/customer-otp";

const OTP_GUESS_WINDOW_MS = 10 * 60 * 1000;
const MAX_OTP_GUESSES = 6;

export async function requestOtp(formData: FormData) {
  const email = String(formData.get("email") ?? "").trim().toLowerCase();
  if (!isValidEmail(email)) {
    redirect(`/account/login?mode=otp&error=${encodeURIComponent("Enter a valid email address")}`);
  }

  const customer = await prisma.customer.upsert({
    where: { email },
    update: {},
    create: { email },
  });

  await sendLoginCode(customer);
}

export async function verifyOtp(formData: FormData) {
  const email = String(formData.get("email") ?? "").trim().toLowerCase();
  const code = String(formData.get("code") ?? "").trim();

  const customer = await prisma.customer.findUnique({ where: { email } });
  if (!customer) redirect(`/account/login?mode=otp&error=${encodeURIComponent("Something went wrong — start again")}`);
  if (customer!.blocked) {
    redirect(`/account/login?mode=otp&error=${encodeURIComponent("This account can't sign in — contact us for help")}`);
  }

  const guessIdentifier = `otp:${customer.id}`;
  if (await isRateLimited(guessIdentifier, OTP_GUESS_WINDOW_MS, MAX_OTP_GUESSES)) {
    redirect(
      `/account/login?step=code&email=${encodeURIComponent(email)}&error=${encodeURIComponent(
        "Too many wrong attempts — request a new code"
      )}`
    );
  }

  const otp = await prisma.otpCode.findFirst({
    where: { customerId: customer.id, code, consumedAt: null, expiresAt: { gt: new Date() } },
    orderBy: { createdAt: "desc" },
  });

  if (!otp) {
    await recordFailedAttempt(guessIdentifier);
    redirect(
      `/account/login?step=code&email=${encodeURIComponent(email)}&error=${encodeURIComponent(
        "That code is invalid or has expired"
      )}`
    );
  }

  await clearAttempts(guessIdentifier);
  await prisma.otpCode.update({ where: { id: otp.id }, data: { consumedAt: new Date() } });
  await linkGuestOrders(customer.id, email);
  await createCustomerSession(customer.id);

  // Email is proven at this point (they just verified a code sent to it) — customers who haven't
  // set a password yet get offered one now instead of being asked to re-verify from scratch later.
  const full = await prisma.customer.findUnique({ where: { id: customer.id }, omit: { passwordHash: false } });
  redirect(full?.passwordHash ? "/account?success=Logged+in+successfully." : "/account/complete-profile");
}

export async function logoutCustomer() {
  await destroyCustomerSession();
  redirect("/?success=Logged+out.");
}
