/**
 * Translation daily budget gate.
 *
 * All mutations use a single atomic SQL UPDATE so concurrent workers cannot
 * both succeed when only one fits inside the remaining budget.
 */
import { sql } from 'drizzle-orm';
import type { DrizzleClient } from '../db/client.js';
import { BudgetExhaustedError } from './provider/types.js';

// Re-export so orchestrator (Task 10) can import from one place.
export { BudgetExhaustedError };

/**
 * Atomically reserve `estUsd` from today's translation budget.
 *
 * Executes:
 *   UPDATE system_config
 *     SET value = (value::numeric + $1)::text
 *    WHERE key = 'translation_daily_spent_usd'
 *      AND (value::numeric + $1) <= (
 *            SELECT value::numeric FROM system_config
 *             WHERE key = 'translation_daily_budget_usd'
 *          )
 *
 * If 0 rows are updated the budget is exhausted → throws BudgetExhaustedError.
 */
export async function checkAndIncrementBudget(db: DrizzleClient, estUsd: number): Promise<void> {
  const result = await db.execute(sql`
    UPDATE system_config
       SET value = (value::numeric + ${estUsd})::text
     WHERE key = 'translation_daily_spent_usd'
       AND (value::numeric + ${estUsd}) <= (
             SELECT value::numeric
               FROM system_config
              WHERE key = 'translation_daily_budget_usd'
           )
  `);

  // Neon HTTP driver returns rowCount on DML statements.
  const rowCount = (result as unknown as { rowCount?: number | null }).rowCount ?? 0;
  if (rowCount === 0) {
    throw new BudgetExhaustedError();
  }
}

/**
 * Reconcile estimated vs actual spend after a provider call completes.
 *
 * Adjusts the running total by `(actual - estimated)`.  The delta can be
 * negative (actual cheaper than estimate) or positive (actual more expensive).
 * Uses a single atomic UPDATE to avoid races.
 */
export async function reconcileBudget(
  db: DrizzleClient,
  estimated: number,
  actual: number,
): Promise<void> {
  const delta = actual - estimated;
  if (delta === 0) return;

  await db.execute(sql`
    UPDATE system_config
       SET value = (value::numeric + ${delta})::text
     WHERE key = 'translation_daily_spent_usd'
  `);
}

/**
 * Reset today's translation spend to zero.
 * Called by the midnight Asia/Jerusalem cron (Task 7 cron infrastructure).
 */
export async function resetDailyBudget(db: DrizzleClient): Promise<void> {
  await db.execute(sql`
    UPDATE system_config
       SET value = '0'
     WHERE key = 'translation_daily_spent_usd'
  `);
}
