import { randomUUID } from 'crypto';
import redis from '../redis.js';
import { getPool } from '../db.js';
import { generateMonthlyInvoice } from './invoices.js';

const LOCK_KEY = 'invoice:cron:lock';
const LOCK_TTL_SECONDS = 3600;

// Only releases the lock if the value matches — prevents one replica from
// deleting another's lock after a TTL expiry race.
const COMPARE_DELETE_LUA = `
  if redis.call("get", KEYS[1]) == ARGV[1] then
    return redis.call("del", KEYS[1])
  else
    return 0
  end
`;

export async function runInvoiceCron(): Promise<void> {
  const lockValue = randomUUID();

  const acquired = await redis.set(LOCK_KEY, lockValue, { NX: true, EX: LOCK_TTL_SECONDS });
  if (!acquired) {
    console.log('[cron] Another process holds the invoice lock — skipping');
    return;
  }

  try {
    const [customers] = await getPool().query<any[]>(
      "SELECT id FROM customers WHERE active = TRUE AND plan != 'free'"
    );

    for (const customer of customers) {
      try {
        const invoice = await generateMonthlyInvoice(customer.id);
        if (invoice) {
          console.log(`[cron] Generated invoice ${invoice.invoice_number} for customer ${customer.id}`);
        }
      } catch (err) {
        // One customer failing must not abort the rest
        console.error(`[cron] Failed for customer ${customer.id}:`, err);
      }
    }

    await redis.eval(COMPARE_DELETE_LUA, { keys: [LOCK_KEY], arguments: [lockValue] });
  } catch (err) {
    // Leave the lock in place on unexpected failure — TTL releases it after 1 hour
    console.error('[cron] Invoice cron fatal error:', err);
    throw err;
  }
}
