"use strict";
/**
 * Quota Fetcher for Claude (Anthropic) Accounts
 *
 * Fetches OAuth usage windows from Claude API and normalizes 5h + weekly windows.
 */
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.fetchAllClaudeQuotas = exports.fetchClaudeQuota = exports.CLAUDE_OAUTH_USAGE_URL = exports.buildClaudeCoreUsageSummary = exports.buildClaudeQuotaWindows = void 0;
const path = __importStar(require("node:path"));
const fsp = __importStar(require("node:fs/promises"));
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 quota_fetcher_claude_normalizer_1 = require("./quota-fetcher-claude-normalizer");
Object.defineProperty(exports, "buildClaudeQuotaWindows", { enumerable: true, get: function () { return quota_fetcher_claude_normalizer_1.buildClaudeQuotaWindows; } });
Object.defineProperty(exports, "buildClaudeCoreUsageSummary", { enumerable: true, get: function () { return quota_fetcher_claude_normalizer_1.buildClaudeCoreUsageSummary; } });
exports.CLAUDE_OAUTH_USAGE_URL = 'https://api.anthropic.com/api/oauth/usage';
const CLAUDE_QUOTA_TIMEOUT_MS = 10000;
const CLAUDE_QUOTA_MAX_ATTEMPTS = 2;
const CLAUDE_OAUTH_BETA_HEADER = 'oauth-2025-04-20';
const CLAUDE_QUOTA_ERROR_BODY_MAX_BYTES = 8192;
function asString(value) {
    return typeof value === 'string' && value.trim().length > 0 ? value.trim() : null;
}
function toObject(value) {
    if (typeof value !== 'object' || value === null || Array.isArray(value))
        return null;
    return value;
}
function extractAccessToken(data) {
    const direct = asString(data['access_token']);
    if (direct)
        return direct;
    const nested = toObject(data['token']);
    if (nested) {
        const nestedToken = asString(nested['access_token']);
        if (nestedToken)
            return nestedToken;
    }
    return null;
}
function extractExpiry(data) {
    const direct = asString(data['expired']);
    if (direct)
        return direct;
    const nested = toObject(data['token']);
    if (nested) {
        return asString(nested['expiry']);
    }
    return null;
}
function isAuthExpired(expiry) {
    return expiry ? (0, auth_utils_1.isTokenExpired)(expiry) : false;
}
function extractErrorMessage(payload) {
    const root = toObject(payload);
    if (!root)
        return null;
    const direct = asString(root['message']);
    if (direct)
        return direct;
    const nested = toObject(root['error']);
    if (!nested)
        return null;
    return asString(nested['message']);
}
async function readResponseErrorMessage(response) {
    try {
        const contentLength = Number(response.headers.get('content-length') ?? '0');
        if (Number.isFinite(contentLength) && contentLength > CLAUDE_QUOTA_ERROR_BODY_MAX_BYTES) {
            return null;
        }
        const reader = response.body?.getReader();
        if (!reader)
            return null;
        const decoder = new TextDecoder();
        const chunks = [];
        let totalBytes = 0;
        while (true) {
            const { done, value } = await reader.read();
            if (done)
                break;
            if (!value)
                continue;
            totalBytes += value.byteLength;
            if (totalBytes > CLAUDE_QUOTA_ERROR_BODY_MAX_BYTES)
                return null;
            chunks.push(decoder.decode(value, { stream: true }));
        }
        chunks.push(decoder.decode());
        const body = chunks.join('').trim();
        if (!body)
            return null;
        try {
            const parsed = JSON.parse(body);
            const extracted = extractErrorMessage(parsed);
            if (extracted)
                return extracted;
        }
        catch {
            // fall through to plain-text fallback
        }
        return body;
    }
    catch {
        return null;
    }
}
async function readJsonFile(filePath) {
    try {
        const raw = await fsp.readFile(filePath, 'utf-8');
        const parsed = JSON.parse(raw);
        return toObject(parsed);
    }
    catch {
        return null;
    }
}
async function readAuthCandidate(filePath) {
    const data = await readJsonFile(filePath);
    if (!data)
        return null;
    const accessToken = extractAccessToken(data);
    if (!accessToken)
        return null;
    const expiry = extractExpiry(data);
    return {
        accessToken,
        isExpired: isAuthExpired(expiry),
    };
}
async function readClaudeAuthData(accountId) {
    const authDirs = [(0, config_generator_1.getAuthDir)(), (0, account_manager_1.getPausedDir)()];
    const sanitizedId = (0, auth_utils_1.sanitizeEmail)(accountId);
    const expectedFiles = [`claude-${sanitizedId}.json`, `anthropic-${sanitizedId}.json`];
    for (const authDir of authDirs) {
        for (const expectedFile of expectedFiles) {
            const filePath = path.join(authDir, expectedFile);
            const authData = await readAuthCandidate(filePath);
            if (authData) {
                return authData;
            }
        }
        let files;
        try {
            files = await fsp.readdir(authDir);
        }
        catch {
            continue;
        }
        for (const file of files) {
            if (!file.endsWith('.json') ||
                (!file.startsWith('claude-') && !file.startsWith('anthropic-'))) {
                continue;
            }
            const filePath = path.join(authDir, file);
            const data = await readJsonFile(filePath);
            if (!data)
                continue;
            const accessToken = extractAccessToken(data);
            if (!accessToken)
                continue;
            const fileEmail = asString(data['email']);
            const typeValue = asString(data['type']);
            const isClaudeType = typeValue === null || typeValue === 'claude' || typeValue === 'anthropic';
            const matchesEmail = fileEmail === accountId;
            const matchesFile = file.includes(sanitizedId);
            if ((matchesEmail || matchesFile) && isClaudeType) {
                const expiry = extractExpiry(data);
                return {
                    accessToken,
                    isExpired: isAuthExpired(expiry),
                };
            }
        }
    }
    return null;
}
function buildEmptyResult(error, accountId, needsReauth = false) {
    return {
        success: false,
        windows: [],
        coreUsage: { fiveHour: null, weekly: null },
        lastUpdated: Date.now(),
        error,
        accountId,
        needsReauth,
    };
}
/**
 * Fetch quota for a single Claude account.
 */
async function fetchClaudeQuota(accountId, verbose = false) {
    const authData = await readClaudeAuthData(accountId);
    if (!authData) {
        return buildEmptyResult('Auth file not found for Claude account', accountId);
    }
    if (authData.isExpired) {
        return buildEmptyResult('Token expired - re-authenticate with ccs cliproxy auth claude', accountId, true);
    }
    let lastError = 'Unknown error';
    for (let attempt = 1; attempt <= CLAUDE_QUOTA_MAX_ATTEMPTS; attempt++) {
        const controller = new AbortController();
        const timeoutId = setTimeout(() => controller.abort(), CLAUDE_QUOTA_TIMEOUT_MS);
        try {
            const response = await fetch(exports.CLAUDE_OAUTH_USAGE_URL, {
                method: 'GET',
                signal: controller.signal,
                headers: {
                    Authorization: `Bearer ${authData.accessToken}`,
                    Accept: 'application/json',
                    'Content-Type': 'application/json',
                    'anthropic-beta': CLAUDE_OAUTH_BETA_HEADER,
                },
            });
            if (verbose) {
                console.error(`[i] Claude OAuth usage status: ${response.status} (attempt ${attempt})`);
            }
            if (response.status === 401) {
                const errorMessage = await readResponseErrorMessage(response);
                clearTimeout(timeoutId);
                return buildEmptyResult(errorMessage || 'Authentication required for Claude OAuth usage', accountId, true);
            }
            if (response.status === 404) {
                clearTimeout(timeoutId);
                return buildEmptyResult('Claude OAuth usage endpoint not found', accountId);
            }
            if (response.status === 403) {
                clearTimeout(timeoutId);
                return buildEmptyResult('Not authorized for Claude OAuth usage', accountId);
            }
            if (!response.ok) {
                lastError =
                    (await readResponseErrorMessage(response)) ||
                        `Claude OAuth usage API error: ${response.status}`;
                if (attempt < CLAUDE_QUOTA_MAX_ATTEMPTS &&
                    (response.status === 429 || response.status >= 500)) {
                    clearTimeout(timeoutId);
                    continue;
                }
                clearTimeout(timeoutId);
                return buildEmptyResult(lastError, accountId);
            }
            let payload;
            try {
                payload = await response.json();
            }
            catch {
                clearTimeout(timeoutId);
                return buildEmptyResult('Invalid Claude OAuth usage format', accountId);
            }
            if (!toObject(payload)) {
                clearTimeout(timeoutId);
                return buildEmptyResult('Invalid Claude OAuth usage format', accountId);
            }
            const windows = (0, quota_fetcher_claude_normalizer_1.buildClaudeQuotaWindows)(payload);
            const coreUsage = (0, quota_fetcher_claude_normalizer_1.buildClaudeCoreUsageSummary)(windows);
            clearTimeout(timeoutId);
            return {
                success: true,
                windows,
                coreUsage,
                lastUpdated: Date.now(),
                accountId,
            };
        }
        catch (error) {
            clearTimeout(timeoutId);
            lastError =
                error instanceof Error && error.name === 'AbortError'
                    ? 'Claude OAuth usage request timeout'
                    : error instanceof Error
                        ? error.message
                        : 'Unknown error';
            if (verbose) {
                const errorDetails = error instanceof Error ? (error.stack ?? error.message) : JSON.stringify(error);
                console.error(`[!] Claude OAuth usage failed (attempt ${attempt}): ${lastError}${errorDetails ? `\n${errorDetails}` : ''}`);
            }
            if (attempt >= CLAUDE_QUOTA_MAX_ATTEMPTS) {
                clearTimeout(timeoutId);
                return buildEmptyResult(lastError, accountId);
            }
        }
    }
    return buildEmptyResult(lastError, accountId);
}
exports.fetchClaudeQuota = fetchClaudeQuota;
/**
 * Fetch quota for all Claude accounts.
 */
async function fetchAllClaudeQuotas(verbose = false) {
    const accounts = (0, account_manager_1.getProviderAccounts)('claude');
    const results = await Promise.all(accounts.map(async (account) => ({
        account: account.id,
        quota: await fetchClaudeQuota(account.id, verbose),
    })));
    return results;
}
exports.fetchAllClaudeQuotas = fetchAllClaudeQuotas;
//# sourceMappingURL=quota-fetcher-claude.js.map