"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.
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.traceProcessorService = exports.perfettoSqlSkill = void 0;
/**
 * Perfetto SQL Routes
 *
 * API endpoints for Perfetto SQL-based trace analysis.
 * All SQL patterns are based on official Perfetto documentation.
 *
 * Architecture:
 * - Uses SkillExecutor (YAML-based) for all supported analysis types
 * - Legacy PerfettoSqlSkill only used for endpoints without YAML skills
 */
const express_1 = __importDefault(require("express"));
const perfettoSqlSkill_1 = require("../services/perfettoSqlSkill");
const skillAnalysisAdapter_1 = require("../services/skillEngine/skillAnalysisAdapter");
const traceProcessorSqlWorker_1 = require("../services/traceProcessorSqlWorker");
const sqlIncludeInjector_1 = require("../agentv3/sqlIncludeInjector");
const router = express_1.default.Router();
// Use the shared TraceProcessorService singleton
const traceProcessorService_1 = require("../services/traceProcessorService");
// Get the shared TraceProcessorService instance
const traceProcessorService = (0, traceProcessorService_1.getTraceProcessorService)();
exports.traceProcessorService = traceProcessorService;
// Legacy skill for endpoints without YAML definitions (deprecated, will be removed)
const perfettoSqlSkill = new perfettoSqlSkill_1.PerfettoSqlSkill(traceProcessorService);
exports.perfettoSqlSkill = perfettoSqlSkill;
// Skill Engine adapter (uses YAML-based skills from skills/)
let skillAdapter = null;
const getSkillAdapter = () => {
    if (!skillAdapter) {
        skillAdapter = (0, skillAnalysisAdapter_1.createSkillAnalysisAdapter)(traceProcessorService);
    }
    return skillAdapter;
};
// Mapping from route skill names to YAML skill IDs
const SKILL_ID_MAP = {
    startup: 'startup_analysis',
    scrolling: 'scrolling_analysis',
    memory: 'memory_analysis',
    cpu: 'cpu_analysis',
    binder: 'binder_analysis',
    surfaceflinger: 'surfaceflinger_analysis',
    navigation: 'navigation_analysis',
    'click-response': 'click_response_analysis',
};
/**
 * Execute analysis using the Skill Engine
 * Returns null only if skill not found (no YAML definition)
 * Throws error if skill exists but execution fails
 */
async function executeWithSkillEngine(skillId, traceId, packageName) {
    const adapter = getSkillAdapter();
    await adapter.ensureInitialized();
    const skill = await adapter.getSkillDetail(skillId);
    if (!skill) {
        // Skill not found - caller should handle (legacy endpoint or error)
        return null;
    }
    console.log(`[PerfettoSql] Using Skill Engine for: ${skillId}`);
    const startTime = Date.now();
    // Execute skill - let errors propagate (no silent fallback)
    const result = await adapter.analyze({
        traceId,
        skillId,
        packageName,
    });
    console.log(`[PerfettoSql] Skill ${skillId} completed in ${Date.now() - startTime}ms`);
    // Convert to response format with v2 enhanced data
    return {
        analysisType: skillId,
        skillEngine: true,
        skillEngineVersion: 'v2',
        skillName: result.skillName,
        vendor: result.vendor,
        sections: result.sections,
        diagnostics: result.diagnostics,
        summary: result.summary,
        executionTimeMs: result.executionTimeMs,
        success: result.success,
        displayResults: result.displayResults,
        aiSummary: result.aiSummary,
        directAnswer: result.directAnswer,
        questionType: result.questionType,
        answerConfidence: result.answerConfidence,
    };
}
// ============================================================================
// Routes
// ============================================================================
/**
 * POST /api/perfetto-sql/analyze
 *
 * Main analysis endpoint - auto-detects skill type from question
 *
 * Body: { traceId, question, packageName?, timeRange? }
 * Response: PerfettoSqlResponse
 */
router.post('/analyze', async (req, res) => {
    try {
        const { traceId, question, packageName, timeRange } = req.body;
        if (!traceId) {
            return res.status(400).json({
                success: false,
                error: 'traceId is required',
            });
        }
        if (!question) {
            return res.status(400).json({
                success: false,
                error: 'question is required',
            });
        }
        // Verify trace exists
        const trace = traceProcessorService.getTrace(traceId);
        if (!trace) {
            return res.status(404).json({
                success: false,
                error: `Trace ${traceId} not found`,
            });
        }
        const result = await perfettoSqlSkill.analyze({
            traceId,
            question,
            packageName,
            timeRange,
        });
        res.json({
            success: true,
            ...result,
        });
    }
    catch (error) {
        console.error('[PerfettoSql] Analyze error:', error);
        res.status(500).json({
            success: false,
            error: error.message || 'Analysis failed',
        });
    }
});
/**
 * POST /api/perfetto-sql/startup
 *
 * Analyze app startup performance (cold/warm/hot)
 * Uses YAML Skill Engine with fallback to legacy
 *
 * Body: { traceId, packageName? }
 * Response: Startup analysis results
 */
router.post('/startup', async (req, res) => {
    try {
        const { traceId, packageName } = req.body;
        if (!traceId) {
            return res.status(400).json({
                success: false,
                error: 'traceId is required',
            });
        }
        // Try Skill Engine first
        const skillResult = await executeWithSkillEngine(SKILL_ID_MAP.startup, traceId, packageName);
        if (skillResult) {
            return res.json({
                success: true,
                ...skillResult,
            });
        }
        // Fallback to legacy
        const result = await perfettoSqlSkill.analyzeStartup(traceId, packageName);
        res.json({
            success: true,
            ...result,
        });
    }
    catch (error) {
        console.error('[PerfettoSql] Startup analysis error:', error);
        res.status(500).json({
            success: false,
            error: error.message || 'Startup analysis failed',
        });
    }
});
/**
 * POST /api/perfetto-sql/scrolling
 *
 * Analyze scrolling performance and jank
 * Uses YAML Skill Engine with fallback to legacy
 *
 * Body: { traceId, packageName? }
 * Response: Scrolling analysis results
 */
router.post('/scrolling', async (req, res) => {
    try {
        const { traceId, packageName } = req.body;
        if (!traceId) {
            return res.status(400).json({
                success: false,
                error: 'traceId is required',
            });
        }
        // Try Skill Engine first
        const skillResult = await executeWithSkillEngine(SKILL_ID_MAP.scrolling, traceId, packageName);
        if (skillResult) {
            return res.json({
                success: true,
                ...skillResult,
            });
        }
        // Fallback to legacy
        const result = await perfettoSqlSkill.analyzeScrolling(traceId, packageName);
        res.json({
            success: true,
            ...result,
        });
    }
    catch (error) {
        console.error('[PerfettoSql] Scrolling analysis error:', error);
        res.status(500).json({
            success: false,
            error: error.message || 'Scrolling analysis failed',
        });
    }
});
/**
 * POST /api/perfetto-sql/memory
 *
 * Analyze memory usage (GC, allocations, OOM)
 * Uses YAML Skill Engine with fallback to legacy
 *
 * Body: { traceId, packageName? }
 * Response: Memory analysis results
 */
router.post('/memory', async (req, res) => {
    try {
        const { traceId, packageName } = req.body;
        if (!traceId) {
            return res.status(400).json({
                success: false,
                error: 'traceId is required',
            });
        }
        // Try Skill Engine first
        const skillResult = await executeWithSkillEngine(SKILL_ID_MAP.memory, traceId, packageName);
        if (skillResult) {
            return res.json({
                success: true,
                ...skillResult,
            });
        }
        // Fallback to legacy
        const result = await perfettoSqlSkill.analyzeMemory(traceId, packageName);
        res.json({
            success: true,
            ...result,
        });
    }
    catch (error) {
        console.error('[PerfettoSql] Memory analysis error:', error);
        res.status(500).json({
            success: false,
            error: error.message || 'Memory analysis failed',
        });
    }
});
/**
 * POST /api/perfetto-sql/cpu
 *
 * Analyze CPU utilization
 * Uses YAML Skill Engine with fallback to legacy
 *
 * Body: { traceId, packageName? }
 * Response: CPU analysis results
 */
router.post('/cpu', async (req, res) => {
    try {
        const { traceId, packageName } = req.body;
        if (!traceId) {
            return res.status(400).json({
                success: false,
                error: 'traceId is required',
            });
        }
        // Try Skill Engine first
        const skillResult = await executeWithSkillEngine(SKILL_ID_MAP.cpu, traceId, packageName);
        if (skillResult) {
            return res.json({
                success: true,
                ...skillResult,
            });
        }
        // Fallback to legacy
        const result = await perfettoSqlSkill.analyzeCpu(traceId, packageName);
        res.json({
            success: true,
            ...result,
        });
    }
    catch (error) {
        console.error('[PerfettoSql] CPU analysis error:', error);
        res.status(500).json({
            success: false,
            error: error.message || 'CPU analysis failed',
        });
    }
});
/**
 * POST /api/perfetto-sql/surfaceflinger
 *
 * Analyze SurfaceFlinger performance
 * Uses YAML Skill Engine with fallback to legacy
 *
 * Body: { traceId }
 * Response: SurfaceFlinger analysis results
 */
router.post('/surfaceflinger', async (req, res) => {
    try {
        const { traceId, packageName } = req.body;
        if (!traceId) {
            return res.status(400).json({
                success: false,
                error: 'traceId is required',
            });
        }
        // Try Skill Engine first
        const skillResult = await executeWithSkillEngine(SKILL_ID_MAP.surfaceflinger, traceId, packageName);
        if (skillResult) {
            return res.json({
                success: true,
                ...skillResult,
            });
        }
        // Fallback to legacy
        const result = await perfettoSqlSkill.analyzeSurfaceFlinger(traceId);
        res.json({
            success: true,
            ...result,
        });
    }
    catch (error) {
        console.error('[PerfettoSql] SurfaceFlinger analysis error:', error);
        res.status(500).json({
            success: false,
            error: error.message || 'SurfaceFlinger analysis failed',
        });
    }
});
/**
 * POST /api/perfetto-sql/binder
 *
 * Analyze Binder transaction performance
 * Uses YAML Skill Engine with fallback to legacy
 *
 * Body: { traceId, packageName? }
 * Response: Binder analysis results
 */
router.post('/binder', async (req, res) => {
    try {
        const { traceId, packageName } = req.body;
        if (!traceId) {
            return res.status(400).json({
                success: false,
                error: 'traceId is required',
            });
        }
        // Try Skill Engine first
        const skillResult = await executeWithSkillEngine(SKILL_ID_MAP.binder, traceId, packageName);
        if (skillResult) {
            return res.json({
                success: true,
                ...skillResult,
            });
        }
        // Fallback to legacy
        const result = await perfettoSqlSkill.analyzeBinder(traceId, packageName);
        res.json({
            success: true,
            ...result,
        });
    }
    catch (error) {
        console.error('[PerfettoSql] Binder analysis error:', error);
        res.status(500).json({
            success: false,
            error: error.message || 'Binder analysis failed',
        });
    }
});
/**
 * GET /api/perfetto-sql/tables
 *
 * Get list of available Perfetto SQL tables
 *
 * Response: { tables: TableSchema[] }
 */
router.get('/tables', (_req, res) => {
    try {
        const knowledgeBase = perfettoSqlSkill.getKnowledgeBase();
        const tableNames = knowledgeBase.getTableNames();
        const tables = tableNames.map((name) => knowledgeBase.getTableSchema(name));
        res.json({
            success: true,
            tables,
        });
    }
    catch (error) {
        console.error('[PerfettoSql] Tables error:', error);
        res.status(500).json({
            success: false,
            error: error.message || 'Failed to get tables',
        });
    }
});
/**
 * GET /api/perfetto-sql/tables/:tableName
 *
 * Get schema for a specific table
 *
 * Response: { table: TableSchema }
 */
router.get('/tables/:tableName', (req, res) => {
    try {
        const { tableName } = req.params;
        const knowledgeBase = perfettoSqlSkill.getKnowledgeBase();
        const table = knowledgeBase.getTableSchema(tableName);
        if (!table) {
            return res.status(404).json({
                success: false,
                error: `Table ${tableName} not found`,
            });
        }
        res.json({
            success: true,
            table,
        });
    }
    catch (error) {
        console.error('[PerfettoSql] Table schema error:', error);
        res.status(500).json({
            success: false,
            error: error.message || 'Failed to get table schema',
        });
    }
});
/**
 * GET /api/perfetto-sql/functions
 *
 * Get list of available Perfetto SQL functions
 *
 * Query params:
 * - category: Filter by function category
 *
 * Response: { functions: FunctionSignature[] }
 */
router.get('/functions', (req, res) => {
    try {
        const { category } = req.query;
        const knowledgeBase = perfettoSqlSkill.getKnowledgeBase();
        let functions;
        if (category && typeof category === 'string') {
            functions = knowledgeBase.getFunctionsByCategory(category);
        }
        else {
            functions = knowledgeBase.getFunctions();
        }
        res.json({
            success: true,
            functions,
        });
    }
    catch (error) {
        console.error('[PerfettoSql] Functions error:', error);
        res.status(500).json({
            success: false,
            error: error.message || 'Failed to get functions',
        });
    }
});
/**
 * GET /api/perfetto-sql/skills
 *
 * Get list of available analysis skills
 *
 * Response: { skills: PerfettoSkillType[] }
 */
router.get('/skills', (_req, res) => {
    try {
        const skills = perfettoSqlSkill.getAvailableSkills();
        res.json({
            success: true,
            skills,
        });
    }
    catch (error) {
        console.error('[PerfettoSql] Skills error:', error);
        res.status(500).json({
            success: false,
            error: error.message || 'Failed to get skills',
        });
    }
});
/**
 * POST /api/perfetto-sql/sql
 *
 * Execute raw SQL query on a trace
 *
 * Body: { traceId, sql }
 * Response: { rows, rowCount, columns }
 */
router.post('/sql', async (req, res) => {
    try {
        const { traceId, sql, priority: priorityInput } = req.body;
        if (!traceId) {
            return res.status(400).json({
                success: false,
                error: 'traceId is required',
            });
        }
        if (!sql) {
            return res.status(400).json({
                success: false,
                error: 'sql is required',
            });
        }
        // Verify trace exists
        const trace = traceProcessorService.getTrace(traceId);
        if (!trace) {
            return res.status(404).json({
                success: false,
                error: `Trace ${traceId} not found`,
            });
        }
        const priority = (0, traceProcessorSqlWorker_1.normalizeTraceProcessorQueryPriority)(req.get('x-smartperfetto-query-priority') || priorityInput, 'p0');
        const { sql: finalSql, injected } = (0, sqlIncludeInjector_1.injectStdlibIncludes)(sql);
        const result = await traceProcessorService.query(traceId, finalSql, { priority });
        if (result.error) {
            return res.status(400).json({
                success: false,
                error: result.error,
            });
        }
        res.json({
            success: true,
            rows: result.rows,
            rowCount: result.rows.length,
            columns: result.columns,
            stdlibInjectedModules: injected,
        });
    }
    catch (error) {
        console.error('[PerfettoSql] SQL execution error:', error);
        res.status(500).json({
            success: false,
            error: error.message || 'SQL execution failed',
        });
    }
});
/**
 * POST /api/perfetto-sql/navigation
 *
 * Analyze navigation/activity switching performance
 * Uses YAML Skill Engine with fallback to legacy
 *
 * Body: { traceId, packageName? }
 * Response: Navigation analysis results
 */
router.post('/navigation', async (req, res) => {
    try {
        const { traceId, packageName } = req.body;
        if (!traceId) {
            return res.status(400).json({
                success: false,
                error: 'traceId is required',
            });
        }
        // Try Skill Engine first
        const skillResult = await executeWithSkillEngine(SKILL_ID_MAP.navigation, traceId, packageName);
        if (skillResult) {
            return res.json({
                success: true,
                ...skillResult,
            });
        }
        // Fallback to legacy
        const result = await perfettoSqlSkill.analyzeNavigation(traceId, packageName);
        res.json({
            success: true,
            ...result,
        });
    }
    catch (error) {
        console.error('[PerfettoSql] Navigation analysis error:', error);
        res.status(500).json({
            success: false,
            error: error.message || 'Navigation analysis failed',
        });
    }
});
/**
 * POST /api/perfetto-sql/click-response
 *
 * Analyze click/tap response performance
 * Uses YAML Skill Engine with fallback to legacy
 *
 * Body: { traceId, packageName? }
 * Response: Click response analysis results
 */
router.post('/click-response', async (req, res) => {
    try {
        const { traceId, packageName } = req.body;
        if (!traceId) {
            return res.status(400).json({
                success: false,
                error: 'traceId is required',
            });
        }
        // Try Skill Engine first
        const skillResult = await executeWithSkillEngine(SKILL_ID_MAP['click-response'], traceId, packageName);
        if (skillResult) {
            return res.json({
                success: true,
                ...skillResult,
            });
        }
        // Fallback to legacy
        const result = await perfettoSqlSkill.analyzeClickResponse(traceId, packageName);
        res.json({
            success: true,
            ...result,
        });
    }
    catch (error) {
        console.error('[PerfettoSql] Click response analysis error:', error);
        res.status(500).json({
            success: false,
            error: error.message || 'Click response analysis failed',
        });
    }
});
/**
 * POST /api/perfetto-sql/input
 *
 * Analyze input events and latency
 *
 * Body: { traceId, packageName? }
 * Response: Input analysis results
 */
router.post('/input', async (req, res) => {
    try {
        const { traceId, packageName } = req.body;
        if (!traceId) {
            return res.status(400).json({
                success: false,
                error: 'traceId is required',
            });
        }
        const result = await perfettoSqlSkill.analyzeInput(traceId, packageName);
        res.json({
            success: true,
            ...result,
        });
    }
    catch (error) {
        console.error('[PerfettoSql] Input analysis error:', error);
        res.status(500).json({
            success: false,
            error: error.message || 'Input analysis failed',
        });
    }
});
/**
 * POST /api/perfetto-sql/buffer-flow
 *
 * Analyze buffer flow and queue
 *
 * Body: { traceId }
 * Response: Buffer flow analysis results
 */
router.post('/buffer-flow', async (req, res) => {
    try {
        const { traceId } = req.body;
        if (!traceId) {
            return res.status(400).json({
                success: false,
                error: 'traceId is required',
            });
        }
        const result = await perfettoSqlSkill.analyzeBufferFlow(traceId);
        res.json({
            success: true,
            ...result,
        });
    }
    catch (error) {
        console.error('[PerfettoSql] Buffer flow analysis error:', error);
        res.status(500).json({
            success: false,
            error: error.message || 'Buffer flow analysis failed',
        });
    }
});
/**
 * POST /api/perfetto-sql/systemserver
 *
 * Analyze SystemServer performance
 *
 * Body: { traceId }
 * Response: SystemServer analysis results
 */
router.post('/systemserver', async (req, res) => {
    try {
        const { traceId } = req.body;
        if (!traceId) {
            return res.status(400).json({
                success: false,
                error: 'traceId is required',
            });
        }
        const result = await perfettoSqlSkill.analyzeSystemServer(traceId);
        res.json({
            success: true,
            ...result,
        });
    }
    catch (error) {
        console.error('[PerfettoSql] SystemServer analysis error:', error);
        res.status(500).json({
            success: false,
            error: error.message || 'SystemServer analysis failed',
        });
    }
});
exports.default = router;
//# sourceMappingURL=perfettoSqlRoutes.js.map