"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.hasUsageTotals = exports.hasUsageDetails = exports.buildUsageResponseFromApiKeyUsage = exports.mergeUsageResponseWithMissingDetails = exports.mergeUsageResponses = exports.buildUsageResponseFromQueueRecords = void 0;
function asRecord(value) {
    return value !== null && typeof value === 'object' && !Array.isArray(value)
        ? value
        : null;
}
function asString(value, fallback) {
    return typeof value === 'string' && value.trim() ? value.trim() : fallback;
}
function asNumber(value) {
    if (typeof value === 'number' && Number.isFinite(value)) {
        return value;
    }
    if (typeof value === 'string' && value.trim()) {
        const parsed = Number(value);
        return Number.isFinite(parsed) ? parsed : 0;
    }
    return 0;
}
function asBoolean(value) {
    return value === true;
}
function normalizeTokens(rawTokens) {
    const tokens = asRecord(rawTokens) ?? {};
    const input = asNumber(tokens.input_tokens);
    const output = asNumber(tokens.output_tokens);
    const reasoning = asNumber(tokens.reasoning_tokens);
    const cached = asNumber(tokens.cached_tokens);
    const explicitTotal = asNumber(tokens.total_tokens);
    const total = explicitTotal || input + output + reasoning + cached;
    return {
        input_tokens: input,
        output_tokens: output,
        reasoning_tokens: reasoning,
        cached_tokens: cached,
        total_tokens: total,
    };
}
function normalizeQueueRecord(record) {
    const raw = asRecord(record);
    if (!raw) {
        return null;
    }
    const provider = asString(raw.provider, 'unknown');
    const model = asString(raw.model, asString(raw.alias, 'unknown'));
    const source = asString(raw.source, 'unknown');
    const authIndex = typeof raw.auth_index === 'string' || typeof raw.auth_index === 'number'
        ? raw.auth_index
        : source;
    return {
        timestamp: asString(raw.timestamp, new Date().toISOString()),
        provider,
        model,
        alias: asString(raw.alias, model),
        source,
        auth_index: authIndex,
        request_id: asString(raw.request_id, ''),
        tokens: normalizeTokens(raw.tokens),
        failed: asBoolean(raw.failed),
    };
}
function ensureProviderBucket(response, provider) {
    const usage = (response.usage ?? (response.usage = { apis: {} }));
    const apis = (usage.apis ?? (usage.apis = {}));
    return (apis[provider] ?? (apis[provider] = { total_requests: 0, total_tokens: 0, models: {} }));
}
function ensureModelBucket(providerBucket, model) {
    const models = (providerBucket.models ?? (providerBucket.models = {}));
    return (models[model] ?? (models[model] = { total_requests: 0, total_tokens: 0, details: [] }));
}
function addDetail(response, provider, model, detail) {
    const usage = (response.usage ?? (response.usage = { apis: {} }));
    const providerBucket = ensureProviderBucket(response, provider);
    const modelBucket = ensureModelBucket(providerBucket, model);
    const totalTokens = detail.tokens?.total_tokens ?? 0;
    usage.total_requests = (usage.total_requests ?? 0) + 1;
    usage.total_tokens = (usage.total_tokens ?? 0) + totalTokens;
    if (detail.failed) {
        usage.failure_count = (usage.failure_count ?? 0) + 1;
        response.failed_requests = (response.failed_requests ?? 0) + 1;
    }
    else {
        usage.success_count = (usage.success_count ?? 0) + 1;
    }
    providerBucket.total_requests = (providerBucket.total_requests ?? 0) + 1;
    providerBucket.total_tokens = (providerBucket.total_tokens ?? 0) + totalTokens;
    modelBucket.total_requests = (modelBucket.total_requests ?? 0) + 1;
    modelBucket.total_tokens = (modelBucket.total_tokens ?? 0) + totalTokens;
    (modelBucket.details ?? (modelBucket.details = [])).push(detail);
}
function createDetailSignature(provider, model, detail) {
    return [
        provider,
        model,
        detail.request_id?.trim() ?? '',
        detail.timestamp,
        detail.source,
        String(detail.auth_index),
        detail.tokens?.input_tokens ?? 0,
        detail.tokens?.output_tokens ?? 0,
        detail.tokens?.reasoning_tokens ?? 0,
        detail.tokens?.cached_tokens ?? 0,
        detail.tokens?.total_tokens ?? 0,
        detail.failed ? '1' : '0',
    ].join('|');
}
function collectResponseDetails(response) {
    const entries = [];
    for (const [provider, providerData] of Object.entries(response.usage?.apis ?? {})) {
        for (const [model, modelData] of Object.entries(providerData.models ?? {})) {
            for (const detail of modelData.details ?? []) {
                entries.push({ provider, model, detail });
            }
        }
    }
    return entries;
}
function buildUsageResponseFromQueueRecords(records) {
    const response = {
        failed_requests: 0,
        usage: {
            total_requests: 0,
            success_count: 0,
            failure_count: 0,
            total_tokens: 0,
            apis: {},
        },
    };
    for (const rawRecord of records) {
        const record = normalizeQueueRecord(rawRecord);
        if (!record) {
            continue;
        }
        addDetail(response, record.provider ?? 'unknown', record.model ?? 'unknown', {
            timestamp: record.timestamp ?? new Date().toISOString(),
            source: record.source ?? 'unknown',
            auth_index: record.auth_index ?? record.source ?? 'unknown',
            request_id: record.request_id || undefined,
            tokens: normalizeTokens(record.tokens),
            failed: record.failed === true,
        });
    }
    return response;
}
exports.buildUsageResponseFromQueueRecords = buildUsageResponseFromQueueRecords;
function mergeUsageResponses(base, incoming) {
    const merged = buildUsageResponseFromQueueRecords([]);
    const seen = new Set();
    for (const entry of [...collectResponseDetails(base), ...collectResponseDetails(incoming)]) {
        const signature = createDetailSignature(entry.provider, entry.model, entry.detail);
        if (seen.has(signature)) {
            continue;
        }
        seen.add(signature);
        addDetail(merged, entry.provider, entry.model, entry.detail);
    }
    return merged;
}
exports.mergeUsageResponses = mergeUsageResponses;
function cloneUsageResponse(response) {
    return {
        failed_requests: response.failed_requests ?? 0,
        usage: {
            total_requests: response.usage?.total_requests ?? 0,
            success_count: response.usage?.success_count ?? 0,
            failure_count: response.usage?.failure_count ?? 0,
            total_tokens: response.usage?.total_tokens ?? 0,
            apis: Object.fromEntries(Object.entries(response.usage?.apis ?? {}).map(([provider, providerData]) => [
                provider,
                {
                    total_requests: providerData.total_requests ?? 0,
                    total_tokens: providerData.total_tokens ?? 0,
                    models: Object.fromEntries(Object.entries(providerData.models ?? {}).map(([model, modelData]) => [
                        model,
                        {
                            total_requests: modelData.total_requests ?? 0,
                            total_tokens: modelData.total_tokens ?? 0,
                            details: (modelData.details ?? []).map((detail) => ({
                                ...detail,
                                tokens: { ...detail.tokens },
                            })),
                        },
                    ])),
                },
            ])),
        },
    };
}
function cloneDetail(detail) {
    return {
        ...detail,
        tokens: { ...detail.tokens },
    };
}
function createMissingDetailMergeKey(provider, model, detail) {
    return [
        provider,
        model,
        detail.request_id?.trim() ?? '',
        detail.timestamp,
        detail.source?.trim() ?? '',
        String(detail.auth_index ?? '').trim(),
        detail.tokens?.input_tokens ?? 0,
        detail.tokens?.output_tokens ?? 0,
        detail.tokens?.reasoning_tokens ?? 0,
        detail.tokens?.cached_tokens ?? 0,
        detail.tokens?.total_tokens ?? 0,
        detail.failed ? '1' : '0',
    ].join('|');
}
function extractDuplicateIdentityValue(value) {
    const authFileMatch = value.match(/(?:^|\s)auth_file=("[^"]+"|'[^']+'|[^\s]+)/i);
    const rawValue = authFileMatch?.[1] ?? value;
    const unquotedValue = rawValue.trim().replace(/^['"]|['"]$/g, '');
    const pipeCandidate = unquotedValue.split('|').pop() ?? unquotedValue;
    return pipeCandidate.split(/[\\/]/).pop() ?? pipeCandidate.trim();
}
function stripKnownAuthPlanSuffix(value) {
    return value.replace(/-(?:free|plus|pro|team|business|enterprise)$/i, '');
}
function normalizeDuplicateIdentity(provider, value) {
    const rawValue = String(value ?? '').trim();
    if (!rawValue) {
        return '';
    }
    const normalizedProvider = provider.trim().toLowerCase();
    const identityValue = extractDuplicateIdentityValue(rawValue);
    const hadJsonExtension = /\.json$/i.test(identityValue);
    let candidate = identityValue.replace(/\.json$/i, '');
    const providerPrefix = `${normalizedProvider}-`;
    if (candidate.toLowerCase().startsWith(providerPrefix)) {
        candidate = candidate.slice(providerPrefix.length);
    }
    candidate = candidate.replace(/^[a-f0-9]{8}[-_]/i, '').trim();
    if (hadJsonExtension) {
        candidate = stripKnownAuthPlanSuffix(candidate);
    }
    return candidate ? candidate.toLowerCase() : rawValue.toLowerCase();
}
function resolveCompleteDetailDuplicateIdentity(provider, detail) {
    return (normalizeDuplicateIdentity(provider, detail.source) ||
        normalizeDuplicateIdentity(provider, detail.auth_index) ||
        'unknown');
}
function createLikelyLogDuplicateKey(provider, model, detail) {
    return [
        provider,
        model,
        resolveCompleteDetailDuplicateIdentity(provider, detail),
        detail.failed ? '1' : '0',
    ].join('|');
}
function createRequestIdDuplicateKey(provider, model, detail) {
    const requestId = detail.request_id?.trim();
    return requestId ? [provider, model, requestId, detail.failed ? '1' : '0'].join('|') : '';
}
function parseDetailTimestamp(detail) {
    const timestampMs = Date.parse(detail.timestamp);
    return Number.isFinite(timestampMs) ? timestampMs : null;
}
const TOKENLESS_LOG_DUPLICATE_WINDOW_MS = 5000;
function detailHasTokenUsage(detail) {
    return ((detail.tokens?.input_tokens ?? 0) > 0 ||
        (detail.tokens?.output_tokens ?? 0) > 0 ||
        (detail.tokens?.reasoning_tokens ?? 0) > 0 ||
        (detail.tokens?.cached_tokens ?? 0) > 0 ||
        (detail.tokens?.total_tokens ?? 0) > 0);
}
function collectDetailMergeKeyCounts(response) {
    const counts = new Map();
    for (const entry of collectResponseDetails(response)) {
        const key = createMissingDetailMergeKey(entry.provider, entry.model, entry.detail);
        counts.set(key, (counts.get(key) ?? 0) + 1);
    }
    return counts;
}
function collectLikelyLogDuplicateCandidates(response) {
    const candidates = {
        byRequestId: new Map(),
        byIdentity: new Map(),
    };
    for (const [provider, providerData] of Object.entries(response.usage?.apis ?? {})) {
        for (const [model, modelData] of Object.entries(providerData.models ?? {})) {
            const details = modelData.details ?? [];
            const canUseIdentityWindow = (modelData.total_requests ?? 0) <= details.length;
            for (const detail of details) {
                const requestIdKey = createRequestIdDuplicateKey(provider, model, detail);
                if (requestIdKey) {
                    candidates.byRequestId.set(requestIdKey, (candidates.byRequestId.get(requestIdKey) ?? 0) + 1);
                }
                if (!canUseIdentityWindow) {
                    continue;
                }
                const timestampMs = parseDetailTimestamp(detail);
                if (timestampMs === null) {
                    continue;
                }
                const key = createLikelyLogDuplicateKey(provider, model, detail);
                const timestamps = candidates.byIdentity.get(key) ?? [];
                timestamps.push(timestampMs);
                candidates.byIdentity.set(key, timestamps);
            }
        }
    }
    return candidates;
}
function consumeDetailKey(counts, key) {
    const count = counts.get(key) ?? 0;
    if (count <= 0) {
        return false;
    }
    if (count === 1) {
        counts.delete(key);
    }
    else {
        counts.set(key, count - 1);
    }
    return true;
}
function consumeCount(counts, key) {
    const count = counts.get(key) ?? 0;
    if (count <= 0) {
        return false;
    }
    if (count === 1) {
        counts.delete(key);
    }
    else {
        counts.set(key, count - 1);
    }
    return true;
}
function consumeLikelyLogDuplicateCandidate(candidates, provider, model, detail) {
    if (detailHasTokenUsage(detail)) {
        return false;
    }
    const requestIdKey = createRequestIdDuplicateKey(provider, model, detail);
    if (requestIdKey && consumeCount(candidates.byRequestId, requestIdKey)) {
        return true;
    }
    const timestampMs = parseDetailTimestamp(detail);
    if (timestampMs === null) {
        return false;
    }
    const key = createLikelyLogDuplicateKey(provider, model, detail);
    const timestamps = candidates.byIdentity.get(key) ?? [];
    const index = timestamps.findIndex((candidateTimestampMs) => Math.abs(candidateTimestampMs - timestampMs) <= TOKENLESS_LOG_DUPLICATE_WINDOW_MS);
    if (index === -1) {
        return false;
    }
    timestamps.splice(index, 1);
    if (timestamps.length === 0) {
        candidates.byIdentity.delete(key);
    }
    else {
        candidates.byIdentity.set(key, timestamps);
    }
    return true;
}
function countProviderDetails(providerBucket) {
    return Object.values(providerBucket.models ?? {}).reduce((total, modelData) => total + (modelData.details ?? []).length, 0);
}
function appendDetailToExistingProvider(merged, provider, model, detail, options) {
    const providerBucket = ensureProviderBucket(merged, provider);
    const shouldFillAggregateOnly = (providerBucket.total_requests ?? 0) > countProviderDetails(providerBucket);
    if (!shouldFillAggregateOnly) {
        if (!options.appendExtraDetails) {
            return;
        }
        addDetail(merged, provider, model, cloneDetail(detail));
        return;
    }
    const modelBucket = ensureModelBucket(providerBucket, model);
    const shouldFillModelAggregateOnly = (modelBucket.total_requests ?? 0) > (modelBucket.details ?? []).length;
    if (!shouldFillModelAggregateOnly) {
        modelBucket.total_requests = (modelBucket.total_requests ?? 0) + 1;
    }
    (modelBucket.details ?? (modelBucket.details = [])).push(cloneDetail(detail));
}
function mergeUsageResponseWithMissingDetails(base, incoming, options = {}) {
    if (!incoming || !hasUsageDetails(incoming)) {
        return base;
    }
    const normalizedOptions = {
        appendExtraDetails: options.appendExtraDetails ?? true,
    };
    const merged = cloneUsageResponse(base);
    const existingDetailCounts = collectDetailMergeKeyCounts(base);
    const likelyLogDuplicateCandidates = collectLikelyLogDuplicateCandidates(base);
    for (const entry of collectResponseDetails(incoming)) {
        const mergeKey = createMissingDetailMergeKey(entry.provider, entry.model, entry.detail);
        if (consumeDetailKey(existingDetailCounts, mergeKey)) {
            continue;
        }
        if (consumeLikelyLogDuplicateCandidate(likelyLogDuplicateCandidates, entry.provider, entry.model, entry.detail)) {
            continue;
        }
        if (base.usage?.apis?.[entry.provider]) {
            appendDetailToExistingProvider(merged, entry.provider, entry.model, entry.detail, normalizedOptions);
            continue;
        }
        addDetail(merged, entry.provider, entry.model, cloneDetail(entry.detail));
    }
    return merged;
}
exports.mergeUsageResponseWithMissingDetails = mergeUsageResponseWithMissingDetails;
function buildUsageResponseFromApiKeyUsage(rawResponse) {
    const response = buildUsageResponseFromQueueRecords([]);
    const usage = response.usage ?? {
        total_requests: 0,
        success_count: 0,
        failure_count: 0,
        total_tokens: 0,
        apis: {},
    };
    response.usage = usage;
    const byProvider = asRecord(rawResponse);
    if (!byProvider) {
        return response;
    }
    for (const [provider, sources] of Object.entries(byProvider)) {
        const sourceRecords = asRecord(sources);
        if (!sourceRecords) {
            continue;
        }
        let providerRequests = 0;
        for (const entry of Object.values(sourceRecords)) {
            const usageEntry = asRecord(entry);
            if (!usageEntry) {
                continue;
            }
            const success = asNumber(usageEntry.success);
            const failed = asNumber(usageEntry.failed);
            providerRequests += success + failed;
            usage.success_count = (usage.success_count ?? 0) + success;
            usage.failure_count = (usage.failure_count ?? 0) + failed;
            response.failed_requests = (response.failed_requests ?? 0) + failed;
        }
        if (providerRequests > 0) {
            const providerBucket = ensureProviderBucket(response, provider);
            providerBucket.total_requests = (providerBucket.total_requests ?? 0) + providerRequests;
            usage.total_requests = (usage.total_requests ?? 0) + providerRequests;
        }
    }
    return response;
}
exports.buildUsageResponseFromApiKeyUsage = buildUsageResponseFromApiKeyUsage;
function hasUsageDetails(response) {
    return collectResponseDetails(response).length > 0;
}
exports.hasUsageDetails = hasUsageDetails;
function hasUsageTotals(response) {
    return (response.usage?.total_requests ?? 0) > 0;
}
exports.hasUsageTotals = hasUsageTotals;
//# sourceMappingURL=usage-compatibility-transformer.js.map