"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.BaseExpert = void 0;
/**
 * Base Expert Class
 *
 * Abstract base class for domain expert agents.
 * Experts encapsulate domain-specific knowledge and use decision trees
 * for intelligent, hypothesis-driven analysis.
 *
 * Key capabilities:
 * - Decision tree execution with conditional branching
 * - Skill invocation for data gathering
 * - Session forking for deep exploration
 * - Architecture-aware analysis strategy selection
 */
const events_1 = require("events");
const decision_1 = require("../../decision");
const types_1 = require("../../types");
const state_1 = require("../../state");
const fork_1 = require("../../fork");
/**
 * Abstract base class for domain experts
 */
class BaseExpert extends events_1.EventEmitter {
    constructor(config) {
        super();
        this.state = null;
        this.config = config;
        // Initialize skill adapter and tree executor
        this.skillAdapter = new decision_1.SkillExecutorAdapter({ enableCache: true });
        this.treeExecutor = new decision_1.DecisionTreeExecutor(this.skillAdapter, {
            maxNodes: 50,
            nodeTimeoutMs: 30000,
            verbose: true,
        });
        // Forward tree executor events
        this.treeExecutor.on('node:start', (data) => this.emit('node:start', data));
        this.treeExecutor.on('node:complete', (data) => this.emit('node:complete', data));
    }
    /**
     * Main analysis entry point
     */
    async analyze(input) {
        const startTime = Date.now();
        // Initialize state
        this.state = {
            phase: 'initializing',
            executionPath: [],
            collectedData: new Map(),
            startTime,
            lastUpdateTime: startTime,
        };
        this.emit('analysis:start', { expertId: this.config.id, input });
        try {
            // Phase 1: Architecture detection (if not already done)
            this.updateState('detecting');
            const architecture = input.architecture || await this.detectArchitecture(input);
            // Phase 2: Select analysis strategy based on architecture and intent
            const strategy = this.selectStrategy(input.intent, architecture);
            this.log(`Selected strategy: ${strategy.name} for ${architecture?.type || 'STANDARD'}`);
            // Phase 3: Execute decision tree or custom analysis
            this.updateState('analyzing');
            let treeResult = null;
            if (strategy.decisionTree) {
                treeResult = await this.executeDecisionTree(strategy.decisionTree, input, architecture);
            }
            else {
                // Fallback to custom analysis
                treeResult = await this.performCustomAnalysis(input, architecture);
            }
            // Phase 4: Build conclusion
            this.updateState('concluding');
            const output = this.buildOutput(treeResult, input, startTime);
            // Phase 5: Complete
            this.updateState('completed');
            this.emit('analysis:complete', { expertId: this.config.id, output });
            return output;
        }
        catch (error) {
            this.updateState('failed');
            this.emit('analysis:error', { expertId: this.config.id, error });
            return {
                expertId: this.config.id,
                domain: this.config.domain,
                success: false,
                findings: [],
                suggestions: ['请检查 trace 数据是否完整', '尝试重新分析'],
                error: error.message,
                confidence: 0,
            };
        }
    }
    /**
     * Check if this expert can handle the given intent
     */
    canHandle(intent) {
        return this.config.handlesIntents.includes(intent.category);
    }
    /**
     * Get decision tree by analysis type
     */
    getDecisionTree(analysisType) {
        // First check if we have this tree in our config
        const treeId = analysisType || this.config.decisionTrees[0];
        if (!treeId)
            return undefined;
        // Get from registry
        return (0, decision_1.getDecisionTree)(treeId);
    }
    /**
     * Get current state
     */
    getState() {
        return this.state;
    }
    // ===========================================================================
    // Protected methods for subclasses to override
    // ===========================================================================
    /**
     * Detect architecture (can be overridden by subclasses)
     */
    async detectArchitecture(input) {
        // Use the architecture from input if available
        return input.architecture;
    }
    /**
     * Transform conclusion from decision tree to expert conclusion
     * Subclasses can override for domain-specific transformations
     */
    transformConclusion(treeConclusion, collectedData) {
        return {
            category: treeConclusion?.category || 'UNKNOWN',
            component: treeConclusion?.component || 'UNKNOWN',
            summary: treeConclusion?.summaryTemplate || '分析完成但未能确定具体原因',
            evidence: [],
            optimizationSuggestions: treeConclusion?.suggestedNextSteps || [],
            confidence: treeConclusion?.confidence || 0.5,
        };
    }
    /**
     * Extract findings from collected data
     * Subclasses can override for domain-specific extraction
     */
    extractFindings(collectedData) {
        const findings = [];
        // Default implementation - subclasses should override
        for (const [key, value] of collectedData) {
            if (value?.findings) {
                findings.push(...value.findings);
            }
        }
        return findings;
    }
    // ===========================================================================
    // Protected helper methods
    // ===========================================================================
    /**
     * Execute a skill
     */
    async executeSkill(skillId, params, context) {
        return this.skillAdapter.execute(skillId, params, context);
    }
    /**
     * Fork a session for deeper analysis
     */
    async forkSession(request, input) {
        // Check if forking is enabled
        if (!this.config.canForkSession) {
            return {
                forkSessionId: '',
                success: false,
                findings: [],
                error: 'Session forking is not enabled for this expert',
            };
        }
        this.log(`Forking session for: ${request.reason}`);
        this.emit('fork:start', { request, input });
        try {
            let forkManager = (0, fork_1.getForkManager)();
            const checkpointManager = new state_1.CheckpointManager();
            if (!forkManager) {
                forkManager = (0, fork_1.createForkManager)(checkpointManager);
                (0, fork_1.setForkManager)(forkManager);
            }
            forkManager.initializeSession(input.sessionId, 'main');
            forkManager.registerContext(input.sessionId, {
                sessionId: input.sessionId,
                traceId: input.traceId,
                query: input.query,
                intent: input.intent,
                previousResults: [],
                traceProcessorService: input.traceProcessorService,
                package: input.packageName,
                timeRange: input.timeRange,
            });
            let checkpointId;
            if (request.context && typeof request.context.checkpointId === 'string') {
                checkpointId = request.context.checkpointId;
            }
            else {
                const latest = await checkpointManager.getLatestCheckpoint(input.sessionId);
                if (latest) {
                    checkpointId = latest.id;
                }
            }
            if (!checkpointId) {
                const checkpoint = await checkpointManager.createCheckpoint(input.sessionId, 'expert_fork', types_1.AgentPhase.EXECUTING, [], input.previousFindings || [], {
                    query: input.query,
                    traceId: input.traceId,
                    metadata: {
                        forkReason: request.reason,
                        forkFocus: request.focus,
                        forkHypothesis: request.hypothesis,
                    },
                });
                checkpointId = checkpoint.id;
            }
            const forkResult = await forkManager.fork(input.sessionId, {
                checkpointId,
                branchName: request.focus || request.reason || 'fork',
                description: request.reason,
                hypothesis: request.hypothesis,
                inheritConfig: true,
            });
            if (!forkResult.success) {
                return {
                    forkSessionId: '',
                    success: false,
                    findings: [],
                    error: forkResult.error || 'Fork failed',
                };
            }
            return {
                forkSessionId: forkResult.forkedSessionId,
                success: true,
                findings: [],
            };
        }
        catch (error) {
            return {
                forkSessionId: '',
                success: false,
                findings: [],
                error: error.message || 'Fork failed',
            };
        }
    }
    /**
     * Execute decision tree
     */
    async executeDecisionTree(tree, input, architecture) {
        // Build decision context
        const context = {
            sessionId: input.sessionId,
            traceId: input.traceId,
            query: input.query,
            architecture,
            traceProcessorService: input.traceProcessorService,
            previousResults: new Map(),
            timeRange: input.timeRange,
            packageName: input.packageName,
            analysisParams: input.analysisParams,
        };
        // Add previous findings to context
        if (input.previousFindings) {
            context.previousResults.set('previousFindings', input.previousFindings);
        }
        // Execute tree
        this.log(`Executing decision tree: ${tree.name} (${tree.id})`);
        const result = await this.treeExecutor.execute(tree, context);
        // Update state with execution path
        if (this.state) {
            this.state.executionPath = result.executionPath;
            this.state.collectedData = result.collectedData;
        }
        return result;
    }
    /**
     * Build output from decision tree result
     */
    buildOutput(treeResult, input, startTime) {
        if (!treeResult) {
            return {
                expertId: this.config.id,
                domain: this.config.domain,
                success: false,
                findings: [],
                suggestions: ['分析未能完成'],
                error: 'No decision tree result',
                confidence: 0,
            };
        }
        // Transform conclusion
        const conclusion = treeResult.conclusion
            ? this.transformConclusion(treeResult.conclusion, treeResult.collectedData)
            : undefined;
        // Extract findings
        const findings = this.extractFindings(treeResult.collectedData);
        // Add conclusion as a finding if available
        if (conclusion) {
            findings.unshift({
                id: `conclusion_${Date.now()}`,
                type: 'root_cause',
                severity: conclusion.category === 'APP' ? 'high' : 'medium',
                title: `根因: ${conclusion.component}`,
                description: conclusion.summary,
                source: 'expert',
                confidence: conclusion.confidence,
                details: {
                    category: conclusion.category,
                    component: conclusion.component,
                },
                recommendations: conclusion.optimizationSuggestions.map((s, i) => ({
                    id: `rec_${i}`,
                    text: s,
                    priority: i + 1,
                })),
            });
        }
        return {
            expertId: this.config.id,
            domain: this.config.domain,
            success: treeResult.success,
            conclusion,
            findings,
            suggestions: conclusion?.optimizationSuggestions || [],
            data: Object.fromEntries(treeResult.collectedData),
            executionDetails: {
                treeId: treeResult.treeId,
                executionPath: treeResult.executionPath,
                totalDurationMs: treeResult.totalDurationMs,
            },
            confidence: conclusion?.confidence || 0.5,
        };
    }
    /**
     * Update state phase
     */
    updateState(phase) {
        if (this.state) {
            this.state.phase = phase;
            this.state.lastUpdateTime = Date.now();
            this.emit('state:update', { phase, state: this.state });
        }
    }
    /**
     * Log message with expert context
     */
    log(message) {
        console.log(`[${this.config.name}] ${message}`);
    }
}
exports.BaseExpert = BaseExpert;
exports.default = BaseExpert;
//# sourceMappingURL=baseExpert.js.map