"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.getSceneTemplates = getSceneTemplates;
exports.getSceneTemplateStoreDiagnostics = getSceneTemplateStoreDiagnostics;
exports.resetSceneTemplateStoreCacheForTests = resetSceneTemplateStoreCacheForTests;
const fs_1 = __importDefault(require("fs"));
const path_1 = __importDefault(require("path"));
const js_yaml_1 = __importDefault(require("js-yaml"));
const sceneTemplateValidator_1 = require("./sceneTemplateValidator");
const SCENE_TEMPLATE_CONFIG_ENV = 'SMARTPERFETTO_CONCLUSION_SCENE_TEMPLATE_PATH';
const SCENE_TEMPLATE_BASE_CONFIG_ENV = 'SMARTPERFETTO_CONCLUSION_SCENE_TEMPLATE_BASE_PATH';
const SCENE_TEMPLATE_OVERRIDE_CONFIG_ENV = 'SMARTPERFETTO_CONCLUSION_SCENE_TEMPLATE_OVERRIDE_PATH';
const BASE_TEMPLATE_FILE = 'conclusion_scene_templates.base.yaml';
const OVERRIDE_TEMPLATE_FILE = 'conclusion_scene_templates.yaml';
const FALLBACK_SCENE_TEMPLATES = [
    {
        id: 'jank',
        sceneName: '卡顿/掉帧',
        aspectHints: ['jank', 'scroll', 'frame', 'fps'],
        keywords: ['卡顿', '掉帧', '滑动', 'frame', 'jank'],
        focusLines: ['- 聚焦帧预算违约链路：主线程 -> RenderThread -> SF/HWC。'],
        outputRequirementTemplates: [
            '- 若结论包含主线程长耗时/长 slice，必须继续回答“{{deep_reason_label}}”。',
            '- 每个“{{deep_reason_label}}”必须绑定至少 1 个数值证据。',
        ],
        nextStepLine: '- “下一步”优先给出口径对齐、聚类下钻、同窗验证动作。',
        requireTopClusters: true,
        clusterOutputMode: 'required',
        clusterFrameListMode: 'full',
        maxFramesPerCluster: 120,
        injectClusterFrameAggregation: true,
        injectWorkloadDominantMarker: true,
    },
    {
        id: 'startup',
        sceneName: '启动性能',
        aspectHints: ['startup', 'launch'],
        keywords: ['启动', 'launch', 'startup', 'ttid', 'ttfd'],
        focusLines: ['- 聚焦启动链路：进程创建 -> 初始化 -> 首帧可见 -> 可交互。'],
        outputRequirementTemplates: ['- 启动场景至少给出 1 个阶段耗时或启动指标（TTID/TTFD）。'],
        nextStepLine: '- “下一步”优先对最慢启动阶段下钻。',
        requireTopClusters: false,
        clusterOutputMode: 'optional',
        clusterFrameListMode: 'none',
        injectClusterFrameAggregation: false,
        injectWorkloadDominantMarker: false,
    },
    {
        id: 'generic',
        sceneName: '通用性能',
        aspectHints: [],
        keywords: [],
        focusLines: ['- 先用人话说明“现象 -> 直接证据 -> 影响”，再给根因与优化方向。'],
        outputRequirementTemplates: ['- 结论必须绑定明确证据，避免抽象术语堆砌。'],
        nextStepLine: '- “下一步”给出 1-2 个最高信息增益动作，并与当前证据直接对应。',
        requireTopClusters: false,
        clusterOutputMode: 'optional',
        clusterFrameListMode: 'none',
        injectClusterFrameAggregation: false,
        injectWorkloadDominantMarker: false,
    },
];
let templateCache = null;
let diagnosticsCache = {
    sourceFiles: [],
    warnings: [],
    templateCount: 0,
};
function resolveAbsolutePath(rawPath) {
    return path_1.default.isAbsolute(rawPath) ? rawPath : path_1.default.resolve(process.cwd(), rawPath);
}
function resolveDefaultConfigPath(fileName) {
    const candidates = [
        path_1.default.resolve(__dirname, '../../../skills/config', fileName),
        path_1.default.resolve(__dirname, '../../../../skills/config', fileName),
        path_1.default.resolve(process.cwd(), 'skills/config', fileName),
    ];
    for (const candidate of candidates) {
        if (fs_1.default.existsSync(candidate))
            return candidate;
    }
    return null;
}
function resolveConfiguredPath(envKey, defaultFileName) {
    const envValue = String(process.env[envKey] || '').trim();
    if (envValue)
        return resolveAbsolutePath(envValue);
    return resolveDefaultConfigPath(defaultFileName);
}
function loadSceneTemplatePatchesFromFile(configPath, layer, warnings, sourceFiles) {
    if (!configPath || !fs_1.default.existsSync(configPath))
        return [];
    sourceFiles.push(configPath);
    try {
        const raw = fs_1.default.readFileSync(configPath, 'utf-8');
        const parsed = js_yaml_1.default.load(raw);
        return (0, sceneTemplateValidator_1.parseSceneTemplatePatchesFromConfig)(parsed, {
            filePath: configPath,
            layer,
        }, warnings);
    }
    catch (error) {
        const message = error instanceof Error ? error.message : String(error);
        warnings.push(`[${layer}] ${configPath}: failed to parse YAML (${message})`);
        return [];
    }
}
function fallbackTemplateForPatch(id) {
    const generic = FALLBACK_SCENE_TEMPLATES.find(item => item.id === 'generic');
    return {
        ...generic,
        id,
        sceneName: id,
    };
}
function applySceneTemplatePatch(templateMap, patch) {
    const base = templateMap.get(patch.id) || fallbackTemplateForPatch(patch.id);
    const merged = {
        ...base,
        ...(patch.sceneName !== undefined ? { sceneName: patch.sceneName } : {}),
        ...(patch.aspectHints !== undefined ? { aspectHints: patch.aspectHints } : {}),
        ...(patch.keywords !== undefined ? { keywords: patch.keywords } : {}),
        ...(patch.focusLines !== undefined ? { focusLines: patch.focusLines } : {}),
        ...(patch.outputRequirementTemplates !== undefined ? { outputRequirementTemplates: patch.outputRequirementTemplates } : {}),
        ...(patch.nextStepLine !== undefined ? { nextStepLine: patch.nextStepLine } : {}),
        ...(patch.requireTopClusters !== undefined ? { requireTopClusters: patch.requireTopClusters } : {}),
        ...(patch.clusterOutputMode !== undefined ? { clusterOutputMode: patch.clusterOutputMode } : {}),
        ...(patch.clusterFrameListMode !== undefined ? { clusterFrameListMode: patch.clusterFrameListMode } : {}),
        ...(patch.maxFramesPerCluster !== undefined ? { maxFramesPerCluster: patch.maxFramesPerCluster } : {}),
        ...(patch.injectClusterFrameAggregation !== undefined
            ? { injectClusterFrameAggregation: patch.injectClusterFrameAggregation }
            : {}),
        ...(patch.injectWorkloadDominantMarker !== undefined
            ? { injectWorkloadDominantMarker: patch.injectWorkloadDominantMarker }
            : {}),
    };
    if (!merged.sceneName)
        merged.sceneName = base.sceneName || patch.id;
    if (!Array.isArray(merged.outputRequirementTemplates) || merged.outputRequirementTemplates.length === 0) {
        merged.outputRequirementTemplates = base.outputRequirementTemplates;
    }
    if (!merged.nextStepLine)
        merged.nextStepLine = base.nextStepLine;
    if (!merged.clusterOutputMode)
        merged.clusterOutputMode = base.clusterOutputMode;
    if (!merged.clusterFrameListMode)
        merged.clusterFrameListMode = base.clusterFrameListMode;
    if (merged.maxFramesPerCluster === undefined)
        merged.maxFramesPerCluster = base.maxFramesPerCluster;
    if (merged.injectClusterFrameAggregation === undefined) {
        merged.injectClusterFrameAggregation = base.injectClusterFrameAggregation;
    }
    if (merged.injectWorkloadDominantMarker === undefined) {
        merged.injectWorkloadDominantMarker = base.injectWorkloadDominantMarker;
    }
    templateMap.set(patch.id, merged);
}
function buildDefaultTemplateMap() {
    const map = new Map();
    for (const template of FALLBACK_SCENE_TEMPLATES) {
        map.set(template.id, { ...template });
    }
    return map;
}
function dedupeStringArray(values) {
    return Array.from(new Set(values));
}
function flushWarnings(warnings) {
    for (const warning of warnings) {
        console.warn(`[SceneTemplateStore] ${warning}`);
    }
}
function updateDiagnostics(sourceFiles, warnings, templateCount) {
    diagnosticsCache = {
        sourceFiles: dedupeStringArray(sourceFiles),
        warnings: [...warnings],
        templateCount,
    };
}
function loadTemplates() {
    const templateMap = buildDefaultTemplateMap();
    const sourceFiles = [];
    const warnings = [];
    const singleConfigPath = String(process.env[SCENE_TEMPLATE_CONFIG_ENV] || '').trim();
    if (singleConfigPath) {
        const patches = loadSceneTemplatePatchesFromFile(resolveAbsolutePath(singleConfigPath), 'single', warnings, sourceFiles);
        for (const patch of patches)
            applySceneTemplatePatch(templateMap, patch);
        const templates = Array.from(templateMap.values());
        updateDiagnostics(sourceFiles, warnings, templates.length);
        flushWarnings(warnings);
        return templates;
    }
    const baseConfigPath = resolveConfiguredPath(SCENE_TEMPLATE_BASE_CONFIG_ENV, BASE_TEMPLATE_FILE);
    const overrideConfigPath = resolveConfiguredPath(SCENE_TEMPLATE_OVERRIDE_CONFIG_ENV, OVERRIDE_TEMPLATE_FILE);
    if (baseConfigPath) {
        const basePatches = loadSceneTemplatePatchesFromFile(baseConfigPath, 'base', warnings, sourceFiles);
        for (const patch of basePatches)
            applySceneTemplatePatch(templateMap, patch);
    }
    if (overrideConfigPath) {
        const overridePatches = loadSceneTemplatePatchesFromFile(overrideConfigPath, 'override', warnings, sourceFiles);
        for (const patch of overridePatches)
            applySceneTemplatePatch(templateMap, patch);
    }
    if (!templateMap.has('generic')) {
        templateMap.set('generic', fallbackTemplateForPatch('generic'));
    }
    const templates = Array.from(templateMap.values());
    updateDiagnostics(sourceFiles, warnings, templates.length);
    flushWarnings(warnings);
    return templates;
}
function getSceneTemplates() {
    if (templateCache)
        return templateCache;
    templateCache = loadTemplates();
    return templateCache;
}
function getSceneTemplateStoreDiagnostics() {
    if (!templateCache) {
        templateCache = loadTemplates();
    }
    return {
        sourceFiles: [...diagnosticsCache.sourceFiles],
        warnings: [...diagnosticsCache.warnings],
        templateCount: diagnosticsCache.templateCount,
    };
}
function resetSceneTemplateStoreCacheForTests() {
    templateCache = null;
    diagnosticsCache = {
        sourceFiles: [],
        warnings: [],
        templateCount: 0,
    };
}
//# sourceMappingURL=sceneTemplateStore.js.map