"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.buildThreadSchedContext = buildThreadSchedContext;
/**
 * Thread State + Scheduler Context Aggregator (Spark Plan 11)
 *
 * Aggregates per-thread `thread_state` rows + `sched.with_context` wakeup
 * edges into a `ThreadSchedContextContract`. The contract is the
 * prerequisite prior used by jank, ANR, and startup decision trees.
 */
const sparkContracts_1 = require("../types/sparkContracts");
/** Group rows by utid and pivot duration by state. */
function groupBreakdowns(rows, range, runnableP95, wakeupCounts) {
    const byUtid = new Map();
    for (const row of rows) {
        let entry = byUtid.get(row.utid);
        if (!entry) {
            entry = { row, states: {} };
            byUtid.set(row.utid, entry);
        }
        entry.states[row.state] = (entry.states[row.state] ?? 0) + row.durNs;
    }
    const breakdowns = [];
    for (const [utid, entry] of byUtid) {
        breakdowns.push({
            utid,
            pid: entry.row.pid,
            threadName: entry.row.threadName,
            range,
            durByStateNs: entry.states,
            ...(wakeupCounts[utid] ? { wakeupCount: wakeupCounts[utid] } : {}),
            ...(runnableP95?.[utid] ? { runnableLatencyP95Ns: runnableP95[utid] } : {}),
        });
    }
    // Stable order: utid ascending.
    breakdowns.sort((a, b) => a.utid - b.utid);
    return breakdowns;
}
/**
 * Assemble the contract from raw rows. Caller is responsible for clamping
 * thread_state durations to the analysis window — this function trusts the
 * inputs and just pivots them.
 */
function buildThreadSchedContext(options) {
    const wakeupCounts = {};
    for (const edge of options.wakeupEdges ?? []) {
        wakeupCounts[edge.toUtid] = (wakeupCounts[edge.toUtid] ?? 0) + 1;
    }
    const threadStates = groupBreakdowns(options.threadStateRows, options.range, options.runnableLatencyP95Ns, wakeupCounts);
    const wakeupEdges = options.wakeupEdges?.map(e => ({
        fromUtid: e.fromUtid,
        toUtid: e.toUtid,
        ts: e.ts,
        ...(e.fromThread ? { fromThread: e.fromThread } : {}),
        ...(e.toThread ? { toThread: e.toThread } : {}),
        ...(e.latencyNs !== undefined ? { latencyNs: e.latencyNs } : {}),
        ...(e.reason ? { reason: e.reason } : {}),
    }));
    const criticalChain = options.criticalChain?.map(r => ({
        utid: r.utid,
        threadName: r.threadName,
        range: { startNs: r.startNs, endNs: r.endNs },
        reason: r.reason,
    }));
    const allEmpty = threadStates.length === 0;
    return {
        ...(0, sparkContracts_1.makeSparkProvenance)({
            source: 'thread-sched-context',
            ...(allEmpty ? { unsupportedReason: 'thread_state input is empty' } : {}),
        }),
        range: options.range,
        threadStates,
        ...(wakeupEdges && wakeupEdges.length > 0 ? { wakeupEdges } : {}),
        ...(criticalChain && criticalChain.length > 0 ? { criticalChain } : {}),
        coverage: [
            { sparkId: 6, planId: '11', status: 'implemented' },
            { sparkId: 17, planId: '11', status: criticalChain && criticalChain.length > 0 ? 'implemented' : 'scaffolded' },
        ],
    };
}
//# sourceMappingURL=threadSchedContext.js.map