"use strict";
/**
 * Quota Fetcher for Gemini CLI Accounts
 *
 * Fetches quota information from Google Cloud Code internal API.
 * Used for displaying bucket-based quotas grouped by model series.
 */
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.buildGeminiCliBuckets = exports.resolveGeminiCliProjectId = exports.__testExports = exports.fetchAllGeminiCliQuotas = exports.fetchGeminiCliQuota = void 0;
const fs = __importStar(require("node:fs"));
const path = __importStar(require("node:path"));
const config_generator_1 = require("../config/config-generator");
const account_manager_1 = require("../accounts/account-manager");
const auth_utils_1 = require("../auth/auth-utils");
const gemini_cli_quota_normalizer_1 = require("./gemini-cli-quota-normalizer");
const provider_capabilities_1 = require("../provider-capabilities");
const proxy_target_resolver_1 = require("../proxy/proxy-target-resolver");
const provider_entitlement_evidence_1 = require("../auth/provider-entitlement-evidence");
/** Google Cloud Code API endpoints */
const GEMINI_CLI_API_BASE = 'https://cloudcode-pa.googleapis.com';
const GEMINI_CLI_API_VERSION = 'v1internal';
const GEMINI_CLI_QUOTA_URL = `${GEMINI_CLI_API_BASE}/${GEMINI_CLI_API_VERSION}:retrieveUserQuota`;
const GEMINI_CLI_CODE_ASSIST_URL = `${GEMINI_CLI_API_BASE}/${GEMINI_CLI_API_VERSION}:loadCodeAssist`;
const GEMINI_CLI_ERROR_DETAIL_MAX_LENGTH = 320;
const GEMINI_CLI_ERROR_DETAIL_TRUNCATION_SUFFIX = '...[truncated]';
const GEMINI_CLI_G1_CREDIT_TYPE = 'GOOGLE_ONE_AI';
const MANAGEMENT_API_TIMEOUT_MS = 5000;
const SECONDARY_REQUEST_TIMEOUT_MS = 2000;
function getRemainingTimeoutMs(deadlineMs) {
    return Math.max(1, deadlineMs - Date.now());
}
/**
 * Extract project ID from account field
 * Input: "user@example.com (cloudaicompanion-abc-123)"
 * Output: "cloudaicompanion-abc-123"
 */
function resolveGeminiCliProjectId(accountField) {
    const regex = /\(([^()]+)\)/g;
    let match;
    let lastMatch = null;
    while ((match = regex.exec(accountField)) !== null) {
        lastMatch = match[1];
    }
    return lastMatch;
}
exports.resolveGeminiCliProjectId = resolveGeminiCliProjectId;
/**
 * Extract access token from Gemini auth file data
 * Handles both flat (access_token) and nested (token.access_token) structures
 */
function extractAccessToken(data) {
    // Flat structure: { access_token: "..." }
    if (typeof data.access_token === 'string') {
        return data.access_token;
    }
    // Nested structure: { token: { access_token: "..." } }
    if (data.token && typeof data.token === 'object') {
        const token = data.token;
        if (typeof token.access_token === 'string') {
            return token.access_token;
        }
    }
    return null;
}
/**
 * Extract expiry from Gemini auth file data
 * Handles both flat (expired) and nested (token.expiry) structures
 */
function extractExpiry(data) {
    // Flat structure: { expired: "..." }
    if (typeof data.expired === 'string') {
        return data.expired;
    }
    if (typeof data.expired === 'number') {
        return data.expired;
    }
    // Nested structure: { token: { expiry: "..." } }
    if (data.token && typeof data.token === 'object') {
        const token = data.token;
        if (typeof token.expiry === 'string') {
            return token.expiry;
        }
        if (typeof token.expiry === 'number') {
            return token.expiry;
        }
    }
    return null;
}
/**
 * Check if file matches Gemini CLI auth file patterns
 * Patterns: gemini-*.json OR *-gen-lang-client-*.json OR email@domain.com-*.json with type=gemini
 */
function isGeminiAuthFile(filename) {
    if (!filename.endsWith('.json'))
        return false;
    // Legacy pattern: gemini-email.json
    if (filename.startsWith('gemini-'))
        return true;
    // New pattern: email-gen-lang-client-projectId.json
    if (filename.includes('-gen-lang-client-'))
        return true;
    // Check if contains @ (email pattern) - will verify type inside
    if (filename.includes('@'))
        return true;
    return false;
}
function safeParseJson(bodyText) {
    try {
        return JSON.parse(bodyText);
    }
    catch {
        return null;
    }
}
async function readManagedResponse(response, viaManagement) {
    const bodyText = await response.text();
    return {
        status: response.status,
        bodyText,
        json: safeParseJson(bodyText),
        viaManagement,
    };
}
function isGeminiAuthFileForAccount(file, accountId) {
    const rawProvider = normalizeStringValue(file.provider ?? file.type);
    if (!rawProvider || (0, provider_capabilities_1.mapExternalProviderName)(rawProvider) !== 'gemini') {
        return false;
    }
    const email = normalizeStringValue(file.email);
    const normalizedAccountId = accountId.trim().toLowerCase();
    if (email?.toLowerCase() === normalizedAccountId) {
        return true;
    }
    const normalizedName = normalizeStringValue(file.name);
    if (!normalizedName) {
        return false;
    }
    const normalizedFileName = normalizedName.toLowerCase();
    const sanitizedAccount = (0, auth_utils_1.sanitizeEmail)(accountId).toLowerCase();
    return (normalizedFileName === `gemini-${sanitizedAccount}.json` ||
        normalizedFileName.startsWith(`${normalizedAccountId}-gen-lang-client-`) ||
        normalizedFileName.includes(sanitizedAccount));
}
async function findManagedGeminiAuthIndex(accountId, timeoutMs) {
    const target = (0, proxy_target_resolver_1.getProxyTarget)();
    const controller = new AbortController();
    const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
    try {
        const response = await fetch((0, proxy_target_resolver_1.buildProxyUrl)(target, '/v0/management/auth-files'), {
            signal: controller.signal,
            headers: (0, proxy_target_resolver_1.buildManagementHeaders)(target),
        });
        clearTimeout(timeoutId);
        if (!response.ok) {
            return { authIndex: null, unavailable: true };
        }
        const data = (await response.json());
        const match = data.files?.find((file) => isGeminiAuthFileForAccount(file, accountId));
        return { authIndex: match?.auth_index ?? null, unavailable: false };
    }
    catch {
        clearTimeout(timeoutId);
        return { authIndex: null, unavailable: true };
    }
}
async function getManagedGeminiAuthIndex(accountId, timeoutMs, context) {
    if (!context) {
        return await findManagedGeminiAuthIndex(accountId, timeoutMs);
    }
    context.authIndexLookupPromise ?? (context.authIndexLookupPromise = findManagedGeminiAuthIndex(accountId, timeoutMs));
    return await context.authIndexLookupPromise;
}
class GeminiManagedAuthUnavailableError extends Error {
    constructor() {
        super('CLIProxy managed Gemini auth is temporarily unavailable');
        this.name = 'GeminiManagedAuthUnavailableError';
    }
}
async function performManagedGeminiRequest(accountId, url, body, timeoutMs, authContext) {
    const deadlineMs = Date.now() + timeoutMs;
    const lookupResult = await getManagedGeminiAuthIndex(accountId, getRemainingTimeoutMs(deadlineMs), authContext);
    if (lookupResult.unavailable) {
        return { response: null, unavailable: true };
    }
    const authIndex = lookupResult.authIndex;
    if (authIndex === null || authIndex === undefined) {
        return { response: null, unavailable: false };
    }
    const target = (0, proxy_target_resolver_1.getProxyTarget)();
    const controller = new AbortController();
    const timeoutId = setTimeout(() => controller.abort(), getRemainingTimeoutMs(deadlineMs));
    try {
        const response = await fetch((0, proxy_target_resolver_1.buildProxyUrl)(target, '/v0/management/api-call'), {
            method: 'POST',
            signal: controller.signal,
            headers: (0, proxy_target_resolver_1.buildManagementHeaders)(target, {
                'Content-Type': 'application/json',
            }),
            body: JSON.stringify({
                auth_index: authIndex,
                method: 'POST',
                url,
                header: {
                    Authorization: 'Bearer $TOKEN$',
                    'Content-Type': 'application/json',
                },
                data: body,
            }),
        });
        clearTimeout(timeoutId);
        if (!response.ok) {
            return { response: null, unavailable: true };
        }
        const apiResponse = (await response.json());
        const bodyText = typeof apiResponse.body === 'string' ? apiResponse.body : '';
        return {
            response: {
                status: typeof apiResponse.status_code === 'number' ? apiResponse.status_code : 500,
                bodyText,
                json: safeParseJson(bodyText),
                viaManagement: true,
            },
            unavailable: false,
        };
    }
    catch {
        clearTimeout(timeoutId);
        return { response: null, unavailable: true };
    }
}
async function performGeminiCliRequest(accountId, accessToken, url, body, preferManagement = false, authContext) {
    let managementAttempted = false;
    let managementUnavailable = false;
    if (preferManagement) {
        managementAttempted = true;
        const managedResult = await performManagedGeminiRequest(accountId, url, body, MANAGEMENT_API_TIMEOUT_MS, authContext);
        managementUnavailable = managedResult.unavailable;
        if (managedResult.response) {
            return managedResult.response;
        }
    }
    const controller = new AbortController();
    const timeoutId = setTimeout(() => controller.abort(), managementAttempted ? SECONDARY_REQUEST_TIMEOUT_MS : MANAGEMENT_API_TIMEOUT_MS);
    try {
        const response = await fetch(url, {
            method: 'POST',
            signal: controller.signal,
            headers: {
                Authorization: `Bearer ${accessToken}`,
                'Content-Type': 'application/json',
            },
            body,
        });
        clearTimeout(timeoutId);
        const directResult = await readManagedResponse(response, false);
        if (directResult.status !== 401) {
            return directResult;
        }
        if (managementAttempted) {
            if (managementUnavailable) {
                throw new GeminiManagedAuthUnavailableError();
            }
            return directResult;
        }
        const managedResult = await performManagedGeminiRequest(accountId, url, body, SECONDARY_REQUEST_TIMEOUT_MS, authContext);
        if (managedResult.response) {
            return managedResult.response;
        }
        if (managedResult.unavailable) {
            throw new GeminiManagedAuthUnavailableError();
        }
        return directResult;
    }
    catch (error) {
        clearTimeout(timeoutId);
        throw error;
    }
}
/**
 * Read auth data from Gemini CLI auth file
 * Supports multiple file naming conventions and JSON structures
 */
function readGeminiCliAuthData(accountId) {
    const authDirs = [(0, config_generator_1.getAuthDir)(), (0, account_manager_1.getPausedDir)()];
    const sanitizedId = (0, auth_utils_1.sanitizeEmail)(accountId);
    const expectedFiles = [
        `gemini-${sanitizedId}.json`, // Legacy format
        `${accountId}-gen-lang-client-`, // New format prefix (partial match)
    ];
    for (const authDir of authDirs) {
        if (!fs.existsSync(authDir))
            continue;
        // Try exact legacy match first
        const legacyPath = path.join(authDir, expectedFiles[0]);
        if (fs.existsSync(legacyPath)) {
            try {
                const content = fs.readFileSync(legacyPath, 'utf-8');
                const data = JSON.parse(content);
                const accessToken = extractAccessToken(data);
                if (accessToken) {
                    const projectId = typeof data.project_id === 'string'
                        ? data.project_id
                        : resolveGeminiCliProjectId(String(data.account || ''));
                    const expiry = extractExpiry(data);
                    return {
                        accessToken,
                        projectId,
                        isExpired: (0, auth_utils_1.isTokenExpired)(expiry ?? undefined),
                        expiresAt: expiry,
                    };
                }
            }
            catch {
                // Continue to fallback
            }
        }
        // Scan directory for matching files
        const files = fs.readdirSync(authDir);
        for (const file of files) {
            if (!isGeminiAuthFile(file))
                continue;
            const candidatePath = path.join(authDir, file);
            try {
                const content = fs.readFileSync(candidatePath, 'utf-8');
                const data = JSON.parse(content);
                // Check if this file matches our account
                const fileEmail = typeof data.email === 'string' ? data.email : null;
                const fileType = typeof data.type === 'string' ? data.type : null;
                const matchesEmail = fileEmail === accountId;
                const matchesFilename = file.startsWith(`${accountId}-`) || file.includes(sanitizedId);
                const isGeminiType = fileType === 'gemini' || fileType === 'gemini-cli';
                // Must match account AND be gemini type (or legacy gemini- prefix)
                if ((matchesEmail || matchesFilename) && (isGeminiType || file.startsWith('gemini-'))) {
                    const accessToken = extractAccessToken(data);
                    if (accessToken) {
                        const projectId = typeof data.project_id === 'string'
                            ? data.project_id
                            : resolveGeminiCliProjectId(String(data.account || ''));
                        const expiry = extractExpiry(data);
                        return {
                            accessToken,
                            projectId,
                            isExpired: (0, auth_utils_1.isTokenExpired)(expiry ?? undefined),
                            expiresAt: expiry,
                        };
                    }
                }
            }
            catch {
                continue;
            }
        }
    }
    return null;
}
function normalizeStringValue(value) {
    return typeof value === 'string' && value.trim().length > 0 ? value.trim() : null;
}
function normalizeNumberValue(value) {
    if (typeof value === 'number' && Number.isFinite(value)) {
        return value;
    }
    if (typeof value === 'string' && value.trim().length > 0) {
        const parsed = Number(value);
        if (Number.isFinite(parsed)) {
            return parsed;
        }
    }
    return null;
}
function resolveGeminiCliTierId(payload) {
    if (!payload)
        return null;
    const currentTier = payload.currentTier ?? payload.current_tier;
    const paidTier = payload.paidTier ?? payload.paid_tier;
    const rawId = normalizeStringValue(paidTier?.id) ?? normalizeStringValue(currentTier?.id);
    return rawId ? rawId.toLowerCase() : null;
}
function resolveGeminiCliTierLabel(payload) {
    const tierId = resolveGeminiCliTierId(payload);
    return (0, provider_entitlement_evidence_1.getProviderTierLabel)(tierId);
}
function resolveGeminiCliCreditBalance(payload) {
    if (!payload)
        return null;
    const paidTier = payload.paidTier ?? payload.paid_tier;
    const currentTier = payload.currentTier ?? payload.current_tier;
    const tier = paidTier ?? currentTier;
    if (!tier)
        return null;
    const credits = tier.availableCredits ?? tier.available_credits ?? [];
    let total = 0;
    let found = false;
    for (const credit of credits) {
        const creditType = normalizeStringValue(credit.creditType ?? credit.credit_type);
        if (creditType !== GEMINI_CLI_G1_CREDIT_TYPE)
            continue;
        const amount = normalizeNumberValue(credit.creditAmount ?? credit.credit_amount);
        if (amount !== null) {
            total += amount;
            found = true;
        }
    }
    return found ? total : null;
}
async function fetchGeminiCliSupplementary(accountId, accessToken, projectId, verbose, authContext) {
    const requestBody = JSON.stringify({
        cloudaicompanionProject: projectId,
        metadata: {
            ideType: 'IDE_UNSPECIFIED',
            platform: 'PLATFORM_UNSPECIFIED',
            pluginType: 'GEMINI',
            duetProject: projectId,
        },
    });
    try {
        const response = await performGeminiCliRequest(accountId, accessToken, GEMINI_CLI_CODE_ASSIST_URL, requestBody, false, authContext);
        if (response.status !== 200) {
            if (verbose) {
                const source = response.viaManagement ? 'managed' : 'direct';
                console.error(`[i] Gemini CLI supplementary metadata unavailable via ${source}: HTTP ${response.status}`);
            }
            return { tierLabel: null, tierId: null, creditBalance: null, normalizedTier: 'unknown' };
        }
        const payload = response.json;
        return {
            tierLabel: resolveGeminiCliTierLabel(payload),
            tierId: resolveGeminiCliTierId(payload),
            creditBalance: resolveGeminiCliCreditBalance(payload),
            normalizedTier: (0, provider_entitlement_evidence_1.normalizeProviderTierId)(resolveGeminiCliTierId(payload)),
        };
    }
    catch (error) {
        if (verbose) {
            const message = error instanceof Error ? error.message : 'Unknown error';
            console.error(`[i] Gemini CLI supplementary metadata skipped: ${message}`);
        }
        return { tierLabel: null, tierId: null, creditBalance: null, normalizedTier: 'unknown' };
    }
}
function buildGeminiCliFailureResult(accountId, projectId, options) {
    return {
        success: false,
        buckets: [],
        projectId,
        tierLabel: null,
        tierId: null,
        creditBalance: null,
        lastUpdated: Date.now(),
        accountId,
        error: options.error,
        httpStatus: options.httpStatus,
        errorCode: options.errorCode,
        errorDetail: options.errorDetail,
        actionHint: options.actionHint,
        retryable: options.retryable,
        needsReauth: options.needsReauth,
        isForbidden: options.isForbidden,
        entitlement: options.entitlement,
    };
}
function sanitizeGeminiCliErrorDetail(bodyText) {
    const trimmed = bodyText.trim();
    if (!trimmed) {
        return undefined;
    }
    if (/^<!doctype html/i.test(trimmed) || /^<html/i.test(trimmed) || /^<[^>]+>/.test(trimmed)) {
        return '[HTML error response omitted]';
    }
    let sanitized = trimmed
        .replace(/"(access[_-]?token|refresh[_-]?token|authorization|cookie|set-cookie|api[_-]?key|session[_-]?token|token)"\s*:\s*"[^"]*"/gi, '"$1":"[redacted]"')
        .replace(/Bearer\s+[A-Za-z0-9._-]+/g, 'Bearer [redacted]')
        .replace(/\s+/g, ' ');
    if (sanitized.length > GEMINI_CLI_ERROR_DETAIL_MAX_LENGTH) {
        sanitized = `${sanitized.slice(0, GEMINI_CLI_ERROR_DETAIL_MAX_LENGTH - GEMINI_CLI_ERROR_DETAIL_TRUNCATION_SUFFIX.length)}${GEMINI_CLI_ERROR_DETAIL_TRUNCATION_SUFFIX}`;
    }
    return sanitized;
}
function extractGeminiCliNestedMessage(value) {
    if (Array.isArray(value)) {
        for (const entry of value) {
            const nested = extractGeminiCliNestedMessage(entry);
            if (nested)
                return nested;
        }
        return undefined;
    }
    if (!value || typeof value !== 'object') {
        return undefined;
    }
    const record = value;
    const directMessage = [
        record.message,
        record.localizedMessage,
        record.description,
        record.reason,
        record.error,
    ].find((candidate) => typeof candidate === 'string' && candidate.trim().length > 0);
    if (directMessage) {
        return directMessage;
    }
    return undefined;
}
function parseGeminiCliErrorBody(bodyText) {
    const trimmed = bodyText.trim();
    if (!trimmed) {
        return {};
    }
    const sanitizedDetail = sanitizeGeminiCliErrorDetail(trimmed);
    try {
        const parsed = JSON.parse(trimmed);
        const topLevelMessage = [parsed.message, parsed.error].find((candidate) => typeof candidate === 'string' && candidate.trim().length > 0);
        const topLevelCode = [parsed.code, parsed.status].find((candidate) => typeof candidate === 'string' && candidate.trim().length > 0);
        if (parsed.error && typeof parsed.error === 'object') {
            const error = parsed.error;
            return {
                errorCode: [error.status, error.code, topLevelCode].find((candidate) => typeof candidate === 'string' && candidate.trim().length > 0) || undefined,
                errorDetail: sanitizedDetail,
                message: [
                    error.message,
                    error.error,
                    extractGeminiCliNestedMessage(error.details),
                    topLevelMessage,
                ].find((candidate) => typeof candidate === 'string' && candidate.trim().length > 0) || undefined,
            };
        }
        return {
            errorCode: topLevelCode,
            errorDetail: sanitizedDetail,
            message: [topLevelMessage, extractGeminiCliNestedMessage(parsed.details)].find((candidate) => typeof candidate === 'string' && candidate.trim().length > 0) || undefined,
        };
    }
    catch {
        return {
            errorDetail: sanitizedDetail,
            message: sanitizedDetail === '[HTML error response omitted]' ? undefined : trimmed,
        };
    }
}
function buildGeminiCliForbiddenActionHint(parsed) {
    const combined = `${parsed.message || ''} ${parsed.errorDetail || ''}`.toLowerCase();
    if (combined.includes('verify') || combined.includes('verification')) {
        return 'Complete the Google account verification mentioned above, then retry quota refresh.';
    }
    if (combined.includes('project')) {
        return 'Confirm this Google project still has Gemini CLI quota access, then retry.';
    }
    return 'Check the Google account or workspace access shown above, then retry quota refresh.';
}
function buildGeminiCliHttpFailureResult(accountId, projectId, status, bodyText) {
    const parsed = parseGeminiCliErrorBody(bodyText);
    if (status === 401) {
        return buildGeminiCliFailureResult(accountId, projectId, {
            error: parsed.message || 'Token expired or invalid',
            httpStatus: 401,
            errorCode: parsed.errorCode || 'reauth_required',
            errorDetail: parsed.errorDetail,
            actionHint: 'Run ccs gemini --auth to reconnect this account.',
            needsReauth: true,
            retryable: false,
        });
    }
    if (status === 403) {
        return buildGeminiCliFailureResult(accountId, projectId, {
            error: parsed.message || 'Quota access forbidden for this account',
            httpStatus: 403,
            errorCode: parsed.errorCode || 'quota_api_forbidden',
            errorDetail: parsed.errorDetail,
            actionHint: buildGeminiCliForbiddenActionHint(parsed),
            isForbidden: true,
            retryable: false,
            entitlement: (0, provider_entitlement_evidence_1.buildProviderEntitlementEvidence)({
                normalizedTier: 'unknown',
                source: 'runtime_inference',
                confidence: 'medium',
                accessState: 'not_entitled',
                capacityState: 'unknown',
            }),
        });
    }
    if (status === 429) {
        if ((0, provider_entitlement_evidence_1.isModelCapacityExhausted)(parsed.message, parsed.errorDetail, parsed.errorCode)) {
            return buildGeminiCliFailureResult(accountId, projectId, {
                error: parsed.message || 'Model capacity exhausted for this account right now',
                httpStatus: 429,
                errorCode: 'capacity_exhausted',
                errorDetail: parsed.errorDetail,
                actionHint: 'Retry later or switch to another Gemini model. This indicates temporary model capacity, not an authentication failure.',
                retryable: true,
                entitlement: (0, provider_entitlement_evidence_1.buildProviderEntitlementEvidence)({
                    normalizedTier: 'unknown',
                    source: 'runtime_inference',
                    confidence: 'medium',
                    accessState: 'entitled',
                    capacityState: 'capacity_exhausted',
                    notes: 'Upstream returned MODEL_CAPACITY_EXHAUSTED for this model.',
                }),
            });
        }
        return buildGeminiCliFailureResult(accountId, projectId, {
            error: parsed.message || 'Rate limited - try again later',
            httpStatus: 429,
            errorCode: parsed.errorCode || 'rate_limited',
            errorDetail: parsed.errorDetail,
            actionHint: 'Retry after a short delay.',
            retryable: true,
            entitlement: (0, provider_entitlement_evidence_1.buildProviderEntitlementEvidence)({
                normalizedTier: 'unknown',
                source: 'runtime_inference',
                confidence: 'low',
                accessState: 'unknown',
                capacityState: 'rate_limited',
            }),
        });
    }
    if (status >= 500) {
        return buildGeminiCliFailureResult(accountId, projectId, {
            error: parsed.message || `Gemini quota service unavailable (HTTP ${status})`,
            httpStatus: status,
            errorCode: parsed.errorCode || 'provider_unavailable',
            errorDetail: parsed.errorDetail,
            actionHint: 'Retry later. This looks like a temporary Google upstream problem.',
            retryable: true,
        });
    }
    return buildGeminiCliFailureResult(accountId, projectId, {
        error: parsed.message || `Gemini quota request failed (HTTP ${status})`,
        httpStatus: status,
        errorCode: parsed.errorCode || 'quota_request_failed',
        errorDetail: parsed.errorDetail,
        actionHint: 'Inspect the upstream response details and retry if appropriate.',
        retryable: false,
    });
}
/**
 * Build GeminiCliBucket array from API response
 * Groups buckets by model series and token type
 */
function buildGeminiCliBuckets(rawBuckets) {
    const parsedBuckets = rawBuckets
        .map((bucket) => {
        const modelId = normalizeStringValue(bucket.model_id ?? bucket.modelId);
        if (!modelId)
            return null;
        const tokenType = normalizeStringValue(bucket.token_type ?? bucket.tokenType);
        const remainingFractionRaw = normalizeNumberValue(bucket.remaining_fraction ?? bucket.remainingFraction);
        const remainingAmount = normalizeNumberValue(bucket.remaining_amount ?? bucket.remainingAmount);
        const resetTime = normalizeStringValue(bucket.reset_time ?? bucket.resetTime);
        let fallbackFraction = null;
        if (remainingAmount !== null) {
            fallbackFraction = remainingAmount <= 0 ? 0 : null;
        }
        else if (resetTime) {
            fallbackFraction = 0;
        }
        return {
            modelId,
            tokenType,
            remainingFraction: remainingFractionRaw ?? fallbackFraction ?? 1,
            remainingAmount,
            resetTime,
        };
    })
        .filter((bucket) => bucket !== null);
    return (0, gemini_cli_quota_normalizer_1.buildGeminiCliBucketsFromParsedBuckets)(parsedBuckets);
}
exports.buildGeminiCliBuckets = buildGeminiCliBuckets;
/**
 * Internal helper: Fetch quota with validated auth data
 * Extracted to support auto-refresh retry logic
 */
async function fetchWithAuthData(authData, accountId, verbose) {
    if (!authData.projectId) {
        const error = 'Cannot resolve project ID from auth file';
        if (verbose)
            console.error(`[!] Error: ${error}`);
        return buildGeminiCliFailureResult(accountId, null, {
            error,
            errorCode: 'missing_project_id',
            actionHint: 'Run ccs gemini --auth to reconnect this account and recover the project ID.',
            retryable: false,
        });
    }
    const authContext = {};
    const supplementaryPromise = fetchGeminiCliSupplementary(accountId, authData.accessToken, authData.projectId, verbose, authContext);
    const requestBody = JSON.stringify({ project: authData.projectId });
    try {
        const response = await performGeminiCliRequest(accountId, authData.accessToken, GEMINI_CLI_QUOTA_URL, requestBody, authData.isExpired, authContext);
        if (verbose) {
            const source = response.viaManagement ? 'managed' : 'direct';
            console.error(`[i] Gemini CLI API status via ${source}: ${response.status}`);
        }
        if (response.status !== 200) {
            return buildGeminiCliHttpFailureResult(accountId, authData.projectId, response.status, response.bodyText);
        }
        const data = response.json;
        const rawBuckets = data?.buckets || [];
        const buckets = buildGeminiCliBuckets(rawBuckets);
        const supplementary = await supplementaryPromise;
        if (verbose)
            console.error(`[i] Gemini CLI buckets found: ${buckets.length}`);
        if (supplementary.normalizedTier !== 'unknown') {
            (0, account_manager_1.setAccountTier)('gemini', accountId, supplementary.normalizedTier);
        }
        return {
            success: true,
            buckets,
            projectId: authData.projectId,
            tierLabel: supplementary.tierLabel,
            tierId: supplementary.tierId,
            creditBalance: supplementary.creditBalance,
            entitlement: (0, provider_entitlement_evidence_1.buildProviderEntitlementEvidence)({
                normalizedTier: supplementary.normalizedTier,
                rawTierId: supplementary.tierId,
                rawTierLabel: supplementary.tierLabel,
                source: supplementary.tierId ? 'runtime_api' : 'runtime_inference',
                confidence: supplementary.tierId ? 'high' : 'medium',
                accessState: 'entitled',
                capacityState: 'available',
            }),
            lastUpdated: Date.now(),
            accountId,
        };
    }
    catch (err) {
        if (err instanceof GeminiManagedAuthUnavailableError) {
            return buildGeminiCliFailureResult(accountId, authData.projectId, {
                error: 'Gemini delegated auth refresh is temporarily unavailable',
                errorCode: 'managed_auth_unavailable',
                errorDetail: err.message,
                actionHint: 'Retry later. CLIProxy management could not refresh this Gemini account.',
                retryable: true,
            });
        }
        const errorMsg = err instanceof Error && err.name === 'AbortError'
            ? 'Request timeout'
            : err instanceof Error
                ? err.message
                : 'Unknown error';
        if (verbose)
            console.error(`[!] Gemini CLI quota error: ${errorMsg}`);
        return buildGeminiCliFailureResult(accountId, authData.projectId, {
            error: errorMsg,
            errorCode: err instanceof Error && err.name === 'AbortError' ? 'network_timeout' : 'network_error',
            actionHint: 'Retry later. This looks temporary.',
            retryable: true,
            httpStatus: err instanceof Error && err.name === 'AbortError' ? 408 : undefined,
        });
    }
}
/**
 * Fetch quota for a single Gemini CLI account
 *
 * @param accountId - Account identifier (email)
 * @param verbose - Show detailed diagnostics
 * @returns Quota result with buckets and percentages
 */
async function fetchGeminiCliQuota(accountId, verbose = false) {
    if (verbose)
        console.error(`[i] Fetching Gemini CLI quota for ${accountId}...`);
    const authData = readGeminiCliAuthData(accountId);
    if (!authData) {
        const error = 'Auth file not found for Gemini account';
        if (verbose)
            console.error(`[!] Error: ${error}`);
        return buildGeminiCliFailureResult(accountId, null, {
            error,
            errorCode: 'auth_file_missing',
            actionHint: 'Run ccs gemini --auth to reconnect this account.',
            retryable: false,
        });
    }
    if (authData.isExpired && verbose) {
        const expiresAt = (0, auth_utils_1.getTokenExpiryTimestamp)(authData.expiresAt);
        const expiryLabel = expiresAt ? new Date(expiresAt).toISOString() : 'unknown';
        console.error(`[i] Gemini access token is expired (${expiryLabel}); quota requests will defer to managed auth when available.`);
    }
    return await fetchWithAuthData(authData, accountId, verbose);
}
exports.fetchGeminiCliQuota = fetchGeminiCliQuota;
/**
 * Fetch quota for all Gemini CLI accounts
 *
 * @param verbose - Show detailed diagnostics
 * @returns Array of account quotas
 */
async function fetchAllGeminiCliQuotas(verbose = false) {
    const accounts = (0, account_manager_1.getProviderAccounts)('gemini');
    if (accounts.length === 0) {
        return [];
    }
    const results = await Promise.all(accounts.map(async (account) => ({
        account: account.id,
        quota: await fetchGeminiCliQuota(account.id, verbose),
    })));
    return results;
}
exports.fetchAllGeminiCliQuotas = fetchAllGeminiCliQuotas;
exports.__testExports = {
    sanitizeGeminiCliErrorDetail,
    extractGeminiCliNestedMessage,
    parseGeminiCliErrorBody,
    buildGeminiCliForbiddenActionHint,
};
//# sourceMappingURL=quota-fetcher-gemini-cli.js.map