"use strict";
/**
 * Quota Manager for Hybrid Auto+Manual Account Selection
 *
 * Provides pre-flight quota checking with caching, tier-based failover,
 * and cooldown tracking for exhausted accounts.
 *
 * Key features:
 * - 30-second in-memory cache for quota results
 * - Tier-priority failover (ultra > pro by default)
 * - Cooldown tracking for exhausted accounts
 * - Respects paused accounts from manual config
 * - Graceful degradation on API failures
 */
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
    if (mod && mod.__esModule) return mod;
    var result = {};
    if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
    __setModuleDefault(result, mod);
    return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.stopQuotaMonitor = exports.startQuotaMonitor = exports.getQuotaStatus = exports.preflightCheck = exports.reconcileExhaustedRotationAccounts = exports.findHealthyAccount = exports.clearCooldown = exports.applyCooldown = exports.isOnCooldown = exports.clearQuotaCache = exports.setCachedQuota = exports.getCachedQuota = exports.isManagedQuotaProvider = exports.MANAGED_QUOTA_PROVIDERS = void 0;
const quota_fetcher_1 = require("./quota-fetcher");
const quota_fetcher_claude_1 = require("./quota-fetcher-claude");
const quota_fetcher_codex_1 = require("./quota-fetcher-codex");
const quota_fetcher_gemini_cli_1 = require("./quota-fetcher-gemini-cli");
const quota_fetcher_ghcp_1 = require("./quota-fetcher-ghcp");
const account_manager_1 = require("../accounts/account-manager");
const config_loader_facade_1 = require("../../config/config-loader-facade");
exports.MANAGED_QUOTA_PROVIDERS = [
    'agy',
    'claude',
    'codex',
    'gemini',
    'ghcp',
];
function isManagedQuotaProvider(provider) {
    return exports.MANAGED_QUOTA_PROVIDERS.includes(provider);
}
exports.isManagedQuotaProvider = isManagedQuotaProvider;
const CACHE_TTL_MS = 30000; // 30 seconds
const quotaCache = new Map();
// Request deduplication: track in-flight fetch promises to avoid parallel duplicate requests
const pendingFetches = new Map();
function getCacheKey(provider, accountId) {
    return `${provider}:${accountId}`;
}
/**
 * Get cached quota result if still valid
 */
function getCachedQuota(provider, accountId) {
    const key = getCacheKey(provider, accountId);
    const entry = quotaCache.get(key);
    if (!entry)
        return null;
    if (Date.now() - entry.timestamp > CACHE_TTL_MS) {
        quotaCache.delete(key);
        return null;
    }
    return entry.result;
}
exports.getCachedQuota = getCachedQuota;
/**
 * Cache quota result
 */
function setCachedQuota(provider, accountId, result) {
    const key = getCacheKey(provider, accountId);
    quotaCache.set(key, { result, timestamp: Date.now() });
}
exports.setCachedQuota = setCachedQuota;
/**
 * Clear all cached quota results
 */
function clearQuotaCache() {
    quotaCache.clear();
}
exports.clearQuotaCache = clearQuotaCache;
/**
 * Fetch quota with request deduplication
 * If a fetch for this account is already in progress, return the existing promise
 */
async function fetchQuotaWithDedup(provider, accountId, verbose = false) {
    const key = getCacheKey(provider, accountId);
    // Check if fetch already in progress
    const pending = pendingFetches.get(key);
    if (pending) {
        return pending;
    }
    // Start new fetch and track it
    const fetchPromise = fetchManagedQuota(provider, accountId, verbose)
        .then((result) => {
        setCachedQuota(provider, accountId, result);
        return result;
    })
        .catch(() => buildFailedQuotaResult(provider, accountId))
        .finally(() => {
        pendingFetches.delete(key);
    });
    pendingFetches.set(key, fetchPromise);
    return fetchPromise;
}
async function fetchManagedQuota(provider, accountId, verbose) {
    switch (provider) {
        case 'agy':
            return (0, quota_fetcher_1.fetchAccountQuota)(provider, accountId, verbose);
        case 'claude':
            return (0, quota_fetcher_claude_1.fetchClaudeQuota)(accountId, verbose);
        case 'codex':
            return (0, quota_fetcher_codex_1.fetchCodexQuota)(accountId, verbose);
        case 'gemini':
            return (0, quota_fetcher_gemini_cli_1.fetchGeminiCliQuota)(accountId, verbose);
        case 'ghcp':
            return (0, quota_fetcher_ghcp_1.fetchGhcpQuota)(accountId, verbose);
    }
}
function buildFailedGhcpSnapshot() {
    return {
        reported: false,
        entitlement: 0,
        remaining: 0,
        used: 0,
        percentRemaining: 0,
        percentUsed: 0,
        unlimited: false,
        overageCount: 0,
        overagePermitted: false,
        quotaId: null,
    };
}
function buildFailedQuotaResult(provider, accountId) {
    switch (provider) {
        case 'agy':
            return { success: false, models: [], lastUpdated: Date.now(), accountId };
        case 'claude':
            return {
                success: false,
                windows: [],
                coreUsage: { fiveHour: null, weekly: null },
                lastUpdated: Date.now(),
                error: 'Failed to fetch Claude quota',
                accountId,
            };
        case 'codex':
            return {
                success: false,
                windows: [],
                coreUsage: { fiveHour: null, weekly: null },
                planType: null,
                lastUpdated: Date.now(),
                error: 'Failed to fetch Codex quota',
                accountId,
            };
        case 'gemini':
            return {
                success: false,
                buckets: [],
                projectId: null,
                lastUpdated: Date.now(),
                error: 'Failed to fetch Gemini quota',
                accountId,
            };
        case 'ghcp':
            return {
                success: false,
                planType: null,
                quotaResetDate: null,
                snapshots: {
                    premiumInteractions: buildFailedGhcpSnapshot(),
                    chat: buildFailedGhcpSnapshot(),
                    completions: buildFailedGhcpSnapshot(),
                },
                lastUpdated: Date.now(),
                error: 'Failed to fetch GitHub Copilot quota',
                accountId,
            };
    }
}
const cooldownMap = new Map();
/**
 * Check if account is on cooldown
 */
function isOnCooldown(provider, accountId) {
    const key = getCacheKey(provider, accountId);
    const entry = cooldownMap.get(key);
    if (!entry)
        return false;
    if (Date.now() > entry.until) {
        cooldownMap.delete(key);
        return false;
    }
    return true;
}
exports.isOnCooldown = isOnCooldown;
/**
 * Apply cooldown to an exhausted account
 */
function applyCooldown(provider, accountId, minutes) {
    const key = getCacheKey(provider, accountId);
    cooldownMap.set(key, { until: Date.now() + minutes * 60 * 1000 });
}
exports.applyCooldown = applyCooldown;
/**
 * Clear cooldown for an account
 */
function clearCooldown(provider, accountId) {
    const key = getCacheKey(provider, accountId);
    cooldownMap.delete(key);
}
exports.clearCooldown = clearCooldown;
// ============================================================================
// PRE-FLIGHT CHECK
// ============================================================================
/**
 * Process items with limited concurrency to prevent connection burst
 * @param items - Items to process
 * @param fn - Async function to apply to each item
 * @param concurrency - Number of concurrent operations (default: 10)
 */
async function batchedMap(items, fn, concurrency = 10, delayMs = 100) {
    const results = [];
    for (let i = 0; i < items.length; i += concurrency) {
        if (i > 0 && delayMs > 0) {
            await new Promise((resolve) => setTimeout(resolve, delayMs));
        }
        const batch = items.slice(i, i + concurrency);
        const batchResults = await Promise.all(batch.map(fn));
        results.push(...batchResults);
    }
    return results;
}
function calculateAgyQuotaPercent(quota) {
    if (!quota.success || quota.models.length === 0)
        return null;
    const total = quota.models.reduce((sum, model) => sum + model.percentage, 0);
    return total / quota.models.length;
}
function calculateClaudeQuotaPercent(quota) {
    if (!quota.success)
        return null;
    const coreWindows = [quota.coreUsage?.fiveHour, quota.coreUsage?.weekly].filter((window) => !!window);
    if (coreWindows.length > 0) {
        return Math.min(...coreWindows.map((window) => window.remainingPercent));
    }
    const usageWindows = quota.windows.filter((window) => window.rateLimitType !== 'overage');
    if (usageWindows.length > 0) {
        return Math.min(...usageWindows.map((window) => window.remainingPercent));
    }
    return null;
}
function calculateCodexQuotaPercent(quota) {
    if (!quota.success)
        return null;
    const coreWindows = [quota.coreUsage?.fiveHour, quota.coreUsage?.weekly].filter((window) => !!window);
    if (coreWindows.length > 0) {
        return Math.min(...coreWindows.map((window) => window.remainingPercent));
    }
    const usageWindows = quota.windows.filter((window) => window.category !== 'code-review');
    if (usageWindows.length > 0) {
        return Math.min(...usageWindows.map((window) => window.remainingPercent));
    }
    return null;
}
function calculateGeminiQuotaPercent(quota) {
    if (!quota.success || quota.buckets.length === 0)
        return null;
    return Math.min(...quota.buckets.map((bucket) => bucket.remainingPercent));
}
function calculateGhcpQuotaPercent(quota) {
    if (!quota.success)
        return null;
    const percentages = [
        quota.snapshots.premiumInteractions,
        quota.snapshots.chat,
        quota.snapshots.completions,
    ]
        .filter((snapshot) => snapshot.reported !== false)
        .map((snapshot) => (snapshot.unlimited ? 100 : snapshot.percentRemaining))
        .filter((percentage) => Number.isFinite(percentage));
    return percentages.length > 0 ? Math.min(...percentages) : null;
}
/**
 * Calculate normalized quota percentage for managed providers.
 */
function calculateQuotaPercent(provider, quota) {
    switch (provider) {
        case 'agy':
            return calculateAgyQuotaPercent(quota);
        case 'claude':
            return calculateClaudeQuotaPercent(quota);
        case 'codex':
            return calculateCodexQuotaPercent(quota);
        case 'gemini':
            return calculateGeminiQuotaPercent(quota);
        case 'ghcp':
            return calculateGhcpQuotaPercent(quota);
    }
}
/**
 * Find healthy account with remaining quota
 * Respects tier priority and skips paused/cooldown accounts
 */
async function findHealthyAccount(provider, exclude) {
    if (!isManagedQuotaProvider(provider)) {
        return null;
    }
    const { restoreExpiredQuotaPauses } = await Promise.resolve().then(() => __importStar(require('../accounts/account-safety')));
    restoreExpiredQuotaPauses();
    const config = (0, config_loader_facade_1.loadOrCreateUnifiedConfig)();
    const tierPriority = config.quota_management?.auto?.tier_priority ?? ['ultra', 'pro', 'free'];
    const threshold = config.quota_management?.auto?.exhaustion_threshold ?? 5;
    const accounts = (0, account_manager_1.getProviderAccounts)(provider);
    // Filter available accounts
    const available = accounts.filter((a) => !exclude.includes(a.id) && !(0, account_manager_1.isAccountPaused)(provider, a.id) && !isOnCooldown(provider, a.id));
    if (available.length === 0)
        return null;
    // Fetch quota for each available account (batched to prevent connection burst)
    const withQuotas = await batchedMap(available, async (account) => {
        let quota = getCachedQuota(provider, account.id);
        if (!quota) {
            quota = await fetchQuotaWithDedup(provider, account.id);
        }
        const avgQuota = calculateQuotaPercent(provider, quota);
        return {
            id: account.id,
            tier: account.tier || 'unknown',
            lastQuota: avgQuota,
        };
    }, 10);
    // Prefer accounts with known healthy quota. If all remaining accounts have unavailable
    // quota data, fall back to those unknown-but-usable accounts instead of treating them as 0%.
    const healthy = withQuotas.filter((a) => a.lastQuota !== null && a.lastQuota >= threshold);
    const selectable = healthy.length > 0 ? healthy : withQuotas.filter((a) => a.lastQuota === null);
    if (selectable.length === 0)
        return null;
    // Sort by tier priority then quota descending
    selectable.sort((a, b) => {
        const tierA = tierPriority.indexOf(a.tier);
        const tierB = tierPriority.indexOf(b.tier);
        const tierOrderA = tierA === -1 ? 999 : tierA;
        const tierOrderB = tierB === -1 ? 999 : tierB;
        if (tierOrderA !== tierOrderB)
            return tierOrderA - tierOrderB;
        return (b.lastQuota ?? -1) - (a.lastQuota ?? -1);
    });
    return selectable[0];
}
exports.findHealthyAccount = findHealthyAccount;
async function reconcileExhaustedRotationAccounts(provider) {
    if (!isManagedQuotaProvider(provider)) {
        return [];
    }
    const { pauseAccountForQuotaCooldown, restoreExpiredQuotaPauses } = await Promise.resolve().then(() => __importStar(require('../accounts/account-safety')));
    restoreExpiredQuotaPauses();
    const config = (0, config_loader_facade_1.loadOrCreateUnifiedConfig)();
    const threshold = config.quota_management?.auto?.exhaustion_threshold ?? 5;
    const cooldownMinutes = config.quota_management?.auto?.cooldown_minutes ?? 5;
    const activeAccounts = (0, account_manager_1.getProviderAccounts)(provider).filter((account) => !(0, account_manager_1.isAccountPaused)(provider, account.id));
    if (activeAccounts.length < 2) {
        return [];
    }
    const accountsWithQuota = await batchedMap(activeAccounts, async (account) => {
        let quota = getCachedQuota(provider, account.id);
        if (!quota) {
            quota = await fetchQuotaWithDedup(provider, account.id);
        }
        return {
            account,
            quotaPercent: calculateQuotaPercent(provider, quota),
        };
    }, 10);
    const healthyAccountIds = new Set(accountsWithQuota
        .filter(({ account, quotaPercent }) => {
        if (isOnCooldown(provider, account.id))
            return false;
        return quotaPercent !== null && quotaPercent >= threshold;
    })
        .map(({ account }) => account.id));
    if (healthyAccountIds.size === 0) {
        return [];
    }
    const pausedAccountIds = [];
    for (const { account, quotaPercent } of accountsWithQuota) {
        const exhaustedByQuota = quotaPercent !== null && quotaPercent < threshold;
        const exhaustedByCooldown = isOnCooldown(provider, account.id);
        if (!exhaustedByQuota && !exhaustedByCooldown)
            continue;
        const hasOtherHealthyAccount = [...healthyAccountIds].some((id) => id !== account.id);
        if (!hasOtherHealthyAccount)
            continue;
        applyCooldown(provider, account.id, cooldownMinutes);
        if (pauseAccountForQuotaCooldown(provider, account.id, cooldownMinutes)) {
            pausedAccountIds.push(account.id);
        }
    }
    return pausedAccountIds;
}
exports.reconcileExhaustedRotationAccounts = reconcileExhaustedRotationAccounts;
/**
 * Find and switch to a healthy account
 */
async function findAndSwitch(provider, excludeAccountId, reason) {
    const alternative = await findHealthyAccount(provider, [excludeAccountId]);
    if (!alternative) {
        // No alternatives: use original anyway (graceful degradation)
        return {
            proceed: true,
            accountId: excludeAccountId,
            reason: `${reason}, no alternatives available`,
        };
    }
    // Switch default
    (0, account_manager_1.setDefaultAccount)(provider, alternative.id);
    (0, account_manager_1.touchAccount)(provider, alternative.id);
    return {
        proceed: true,
        accountId: alternative.id,
        switchedFrom: excludeAccountId,
        reason,
        quotaPercent: alternative.lastQuota,
    };
}
/**
 * Perform pre-flight quota check before session start
 *
 * Checks if default account has sufficient quota, auto-switches if needed.
 * Respects paused accounts, tier priority, and cooldown settings.
 *
 * @param provider - CLIProxy provider
 * @returns PreflightResult with account to use and any switch info
 */
async function preflightCheck(provider) {
    // Only providers with quota-based account rotation need preflight checks.
    if (!isManagedQuotaProvider(provider)) {
        const defaultAccount = (0, account_manager_1.getDefaultAccount)(provider);
        return { proceed: true, accountId: defaultAccount?.id || '' };
    }
    const { pauseAccountForQuotaCooldown, restoreExpiredQuotaPauses } = await Promise.resolve().then(() => __importStar(require('../accounts/account-safety')));
    restoreExpiredQuotaPauses();
    const config = (0, config_loader_facade_1.loadOrCreateUnifiedConfig)();
    const quotaConfig = config.quota_management;
    // Skip if preflight disabled or mode is manual
    if (!quotaConfig?.auto?.preflight_check || quotaConfig?.mode === 'manual') {
        const defaultAccount = (0, account_manager_1.getDefaultAccount)(provider);
        return { proceed: true, accountId: defaultAccount?.id || '' };
    }
    const defaultAccount = (0, account_manager_1.getDefaultAccount)(provider);
    if (!defaultAccount) {
        return { proceed: false, accountId: '', reason: 'No accounts configured' };
    }
    await reconcileExhaustedRotationAccounts(provider);
    // Check forced_default override (manual mode)
    const forcedDefault = quotaConfig.manual?.forced_default;
    if (forcedDefault) {
        const forcedAccount = (0, account_manager_1.getProviderAccounts)(provider).find((a) => a.id === forcedDefault);
        if (forcedAccount && !(0, account_manager_1.isAccountPaused)(provider, forcedAccount.id)) {
            return { proceed: true, accountId: forcedAccount.id, reason: 'Forced default override' };
        }
    }
    // Check if default is paused
    if ((0, account_manager_1.isAccountPaused)(provider, defaultAccount.id)) {
        return await findAndSwitch(provider, defaultAccount.id, 'Default account is paused');
    }
    // Check cooldown
    if (isOnCooldown(provider, defaultAccount.id)) {
        return await findAndSwitch(provider, defaultAccount.id, 'Default account on cooldown');
    }
    // Check quota (with cache and deduplication)
    let quota = getCachedQuota(provider, defaultAccount.id);
    if (!quota) {
        quota = await fetchQuotaWithDedup(provider, defaultAccount.id);
    }
    // Calculate normalized quota percentage.
    // Null means quota data is unavailable (e.g. provider does not expose limits for this account).
    const quotaPercent = calculateQuotaPercent(provider, quota);
    if (quotaPercent === null) {
        return {
            proceed: true,
            accountId: defaultAccount.id,
            reason: 'Quota unavailable, using default account',
        };
    }
    const avgQuota = quotaPercent;
    const threshold = quotaConfig.auto?.exhaustion_threshold ?? 5;
    if (avgQuota < threshold) {
        applyCooldown(provider, defaultAccount.id, quotaConfig.auto?.cooldown_minutes ?? 5);
        const alternative = await findHealthyAccount(provider, [defaultAccount.id]);
        if (!alternative) {
            return {
                proceed: true,
                accountId: defaultAccount.id,
                reason: `Quota exhausted (${avgQuota.toFixed(1)}%), no alternatives available`,
                quotaPercent,
            };
        }
        if (alternative.lastQuota !== null && alternative.lastQuota >= threshold) {
            pauseAccountForQuotaCooldown(provider, defaultAccount.id, quotaConfig.auto?.cooldown_minutes ?? 5);
        }
        (0, account_manager_1.setDefaultAccount)(provider, alternative.id);
        (0, account_manager_1.touchAccount)(provider, alternative.id);
        return {
            proceed: true,
            accountId: alternative.id,
            switchedFrom: defaultAccount.id,
            reason: `Quota exhausted (${avgQuota.toFixed(1)}%)`,
            quotaPercent: alternative.lastQuota,
        };
    }
    return {
        proceed: true,
        accountId: defaultAccount.id,
        quotaPercent,
    };
}
exports.preflightCheck = preflightCheck;
/**
 * Get quota status for all accounts of a provider
 * Used by CLI status command
 */
async function getQuotaStatus(provider) {
    const { restoreExpiredQuotaPauses } = await Promise.resolve().then(() => __importStar(require('../accounts/account-safety')));
    restoreExpiredQuotaPauses();
    const accounts = (0, account_manager_1.getProviderAccounts)(provider);
    const defaultAccount = (0, account_manager_1.getDefaultAccount)(provider);
    const results = await Promise.all(accounts.map(async (account) => {
        const managedProvider = isManagedQuotaProvider(provider) ? provider : null;
        let quota = getCachedQuota(provider, account.id);
        if (!quota && managedProvider) {
            quota = await fetchQuotaWithDedup(managedProvider, account.id);
        }
        const avgQuota = quota && managedProvider ? calculateQuotaPercent(managedProvider, quota) : null;
        return {
            account,
            quota: avgQuota,
            paused: (0, account_manager_1.isAccountPaused)(provider, account.id),
            onCooldown: isOnCooldown(provider, account.id),
            isDefault: defaultAccount?.id === account.id,
        };
    }));
    return { accounts: results };
}
exports.getQuotaStatus = getQuotaStatus;
/** Active monitor timers keyed by provider/account. */
const activeRuntimeMonitors = new Map();
/**
 * Schedule next quota poll with adaptive interval.
 * Uses setTimeout chain (not setInterval) for dynamic interval switching.
 */
function scheduleNextPoll(provider, accountId, monitorConfig, intervalMs, state) {
    const monitorKey = getCacheKey(provider, accountId);
    state.timer = setTimeout(async () => {
        // Guard: skip if monitor was stopped while this callback was queued
        if (state.stopped || activeRuntimeMonitors.get(monitorKey) !== state)
            return;
        try {
            const quota = await fetchQuotaWithDedup(provider, accountId);
            if (state.stopped || activeRuntimeMonitors.get(monitorKey) !== state)
                return;
            const avgQuota = calculateQuotaPercent(provider, quota);
            if (avgQuota === null) {
                // Quota data unavailable: keep polling, but do not treat unknown as healthy/exhausted.
                scheduleNextPoll(provider, accountId, monitorConfig, monitorConfig.normal_interval_seconds * 1000, state);
                return;
            }
            if (avgQuota <= monitorConfig.exhaustion_threshold) {
                // EXHAUSTED: cooldown + switch default + stop monitoring.
                // NOTE: Monitor stops here intentionally. The current session continues
                // on the exhausted account (can't hot-swap mid-session). The switched
                // default only takes effect on next session start via preflightCheck().
                const { handleQuotaExhaustion } = await Promise.resolve().then(() => __importStar(require('../accounts/account-safety')));
                await handleQuotaExhaustion(provider, accountId, monitorConfig.cooldown_minutes);
                state.stopped = true;
                state.timer = null;
                activeRuntimeMonitors.delete(monitorKey);
                return; // Stop polling
            }
            if (avgQuota <= monitorConfig.warn_threshold) {
                // WARNING: switch to critical interval, warn once
                if (!state.hasWarnedThisSession) {
                    const { writeQuotaWarning } = await Promise.resolve().then(() => __importStar(require('../accounts/account-safety')));
                    writeQuotaWarning(accountId, avgQuota);
                    state.hasWarnedThisSession = true;
                }
                scheduleNextPoll(provider, accountId, monitorConfig, monitorConfig.critical_interval_seconds * 1000, state);
                return;
            }
            // HEALTHY: keep normal interval
            scheduleNextPoll(provider, accountId, monitorConfig, monitorConfig.normal_interval_seconds * 1000, state);
        }
        catch {
            // API failure: silently reschedule at same interval
            if (!state.stopped && activeRuntimeMonitors.get(monitorKey) === state) {
                scheduleNextPoll(provider, accountId, monitorConfig, intervalMs, state);
            }
        }
    }, intervalMs);
    // Prevent monitor from keeping Node.js process alive
    if (state.timer && typeof state.timer === 'object' && 'unref' in state.timer) {
        state.timer.unref();
    }
}
/**
 * Start adaptive quota monitor for an active session.
 * Polls at normal_interval (300s) when healthy, switches to
 * critical_interval (60s) when quota hits warn_threshold (20%).
 * Auto-stops on exhaustion or when stopQuotaMonitor() is called.
 *
 * Only monitors providers with quota-based account rotation.
 * No-op for other providers, manual mode, or if disabled in config.
 */
function startQuotaMonitor(provider, accountId) {
    // Only managed providers support runtime quota monitoring.
    if (!isManagedQuotaProvider(provider))
        return;
    const config = (0, config_loader_facade_1.loadOrCreateUnifiedConfig)();
    const quotaConfig = config.quota_management;
    // Skip if config missing (shouldn't happen with defaults)
    if (!quotaConfig)
        return;
    // Skip if manual mode or runtime monitor disabled
    if (quotaConfig.mode === 'manual')
        return;
    if (!quotaConfig.runtime_monitor?.enabled)
        return;
    // Validate thresholds: warn must be > exhaustion to avoid immediate exhaustion on warning
    const monitorConfig = quotaConfig.runtime_monitor;
    if (monitorConfig.warn_threshold <= monitorConfig.exhaustion_threshold) {
        return; // Invalid config — skip monitoring silently (logged at config level)
    }
    const monitorKey = getCacheKey(provider, accountId);
    if (activeRuntimeMonitors.has(monitorKey))
        return;
    const state = {
        timer: null,
        hasWarnedThisSession: false,
        stopped: false,
    };
    activeRuntimeMonitors.set(monitorKey, state);
    // Start first poll at normal interval
    scheduleNextPoll(provider, accountId, quotaConfig.runtime_monitor, quotaConfig.runtime_monitor.normal_interval_seconds * 1000, state);
}
exports.startQuotaMonitor = startQuotaMonitor;
/**
 * Stop the runtime quota monitor. Safe to call multiple times.
 */
function stopQuotaMonitor() {
    for (const state of activeRuntimeMonitors.values()) {
        state.stopped = true;
        if (state.timer) {
            clearTimeout(state.timer);
            state.timer = null;
        }
    }
    activeRuntimeMonitors.clear();
}
exports.stopQuotaMonitor = stopQuotaMonitor;
//# sourceMappingURL=quota-manager.js.map