import { query, withTransaction } from './db.js';
import { signBalance, verifyBalance } from './balance-integrity.js';
import { syncBalanceFromD1 } from './routes/balance.js';
let staleChargeHandle = null;
let d1SyncHandle = null;
/**
 * Expire stale pending charges (older than 30 minutes) and refund balances.
 * Runs as a periodic job.
 */
export function expireStaleCharges() {
    return withTransaction(() => {
        // Find stale pending charges
        const stale = query(`SELECT id, license_key_id, amount_cents FROM charges
       WHERE status = 'pending' AND created_at < datetime('now', '-30 minutes')`);
        if (stale.rows.length === 0)
            return 0;
        // Mark them expired
        for (const row of stale.rows) {
            query('UPDATE charges SET status = ? WHERE id = ?', ['expired', row.id]);
        }
        // Refund balances (aggregate by license_key_id) with HMAC verification and re-signing
        const refunds = new Map();
        for (const row of stale.rows) {
            refunds.set(row.license_key_id, (refunds.get(row.license_key_id) ?? 0) + row.amount_cents);
        }
        for (const [keyId, total] of refunds) {
            const keyRow = query('SELECT id, balance_cents, balance_hmac FROM license_keys WHERE id = ?', [keyId]);
            if (keyRow.rows.length === 0)
                continue;
            const key = keyRow.rows[0];
            if (!verifyBalance(key.id, key.balance_cents, key.balance_hmac)) {
                console.error(`[FATAL] Balance integrity check failed for key ${key.id} during stale charge refund: HMAC mismatch. balance_cents=${key.balance_cents}`);
                continue; // Skip refund for tampered key — do not credit tampered balances
            }
            const newBalance = key.balance_cents + total;
            const hmac = signBalance(key.id, newBalance);
            query('UPDATE license_keys SET balance_cents = ?, balance_hmac = ? WHERE id = ?', [newBalance, hmac, key.id]);
        }
        return stale.rows.length;
    });
}
/**
 * Clean up expired sessions.
 */
export function cleanupExpiredSessions() {
    const result = query("DELETE FROM sessions WHERE expires_at < datetime('now')");
    return result.rowCount;
}
/**
 * Periodic D1 balance sync — picks up Stripe refunds issued via admin dashboard.
 * Syncs all active license keys' balances from D1 (source of truth).
 * Runs every 60 seconds so refunds reflect within ~1 minute.
 */
async function syncAllBalancesFromD1() {
    const activeKeys = query("SELECT id, key_hash, key_prefix FROM license_keys WHERE status = 'active'");
    if (activeKeys.rows.length === 0)
        return;
    for (const key of activeKeys.rows) {
        try {
            await syncBalanceFromD1(key.id, key.key_hash);
        }
        catch (err) {
            console.error(`[d1-sync-job] Failed to sync key=${key.key_prefix}...: ${err instanceof Error ? err.message : String(err)}`);
        }
    }
}
export function startJobs() {
    // Run stale charge cleanup every 5 minutes
    staleChargeHandle = setInterval(() => {
        try {
            const expired = expireStaleCharges();
            const sessions = cleanupExpiredSessions();
            if (expired > 0 || sessions > 0) {
                console.error(`[Jobs] Expired ${expired} stale charges, cleaned ${sessions} sessions`);
            }
        }
        catch (error) {
            console.error('[Jobs] Error in periodic cleanup:', error instanceof Error ? error.message : String(error));
        }
    }, 5 * 60 * 1000);
    // Run D1 balance sync every 60 seconds — picks up admin-issued Stripe refunds
    d1SyncHandle = setInterval(() => {
        syncAllBalancesFromD1().catch((err) => {
            console.error('[Jobs] Error in D1 balance sync:', err instanceof Error ? err.message : String(err));
        });
    }, 60 * 1000);
    // Run immediately on startup
    try {
        const expired = expireStaleCharges();
        const sessions = cleanupExpiredSessions();
        if (expired > 0 || sessions > 0) {
            console.error(`[Jobs] Startup cleanup: expired ${expired} charges, cleaned ${sessions} sessions`);
        }
    }
    catch (error) {
        console.error('[Jobs] Error in startup cleanup:', error instanceof Error ? error.message : String(error));
    }
    // Also sync from D1 on startup (async, non-blocking)
    syncAllBalancesFromD1().catch((err) => {
        console.error('[Jobs] Error in startup D1 sync:', err instanceof Error ? err.message : String(err));
    });
}
export function stopJobs() {
    if (staleChargeHandle) {
        clearInterval(staleChargeHandle);
        staleChargeHandle = null;
    }
    if (d1SyncHandle) {
        clearInterval(d1SyncHandle);
        d1SyncHandle = null;
    }
}
//# sourceMappingURL=jobs.js.map