"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.transformPinInstruction = transformPinInstruction;
exports.transformTeachingContent = transformTeachingContent;
exports.validateActiveProcesses = validateActiveProcesses;
exports.validateConfidence = validateConfidence;
exports.parseCandidates = parseCandidates;
exports.parseFeatures = parseFeatures;
exports.isPinInstructionResponse = isPinInstructionResponse;
exports.isActiveProcess = isActiveProcess;
// =============================================================================
// Transformation Functions
// =============================================================================
/**
 * Transform raw pin instruction (snake_case) to API response (camelCase)
 *
 * @param raw - Raw pin instruction from YAML
 * @param activeProcesses - Active rendering processes from SQL
 * @returns Transformed pin instruction for frontend
 */
function transformPinInstruction(raw, activeProcesses) {
    const base = {
        pattern: raw.pattern,
        matchBy: raw.match_by,
        priority: raw.priority,
        reason: raw.reason,
    };
    // Pass through expand and main_thread_only flags
    if (raw.expand !== undefined) {
        base.expand = raw.expand;
    }
    if (raw.main_thread_only !== undefined) {
        base.mainThreadOnly = raw.main_thread_only;
    }
    // Apply smart filter logic if enabled
    if (raw.smart_filter?.enabled) {
        if (activeProcesses.length > 0) {
            base.smartPin = true;
            base.activeProcessNames = activeProcesses.map((p) => p.processName);
        }
        else {
            // If we can't identify active rendering processes, fall back to normal pinning.
            // This avoids missing critical tracks (e.g. app main thread) on sparse traces.
            base.reason = `${raw.reason} (未检测到活跃渲染进程，使用默认 Pin)`;
        }
    }
    return base;
}
/**
 * Transform raw teaching content (snake_case) to API response (camelCase)
 *
 * @param raw - Raw teaching content from YAML
 * @param docPath - Documentation file path
 * @returns Transformed teaching content for frontend
 */
function transformTeachingContent(raw, docPath) {
    return {
        title: raw.title,
        summary: raw.summary,
        mermaidBlocks: raw.mermaid ? [raw.mermaid] : [],
        threadRoles: raw.thread_roles.map((role) => ({
            thread: role.thread,
            responsibility: role.role + (role.description ? `: ${role.description}` : ''),
            traceTag: role.trace_tags,
        })),
        keySlices: raw.key_slices.map((slice) => slice.name),
        docPath,
    };
}
// =============================================================================
// Validation Functions
// =============================================================================
/**
 * Validate and extract active processes from SQL result
 *
 * Uses column name mapping instead of positional access to prevent
 * silent failures when column order changes.
 *
 * @param stepResult - Step result from skill execution
 * @returns Validated array of active processes
 */
function validateActiveProcesses(stepResult) {
    if (!stepResult?.data) {
        console.warn('[Teaching] Missing step data for active_rendering_processes');
        return [];
    }
    // New (SkillExecutor rawResults): array of objects
    if (Array.isArray(stepResult.data)) {
        const processes = [];
        for (const row of stepResult.data) {
            if (!row || typeof row !== 'object')
                continue;
            const processName = (typeof row.process_name === 'string' && row.process_name) ||
                (typeof row.processName === 'string' && row.processName) ||
                (typeof row.name === 'string' && row.name) ||
                '';
            if (!processName)
                continue;
            const upidRaw = row.upid ?? row.UPID;
            const frameCountRaw = row.frame_count ?? row.frameCount ?? row.count;
            const tidRaw = row.render_thread_tid ?? row.renderThreadTid ?? row.tid;
            processes.push({
                upid: typeof upidRaw === 'number' ? upidRaw : parseInt(String(upidRaw ?? ''), 10) || 0,
                processName,
                frameCount: typeof frameCountRaw === 'number' ? frameCountRaw : parseInt(String(frameCountRaw ?? ''), 10) || 0,
                renderThreadTid: typeof tidRaw === 'number' ? tidRaw : parseInt(String(tidRaw ?? ''), 10) || 0,
            });
        }
        return processes;
    }
    // Legacy table structure: { columns, rows }
    const legacy = stepResult.data;
    if (!legacy.rows || !Array.isArray(legacy.rows)) {
        console.warn('[Teaching] Invalid SQL result structure for active_rendering_processes');
        return [];
    }
    const { columns, rows } = legacy;
    // Check columns exist
    if (!columns || !Array.isArray(columns) || columns.length < 4) {
        console.warn('[Teaching] Insufficient columns in SQL result, expected 4, got:', columns?.length);
        return [];
    }
    // Build column index map for name-based access
    const colIndex = new Map();
    columns.forEach((col, i) => {
        if (typeof col === 'string') {
            colIndex.set(col.toLowerCase(), i);
        }
    });
    // Required columns (support both snake_case and as-is)
    const getColIdx = (names) => {
        for (const name of names) {
            const idx = colIndex.get(name.toLowerCase());
            if (idx !== undefined)
                return idx;
        }
        return -1;
    };
    const upidIdx = getColIdx(['upid']);
    const processNameIdx = getColIdx(['process_name', 'processname', 'name']);
    const frameCountIdx = getColIdx(['frame_count', 'framecount', 'count']);
    const tidIdx = getColIdx(['render_thread_tid', 'renderthreadtid', 'tid']);
    // Validate required columns found
    if (upidIdx === -1 || processNameIdx === -1) {
        console.warn('[Teaching] Missing required columns (upid, process_name). Available:', columns);
        return [];
    }
    // Transform rows with validation
    const processes = [];
    for (const row of rows) {
        if (!Array.isArray(row))
            continue;
        const processName = row[processNameIdx];
        if (!processName || typeof processName !== 'string')
            continue;
        processes.push({
            upid: typeof row[upidIdx] === 'number' ? row[upidIdx] : parseInt(String(row[upidIdx])) || 0,
            processName: processName,
            frameCount: frameCountIdx !== -1 && typeof row[frameCountIdx] === 'number'
                ? row[frameCountIdx]
                : parseInt(String(row[frameCountIdx])) || 0,
            renderThreadTid: tidIdx !== -1 && typeof row[tidIdx] === 'number'
                ? row[tidIdx]
                : parseInt(String(row[tidIdx])) || 0,
        });
    }
    return processes;
}
/**
 * Validate confidence value is in valid range [0, 1]
 *
 * @param value - Raw confidence value
 * @param defaultValue - Default if invalid
 * @returns Validated confidence between 0 and 1
 */
function validateConfidence(value, defaultValue = 0.5) {
    if (typeof value === 'number' && !isNaN(value)) {
        return Math.max(0, Math.min(1, value));
    }
    if (typeof value === 'string') {
        const parsed = parseFloat(value);
        if (!isNaN(parsed)) {
            return Math.max(0, Math.min(1, parsed));
        }
    }
    return defaultValue;
}
/**
 * Safely parse candidates from detection result
 *
 * @param candidates - Raw candidates string or array
 * @param limit - Maximum number of candidates to return
 * @returns Validated array of pipeline candidates
 */
function parseCandidates(candidates, limit = 10) {
    if (!candidates)
        return [];
    // Handle string format: "id1:0.9,id2:0.8"
    if (typeof candidates === 'string') {
        return candidates
            .split(/[;,]/)
            .slice(0, limit)
            .map((item) => {
            const [id, score] = item.split(':');
            return {
                id: id?.trim() || 'unknown',
                confidence: validateConfidence(score),
            };
        })
            .filter((c) => c.id !== 'unknown');
    }
    // Handle array format
    if (Array.isArray(candidates)) {
        return candidates.slice(0, limit).map((item) => {
            if (typeof item === 'object' && item !== null) {
                return {
                    id: String(item.id || 'unknown'),
                    confidence: validateConfidence(item.confidence),
                };
            }
            return { id: String(item), confidence: 0 };
        });
    }
    return [];
}
/**
 * Safely parse features from detection result
 *
 * @param features - Raw features string or array
 * @returns Validated array of detected features
 */
function parseFeatures(features) {
    if (!features)
        return [];
    // Handle string format: "feature1,feature2,feature3"
    if (typeof features === 'string') {
        return features
            .split(/[;,]/)
            .filter((f) => f.trim())
            .map((raw) => {
            const item = raw.trim();
            const [name, valueRaw] = item.split(':');
            const value = valueRaw !== undefined ? Number(valueRaw) : NaN;
            return {
                name: (name || item).trim(),
                detected: true,
                value: Number.isFinite(value) ? value : undefined,
            };
        });
    }
    // Handle array format
    if (Array.isArray(features)) {
        return features.map((item) => {
            if (typeof item === 'string') {
                return { name: item, detected: true };
            }
            if (typeof item === 'object' && item !== null) {
                const obj = item;
                return {
                    name: String(obj.name || obj.feature || 'unknown'),
                    detected: obj.detected !== false,
                    value: obj.value,
                };
            }
            return { name: String(item), detected: true };
        });
    }
    return [];
}
// =============================================================================
// Type Guards
// =============================================================================
/**
 * Type guard to check if value is a valid PinInstructionResponse
 */
function isPinInstructionResponse(value) {
    if (typeof value !== 'object' || value === null)
        return false;
    const obj = value;
    return (typeof obj.pattern === 'string' &&
        typeof obj.matchBy === 'string' &&
        typeof obj.priority === 'number' &&
        typeof obj.reason === 'string');
}
/**
 * Type guard to check if value is a valid ActiveProcess
 */
function isActiveProcess(value) {
    if (typeof value !== 'object' || value === null)
        return false;
    const obj = value;
    return (typeof obj.upid === 'number' &&
        typeof obj.processName === 'string' &&
        typeof obj.frameCount === 'number' &&
        typeof obj.renderThreadTid === 'number');
}
//# sourceMappingURL=teaching.types.js.map