import crypto from 'node:crypto';
import fs from 'node:fs';
import path from 'node:path';

type VerificationFlow = 'register' | 'login';

type VerificationRecord = {
  codeHash: string;
  expiresAt: number;
  attempts: number;
};

const CHALLENGE_TTL_MS = 10 * 60 * 1000;
const MAX_ATTEMPTS = 5;
const records = new Map<string, VerificationRecord>();
const STORE_FILE = path.join(process.cwd(), '.vinxi', 'verification-codes.json');

function loadRecords(): void {
  try {
    if (!fs.existsSync(STORE_FILE)) return;
    const raw = fs.readFileSync(STORE_FILE, 'utf8');
    if (!raw) return;
    const parsed = JSON.parse(raw) as Record<string, VerificationRecord>;
    for (const [key, value] of Object.entries(parsed || {})) {
      if (!value || typeof value !== 'object') continue;
      const expiresAt = Number(value.expiresAt || 0);
      const attempts = Number(value.attempts || 0);
      const codeHash = String(value.codeHash || '');
      if (!codeHash || !Number.isFinite(expiresAt) || !Number.isFinite(attempts)) continue;
      records.set(key, { codeHash, expiresAt, attempts });
    }
  } catch {
    // Ignore corrupt persistence and continue with empty in-memory store.
  }
}

function persistRecords(): void {
  try {
    const dir = path.dirname(STORE_FILE);
    if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
    const payload = Object.fromEntries(records.entries());
    fs.writeFileSync(STORE_FILE, JSON.stringify(payload), 'utf8');
  } catch {
    // Persistence best effort in local/dev context.
  }
}

function hashCode(code: string): string {
  return crypto.createHash('sha256').update(code).digest('hex');
}

function keyFor(email: string, flow: VerificationFlow): string {
  return `${flow}:${email.toLowerCase().trim()}`;
}

function cleanupExpired(): void {
  const now = Date.now();
  let changed = false;
  for (const [key, record] of records.entries()) {
    if (record.expiresAt <= now) {
      records.delete(key);
      changed = true;
    }
  }
  if (changed) persistRecords();
}

loadRecords();

export function createVerificationCode(input: { email: string; flow: VerificationFlow }) {
  cleanupExpired();
  const email = input.email.toLowerCase().trim();
  const flow = input.flow;
  const code = String(crypto.randomInt(100000, 1000000));
  const expiresAt = Date.now() + CHALLENGE_TTL_MS;

  records.set(keyFor(email, flow), {
    codeHash: hashCode(code),
    expiresAt,
    attempts: 0,
  });
  persistRecords();

  return {
    code,
    expiresAt,
    expiresInSeconds: Math.floor(CHALLENGE_TTL_MS / 1000),
  };
}

export function consumeVerificationCode(input: { email: string; flow: VerificationFlow; code: string }):
  | { ok: true }
  | { ok: false; reason: 'NOT_FOUND' | 'EXPIRED' | 'INVALID_CODE' | 'TOO_MANY_ATTEMPTS' } {
  cleanupExpired();
  const key = keyFor(input.email, input.flow);
  const record = records.get(key);
  if (!record) return { ok: false, reason: 'NOT_FOUND' };

  if (record.expiresAt <= Date.now()) {
    records.delete(key);
    persistRecords();
    return { ok: false, reason: 'EXPIRED' };
  }

  if (record.attempts >= MAX_ATTEMPTS) {
    records.delete(key);
    persistRecords();
    return { ok: false, reason: 'TOO_MANY_ATTEMPTS' };
  }

  if (record.codeHash !== hashCode(input.code.trim())) {
    record.attempts += 1;
    records.set(key, record);
    persistRecords();
    return { ok: false, reason: 'INVALID_CODE' };
  }

  records.delete(key);
  persistRecords();
  return { ok: true };
}
