"use strict";
// SPDX-License-Identifier: AGPL-3.0-or-later
// Copyright (C) 2024-2026 Gracker (Chris)
// This file is part of SmartPerfetto. See LICENSE for details.
Object.defineProperty(exports, "__esModule", { value: true });
exports.TP_FRONTEND_ACTIVE_MS_ENV = exports.TP_SHARED_QUEUE_THRESHOLD_ENV = exports.TP_SLOW_QUERY_MS_ENV = void 0;
exports.sharedQueueLengthForTrace = sharedQueueLengthForTrace;
exports.hasRecentFrontendHolder = hasRecentFrontendHolder;
exports.buildTraceProcessorLeaseModeDecision = buildTraceProcessorLeaseModeDecision;
exports.decideTraceProcessorLeaseMode = decideTraceProcessorLeaseMode;
exports.TP_SLOW_QUERY_MS_ENV = 'SMARTPERFETTO_TP_SLOW_QUERY_MS';
exports.TP_SHARED_QUEUE_THRESHOLD_ENV = 'SMARTPERFETTO_TP_SHARED_QUEUE_THRESHOLD';
exports.TP_FRONTEND_ACTIVE_MS_ENV = 'SMARTPERFETTO_TP_FRONTEND_ACTIVE_MS';
const DEFAULT_SLOW_QUERY_MS = 5000;
const DEFAULT_SHARED_QUEUE_THRESHOLD = 5;
const DEFAULT_FRONTEND_ACTIVE_MS = 30000;
function parsePositiveNumber(value) {
    if (!value)
        return null;
    const parsed = Number(value);
    return Number.isFinite(parsed) && parsed > 0 ? parsed : null;
}
function parseNonNegativeNumber(value) {
    if (typeof value !== 'number' && typeof value !== 'string')
        return undefined;
    const parsed = Number(value);
    return Number.isFinite(parsed) && parsed >= 0 ? parsed : undefined;
}
function normalizeAnalysisMode(value) {
    if (typeof value !== 'string')
        return undefined;
    const normalized = value.trim().toLowerCase();
    return normalized || undefined;
}
function resolveSlowQueryThresholdMs(env) {
    return parsePositiveNumber(env[exports.TP_SLOW_QUERY_MS_ENV]) ?? DEFAULT_SLOW_QUERY_MS;
}
function resolveSharedQueueThreshold(env) {
    return parsePositiveNumber(env[exports.TP_SHARED_QUEUE_THRESHOLD_ENV]) ?? DEFAULT_SHARED_QUEUE_THRESHOLD;
}
function resolveFrontendActiveMs(env) {
    return parsePositiveNumber(env[exports.TP_FRONTEND_ACTIVE_MS_ENV]) ?? DEFAULT_FRONTEND_ACTIVE_MS;
}
function sharedQueueLengthForTrace(traceId, processors = []) {
    return processors
        .filter(processor => processor.traceId === traceId && (processor.leaseMode ?? 'shared') === 'shared')
        .reduce((sum, processor) => {
        const worker = processor.sqlWorker;
        if (!worker)
            return sum;
        return sum + worker.queuedP0 + worker.queuedP1 + worker.queuedP2;
    }, 0);
}
function hasRecentFrontendHolder(leases = [], now = Date.now(), activeWithinMs = DEFAULT_FRONTEND_ACTIVE_MS) {
    const cutoff = now - activeWithinMs;
    return leases.some(lease => lease.mode === 'shared' && lease.holders.some(holder => {
        return holder.holderType === 'frontend_http_rpc'
            && holder.heartbeatAt !== null
            && holder.heartbeatAt >= cutoff;
    }));
}
function buildTraceProcessorLeaseModeDecision(input) {
    const env = input.env ?? process.env;
    const frontendActiveWithinMs = resolveFrontendActiveMs(env);
    const sharedQueueLength = sharedQueueLengthForTrace(input.traceId, input.processors);
    const frontendActive = hasRecentFrontendHolder(input.leases, input.now, frontendActiveWithinMs);
    return decideTraceProcessorLeaseMode({
        ...input,
        sharedQueueLength,
        frontendActive,
        quotaAvailableForNewLeaseBytes: input.ramBudget?.availableForNewLeaseBytes,
        env,
    });
}
function decideTraceProcessorLeaseMode(input) {
    const env = input.env ?? process.env;
    const analysisMode = normalizeAnalysisMode(input.analysisMode);
    const estimatedSqlMs = parseNonNegativeNumber(input.estimatedSqlMs);
    const slowQueryThresholdMs = resolveSlowQueryThresholdMs(env);
    const sharedQueueThreshold = resolveSharedQueueThreshold(env);
    const frontendActiveWithinMs = resolveFrontendActiveMs(env);
    const sharedQueueLength = Math.max(0, Math.floor(input.sharedQueueLength ?? 0));
    const heavySkill = input.heavySkill === true;
    const fullAnalysis = analysisMode === 'full';
    const fastMode = analysisMode === 'fast';
    const estimatedSlowQuery = estimatedSqlMs !== undefined && estimatedSqlMs > slowQueryThresholdMs;
    const longTask = input.longTask === true ||
        input.holderType === 'report_generation' ||
        fullAnalysis ||
        heavySkill ||
        estimatedSlowQuery;
    const quotaAvailableForNewLeaseBytes = parseNonNegativeNumber(input.quotaAvailableForNewLeaseBytes);
    const estimatedNewLeaseRssBytes = parseNonNegativeNumber(input.estimatedNewLeaseRssBytes);
    const quotaLow = quotaAvailableForNewLeaseBytes !== undefined &&
        estimatedNewLeaseRssBytes !== undefined &&
        estimatedNewLeaseRssBytes > quotaAvailableForNewLeaseBytes;
    const signals = {
        holderType: input.holderType,
        ...(analysisMode ? { analysisMode } : {}),
        ...(input.requestedMode ? { requestedMode: input.requestedMode } : {}),
        ...(estimatedSqlMs !== undefined ? { estimatedSqlMs } : {}),
        heavySkill,
        longTask,
        sharedQueueLength,
        sharedQueueThreshold,
        frontendActive: input.frontendActive === true,
        frontendActiveWithinMs,
        slowQueryThresholdMs,
        ...(quotaAvailableForNewLeaseBytes !== undefined ? { quotaAvailableForNewLeaseBytes } : {}),
        ...(estimatedNewLeaseRssBytes !== undefined ? { estimatedNewLeaseRssBytes } : {}),
    };
    if (input.holderType === 'frontend_http_rpc') {
        return { mode: 'shared', reason: 'frontend_interactive', signals };
    }
    if (input.holderType === 'manual_register') {
        return { mode: 'shared', reason: 'manual_register', signals };
    }
    if (input.requestedMode === 'shared') {
        return { mode: 'shared', reason: 'requested_shared', signals };
    }
    if (quotaLow) {
        return { mode: 'shared', reason: 'quota_low_shared', signals };
    }
    if (input.requestedMode === 'isolated') {
        return { mode: 'isolated', reason: 'requested_isolated', signals };
    }
    if (input.holderType === 'report_generation') {
        return { mode: 'isolated', reason: 'report_generation', signals };
    }
    if (fullAnalysis) {
        return { mode: 'isolated', reason: 'full_analysis', signals };
    }
    if (heavySkill) {
        return { mode: 'isolated', reason: 'heavy_skill', signals };
    }
    if (estimatedSlowQuery) {
        return { mode: 'isolated', reason: 'estimated_slow_query', signals };
    }
    if (sharedQueueLength > sharedQueueThreshold) {
        return { mode: 'isolated', reason: 'shared_queue_backlog', signals };
    }
    if (input.frontendActive === true && longTask) {
        return { mode: 'isolated', reason: 'frontend_active_long_task', signals };
    }
    if (fastMode) {
        return { mode: 'shared', reason: 'fast_mode', signals };
    }
    return { mode: 'shared', reason: 'default_shared', signals };
}
//# sourceMappingURL=traceProcessorLeaseModeDecision.js.map