import crypto from 'crypto';
import prisma from './prisma.js';
import { env } from '../config/env.js';

const OTP_TTL_MS = 60 * 1000; // codes are valid for 1 minute
const MAX_ATTEMPTS = 5;

function hashCode(code) {
  return crypto.createHmac('sha256', env.jwtSecret).update(code).digest('hex');
}

function generateCode() {
  // 6-digit numeric code, zero-padded.
  return String(crypto.randomInt(0, 1_000_000)).padStart(6, '0');
}

// Create a fresh OTP, invalidating any previous outstanding codes for this email+purpose.
export async function createOtp(email, purpose) {
  const lower = email.toLowerCase();
  await prisma.emailOtp.deleteMany({ where: { email: lower, purpose } });
  const code = generateCode();
  await prisma.emailOtp.create({
    data: {
      email: lower,
      purpose,
      codeHash: hashCode(code),
      expiresAt: new Date(Date.now() + OTP_TTL_MS),
    },
  });
  return code;
}

// Returns { ok: true } or { ok: false, reason: 'expired' | 'locked' | 'invalid' }.
export async function verifyOtp(email, purpose, code) {
  const lower = email.toLowerCase();
  const otp = await prisma.emailOtp.findFirst({
    where: { email: lower, purpose, consumedAt: null },
    orderBy: { createdAt: 'desc' },
  });

  if (!otp || otp.expiresAt < new Date()) return { ok: false, reason: 'expired' };
  if (otp.attempts >= MAX_ATTEMPTS) return { ok: false, reason: 'locked' };

  if (otp.codeHash !== hashCode(code)) {
    await prisma.emailOtp.update({ where: { id: otp.id }, data: { attempts: { increment: 1 } } });
    return { ok: false, reason: 'invalid' };
  }

  await prisma.emailOtp.update({ where: { id: otp.id }, data: { consumedAt: new Date() } });
  return { ok: true };
}

export function otpErrorMessage(reason) {
  if (reason === 'expired') return 'Code expired or not found. Please request a new one.';
  if (reason === 'locked') return 'Too many incorrect attempts. Please request a new code.';
  return 'Invalid code. Please check and try again.';
}
