import crypto from "crypto";
import { redirect } from "next/navigation";
import { prisma } from "@/lib/prisma";
import { sendOtpEmail } from "@/lib/resend";

const OTP_TTL_MS = 10 * 60 * 1000;
const RESEND_COOLDOWN_MS = 60 * 1000;

export function isValidEmail(email: string) {
  return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
}

/**
 * Claims past guest-checkout orders for this customer. Order.email isn't normalized at checkout time
 * (whatever case the guest typed), so this compares case-insensitively in JS rather than relying on an
 * exact-match DB query, which would silently miss orders whose stored email differs only in casing.
 */
export async function linkGuestOrders(customerId: string, normalizedEmail: string) {
  const unlinked = await prisma.order.findMany({ where: { customerId: null }, select: { id: true, email: true } });
  const matchingIds = unlinked.filter((o) => o.email.trim().toLowerCase() === normalizedEmail).map((o) => o.id);
  if (matchingIds.length > 0) {
    await prisma.order.updateMany({ where: { id: { in: matchingIds } }, data: { customerId } });
  }
}

/**
 * Generates, stores, and emails a fresh sign-in code, then redirects to the code-entry step.
 * Shared by OTP-only sign-in (requestOtp) and password registration (which also needs to prove
 * the customer owns the email before their new password can be used to sign in).
 *
 * Deliberately NOT exported from a "use server" file: every export of such a file becomes a
 * client-callable RPC endpoint regardless of who's meant to call it, and this takes a trusted
 * {id, email, blocked} object rather than re-deriving it from an authenticated identity — a
 * caller could otherwise fabricate any customerId/email pair and trigger a code to it directly.
 */
export async function sendLoginCode(customer: { id: string; email: string; blocked: boolean }): Promise<never> {
  if (customer.blocked) {
    redirect(`/account/login?mode=otp&error=${encodeURIComponent("This account can't sign in — contact us for help")}`);
  }

  const recent = await prisma.otpCode.findFirst({
    where: { customerId: customer.id },
    orderBy: { createdAt: "desc" },
  });
  if (recent && Date.now() - recent.createdAt.getTime() < RESEND_COOLDOWN_MS) {
    redirect(
      `/account/login?step=code&email=${encodeURIComponent(customer.email)}&error=${encodeURIComponent(
        "Please wait a minute before requesting another code"
      )}`
    );
  }

  const code = crypto.randomInt(100000, 1000000).toString();
  await prisma.otpCode.create({
    data: { customerId: customer.id, code, expiresAt: new Date(Date.now() + OTP_TTL_MS) },
  });

  try {
    await sendOtpEmail(customer.email, code);
  } catch {
    redirect(`/account/login?mode=otp&error=${encodeURIComponent("Couldn't send the code — please try again in a moment")}`);
  }

  redirect(`/account/login?step=code&email=${encodeURIComponent(customer.email)}`);
}
