/**
 * کد یکبار مصرف پیامکی.
 * - کد ۵ رقمی تصادفی امن، فقط HMAC آن ذخیره می‌شود
 * - اعتبار ۲ دقیقه، حداکثر ۵ تلاش برای هر کد
 * - محدودیت ارسال: یک کد در هر ۶۰ ثانیه و حداکثر ۵ کد در ساعت برای هر شماره
 */
import "server-only";
import { db } from "@/lib/db";
import { hmac, randomDigits, safeEqual } from "@/lib/crypto";
import { sms } from "@/integrations/sms";

export const OTP_TTL_SECONDS = 120;
const RESEND_SECONDS = 60;
const MAX_PER_HOUR = 5;
const MAX_ATTEMPTS = 5;

type Result<T = object> = ({ ok: true } & T) | { ok: false; error: string; retryAfter?: number };

export async function requestOtp(phone: string): Promise<Result<{ code: string }>> {
  const now = Date.now();
  const recent = await db.otpCode.findMany({
    where: { phone, createdAt: { gt: new Date(now - 3_600_000) } },
    orderBy: { createdAt: "desc" },
    select: { createdAt: true },
  });
  if (recent.length >= MAX_PER_HOUR) return { ok: false, error: "تعداد درخواست‌ها زیاد است؛ یک ساعت دیگر تلاش کنید" };
  const sinceLast = recent[0] ? (now - recent[0].createdAt.getTime()) / 1000 : Infinity;
  if (sinceLast < RESEND_SECONDS) {
    const retryAfter = Math.ceil(RESEND_SECONDS - sinceLast);
    return { ok: false, error: `لطفاً ${retryAfter} ثانیه دیگر دوباره تلاش کنید`, retryAfter };
  }

  const code = randomDigits(5);
  await db.otpCode.create({ data: { phone, codeHash: hmac(`${phone}:${code}`), expiresAt: new Date(now + OTP_TTL_SECONDS * 1000) } });
  try {
    await sms().sendOtp(phone, code);
  } catch (err) {
    console.error("[otp] ارسال پیامک ناموفق بود", err);
    return { ok: false, error: "ارسال پیامک ناموفق بود؛ چند لحظه دیگر تلاش کنید" };
  }
  return { ok: true, code };
}

/** بررسی کد و در صورت درستی، ساخت یا پیدا کردن کاربر */
export async function verifyOtp(phone: string, code: string): Promise<Result<{ userId: string }>> {
  const otp = await db.otpCode.findFirst({
    where: { phone, consumedAt: null, expiresAt: { gt: new Date() } },
    orderBy: { createdAt: "desc" },
  });
  if (!otp) return { ok: false, error: "کد منقضی شده است؛ کد جدید بگیرید" };
  if (otp.attempts >= MAX_ATTEMPTS) return { ok: false, error: "تعداد تلاش‌ها زیاد بود؛ کد جدید بگیرید" };

  if (!safeEqual(otp.codeHash, hmac(`${phone}:${code}`))) {
    await db.otpCode.update({ where: { id: otp.id }, data: { attempts: { increment: 1 } } });
    return { ok: false, error: "کد وارد شده صحیح نیست" };
  }

  // مصرف کد به‌صورت اتمیک (جلوگیری از استفاده هم‌زمان دوباره)
  const consumed = await db.otpCode.updateMany({ where: { id: otp.id, consumedAt: null }, data: { consumedAt: new Date() } });
  if (consumed.count === 0) return { ok: false, error: "این کد قبلاً استفاده شده است" };

  const user = await db.user.upsert({ where: { phone }, create: { phone }, update: {} });
  return { ok: true, userId: user.id };
}
