import crypto from "crypto";
import bcrypt from "bcryptjs";
import { prisma } from "@/lib/prisma";

const LEGACY_SCRYPT_KEYLEN = 64;

export function randomToken(bytes = 32) {
  return crypto.randomBytes(bytes).toString("base64url");
}

export function randomCode(length = 6) {
  const max = 10 ** length;
  return crypto.randomInt(0, max).toString().padStart(length, "0");
}

export function sha256(value: string) {
  return crypto.createHash("sha256").update(value).digest("hex");
}

export function hashIp(value?: string | null) {
  if (!value) return null;
  const pepper = process.env.AUTH_SECRET ?? process.env.ADMIN_SECRET ?? "toolaro-local";
  return sha256(`${pepper}:${value}`);
}

export async function hashPassword(password: string) {
  return bcrypt.hash(password, 12);
}

export async function verifyPassword(password: string, stored?: string | null) {
  if (!stored) return false;
  if (stored.startsWith("$2a$") || stored.startsWith("$2b$") || stored.startsWith("$2y$")) {
    return bcrypt.compare(password, stored);
  }

  // Keep existing local accounts usable if they were created before the bcrypt switch.
  const [algo, salt, hash] = stored.split("$");
  if (algo !== "scrypt" || !salt || !hash) return false;
  const derived = await new Promise<Buffer>((resolve, reject) => {
    crypto.scrypt(password, salt, LEGACY_SCRYPT_KEYLEN, { N: 16384, r: 8, p: 1 }, (err, key) => {
      if (err) reject(err);
      else resolve(key);
    });
  });
  return crypto.timingSafeEqual(Buffer.from(hash, "hex"), derived);
}

export async function checkRateLimit({
  key,
  action,
  limit,
  windowSeconds,
}: {
  key: string;
  action: string;
  limit: number;
  windowSeconds: number;
}) {
  const now = new Date();
  const existing = await prisma.rateLimit.findUnique({
    where: { key_action: { key, action } },
  });

  if (!existing || existing.resetAt <= now) {
    await prisma.rateLimit.upsert({
      where: { key_action: { key, action } },
      update: { count: 1, resetAt: new Date(now.getTime() + windowSeconds * 1000) },
      create: { key, action, count: 1, resetAt: new Date(now.getTime() + windowSeconds * 1000) },
    });
    return { ok: true, remaining: limit - 1 };
  }

  if (existing.count >= limit) {
    return { ok: false, remaining: 0, resetAt: existing.resetAt };
  }

  await prisma.rateLimit.update({
    where: { key_action: { key, action } },
    data: { count: { increment: 1 } },
  });
  return { ok: true, remaining: limit - existing.count - 1 };
}
