import { EventEmitter } from 'events';
import { getLogger } from '../../../core/logger.js';
import { AppError, ErrorCode, formatDuration, handleError, safeParse, truncate, unique, validateInput, } from '../../../utils/common.js';
import { formatStyleGuideContext } from '../../../utils/style-guide-formatter.js';
import { getWritingTextMetrics as getTextMetrics } from '../../../utils/text-metrics.js';
export class SpecializedAgent extends EventEmitter {
    constructor(langchain, advanced, persona) {
        super();
        this.operationMetrics = new Map();
        this.langchain = langchain;
        this.advanced = advanced;
        this.logger = getLogger(`agent-${persona.name.toLowerCase()}`);
        this.persona = persona;
        // Set up error handling
        this.on('error', (error) => {
            this.logger.error(`Agent ${persona.name} error:`, error);
        });
        // Track performance metrics
        this.on('operation', (operation, duration) => {
            this.updateOperationMetrics(operation, duration);
        });
    }
    updateOperationMetrics(operation, duration) {
        const existing = this.operationMetrics.get(operation);
        if (existing) {
            existing.totalTime += duration;
            existing.callCount += 1;
        }
        else {
            this.operationMetrics.set(operation, {
                totalTime: duration,
                callCount: 1,
            });
        }
    }
    getPerformanceMetrics() {
        const result = {};
        for (const [operation, metrics] of this.operationMetrics.entries()) {
            result[operation] = {
                avgTime: metrics.totalTime / metrics.callCount,
                callCount: metrics.callCount,
                totalTime: metrics.totalTime,
            };
        }
        return result;
    }
    async generateAnalysis(document, prompt, styleGuide) {
        const startTime = performance.now();
        try {
            validateInput({ document, prompt }, {
                document: { type: 'object', required: true },
                prompt: { type: 'string', required: true, minLength: 10, maxLength: 10000 },
            });
            const textMetrics = getTextMetrics(document.content || '');
            const styleContext = styleGuide ? formatStyleGuideContext(styleGuide) : '';
            const contextualPrompt = `
${this.persona.perspective}

**Agent Role**: ${this.persona.role}
**Expertise**: ${this.persona.expertise.join(', ')}
**Focus Areas**: ${this.persona.focusAreas.join(', ')}

**Document Analysis**:
- Title: ${document.title}
- Word Count: ${textMetrics.wordCount}
- Reading Time: ${formatDuration(textMetrics.readingTimeMinutes * 60 * 1000)}
- Content Preview: ${truncate(document.content || '', 500)}

${styleContext ? `**Style Guidelines**:\n${styleContext}` : ''}

**Analysis Request**:
${prompt}

Please provide a detailed analysis focusing on your areas of expertise. Structure your response as JSON with the following format:
{
  "perspective": "brief description of your analytical perspective",
  "findings": [
    {
      "aspect": "specific aspect analyzed",
      "assessment": "detailed assessment",
      "confidence": 0.0-1.0,
      "evidence": ["specific examples from the text"],
      "suggestions": ["actionable recommendations"]
    }
  ],
  "overallScore": 0-100,
  "priority": "critical|high|medium|low",
  "reasoning": "explanation of your assessment and score"
}

Focus on ${this.persona.focusAreas.join(', ')} and consider potential ${this.persona.biases.join(', ')} in your analysis.
			`.trim();
            const response = await this.langchain.generateWithFallback(contextualPrompt);
            const analysisData = safeParse(response, {});
            if (!analysisData || typeof analysisData !== 'object') {
                throw new AppError('Failed to parse agent analysis response', ErrorCode.PROCESSING_ERROR);
            }
            const analysis = {
                agentId: this.persona.name,
                perspective: analysisData.perspective || this.persona.perspective,
                findings: Array.isArray(analysisData.findings)
                    ? analysisData.findings.map((finding) => ({
                        aspect: String(finding.aspect || 'General'),
                        assessment: String(finding.assessment || ''),
                        confidence: Math.min(Math.max(Number(finding.confidence || 0.5), 0), 1),
                        evidence: Array.isArray(finding.evidence)
                            ? finding.evidence.map(String)
                            : [],
                        suggestions: Array.isArray(finding.suggestions)
                            ? finding.suggestions.map(String)
                            : [],
                    }))
                    : [],
                overallScore: Math.min(Math.max(Number(analysisData.overallScore || 50), 0), 100),
                priority: ['critical', 'high', 'medium', 'low'].includes(analysisData.priority)
                    ? analysisData.priority
                    : 'medium',
                reasoning: String(analysisData.reasoning || 'Analysis completed'),
            };
            const duration = performance.now() - startTime;
            this.emit('operation', 'generateAnalysis', duration);
            this.logger.debug('Analysis generated successfully', {
                agentId: analysis.agentId,
                findingsCount: analysis.findings.length,
                overallScore: analysis.overallScore,
                priority: analysis.priority,
                duration: formatDuration(duration),
            });
            return analysis;
        }
        catch (error) {
            const duration = performance.now() - startTime;
            this.emit('operation', 'generateAnalysis', duration);
            throw handleError(error, 'SpecializedAgent.generateAnalysis');
        }
    }
    async discussWith(otherAgent, topic, initialContext, maxRounds = 3) {
        const startTime = performance.now();
        const rounds = [];
        try {
            validateInput({ topic, initialContext, maxRounds }, {
                topic: { type: 'string', required: true, minLength: 5, maxLength: 500 },
                initialContext: { type: 'string', required: true, minLength: 10 },
                maxRounds: { type: 'number', required: true, min: 1, max: 10 },
            });
            this.logger.info(`Starting discussion between ${this.persona.name} and ${otherAgent.persona.name}`, {
                topic: truncate(topic, 100),
                maxRounds,
            });
            let currentContext = initialContext;
            for (let roundNum = 1; roundNum <= maxRounds; roundNum++) {
                const roundStartTime = performance.now();
                // Generate contributions from both agents
                const [myContribution, theirContribution] = await Promise.all([
                    this.generateDiscussionContribution(topic, currentContext, rounds),
                    otherAgent.generateDiscussionContribution(topic, currentContext, rounds),
                ]);
                // Analyze the contributions
                const agreements = await this.findAgreements(myContribution.message, theirContribution.message);
                const disagreements = await this.findDisagreements(myContribution.message, theirContribution.message);
                const newInsights = await this.extractNewInsights(myContribution.message, theirContribution.message, currentContext);
                const round = {
                    roundNumber: roundNum,
                    contributions: [myContribution, theirContribution],
                    agreements,
                    disagreements,
                    newInsights,
                    timestamp: Date.now(),
                };
                rounds.push(round);
                // Update context for next round
                currentContext = [
                    currentContext,
                    `Round ${roundNum} contributions:`,
                    `${this.persona.name}: ${truncate(myContribution.message, 200)}`,
                    `${otherAgent.persona.name}: ${truncate(theirContribution.message, 200)}`,
                    agreements.length > 0 ? `Agreements: ${agreements.join(', ')}` : '',
                    newInsights.length > 0 ? `New insights: ${newInsights.join(', ')}` : '',
                ]
                    .filter(Boolean)
                    .join('\n\n');
                const roundDuration = performance.now() - roundStartTime;
                this.logger.debug(`Discussion round ${roundNum} completed`, {
                    agreements: agreements.length,
                    disagreements: disagreements.length,
                    newInsights: newInsights.length,
                    duration: formatDuration(roundDuration),
                });
                // Check if we've reached consensus
                if (disagreements.length === 0 && agreements.length > 0) {
                    this.logger.info('Early consensus reached, ending discussion', {
                        roundNumber: roundNum,
                        agreementsCount: agreements.length,
                    });
                    break;
                }
            }
            const totalDuration = performance.now() - startTime;
            this.emit('operation', 'discussWith', totalDuration);
            this.logger.info('Discussion completed', {
                totalRounds: rounds.length,
                totalAgreements: unique(rounds.flatMap((r) => r.agreements)).length,
                totalInsights: unique(rounds.flatMap((r) => r.newInsights)).length,
                duration: formatDuration(totalDuration),
            });
            return rounds;
        }
        catch (error) {
            const duration = performance.now() - startTime;
            this.emit('operation', 'discussWith', duration);
            throw handleError(error, 'SpecializedAgent.discussWith');
        }
    }
    async discussWithRoundtable(sharedContext, topic) {
        const startTime = performance.now();
        try {
            const prompt = `
You are participating in a ROUNDTABLE DISCUSSION with other writing experts.
**Topic**: ${topic}
**Shared Knowledge Context**: 
${truncate(sharedContext, 2000)}

**Your Expert Persona**: ${this.persona.name} (${this.persona.role})
**Focus**: ${this.persona.focusAreas.join(', ')}

Please provide your contribution to the group discussion. 
- Validate or critique the findings mentioned in the context.
- Bring in your unique perspective from your expertise.
- Propose specific, high-value improvements for the manuscript.
- Address any conflicts you see in the shared context.

Respond with a focused, analytical message (150-400 words).
			`.trim();
            const response = await this.langchain.generateWithFallback(prompt);
            const duration = performance.now() - startTime;
            this.emit('operation', 'roundtableContribution', duration);
            return {
                agentId: this.persona.name,
                message: response.trim(),
                confidence: 0.9, // Higher confidence for SOTA mode
                references: [],
                timestamp: Date.now(),
            };
        }
        catch (error) {
            throw handleError(error, `Agent ${this.persona.name} roundtable contribution failed`);
        }
    }
    async generateDiscussionContribution(topic, context, previousRounds) {
        const prompt = `
As ${this.persona.name} (${this.persona.role}), contribute to this discussion:

**Topic**: ${topic}
**Current Context**: ${truncate(context, 1000)}
**Your Expertise**: ${this.persona.expertise.join(', ')}
**Your Perspective**: ${this.persona.perspective}

${previousRounds.length > 0
            ? `
**Previous Discussion Rounds**:
${previousRounds
                .slice(-2)
                .map((round) => `Round ${round.roundNumber}: ${round.contributions
                .map((c) => `${c.agentId}: ${truncate(c.message, 150)}`)
                .join(' | ')}`)
                .join('\n')}
`
            : ''}

Please provide your perspective on this topic. Consider:
- Your areas of expertise: ${this.persona.focusAreas.join(', ')}
- Your communication style: ${this.persona.communicationStyle}
- Potential biases you might have: ${this.persona.biases.join(', ')}

Respond with a thoughtful contribution (100-300 words) that adds value to the discussion.
		`.trim();
        const response = await this.langchain.generateWithFallback(prompt);
        return {
            agentId: this.persona.name,
            message: response.trim(),
            confidence: 0.8,
            references: [], // Could be extracted from the response
            timestamp: Date.now(),
        };
    }
    async findAgreements(message1, message2) {
        const prompt = `
Compare these two messages and identify specific points of agreement:

Message 1: ${truncate(message1, 500)}
Message 2: ${truncate(message2, 500)}

List specific points where both messages agree or align. Be concise and specific.
Return only the agreement points, one per line.
		`.trim();
        const response = await this.langchain.generateWithFallback(prompt);
        return response
            .split('\n')
            .map((line) => line.trim())
            .filter((line) => line.length > 0)
            .slice(0, 5); // Limit to top 5 agreements
    }
    async findDisagreements(message1, message2) {
        const prompt = `
Compare these two messages and identify specific points of disagreement or different perspectives:

Message 1: ${truncate(message1, 500)}
Message 2: ${truncate(message2, 500)}

List specific points where the messages disagree or offer different perspectives. Be concise and specific.
Return only the disagreement points, one per line.
		`.trim();
        const response = await this.langchain.generateWithFallback(prompt);
        return response
            .split('\n')
            .map((line) => line.trim())
            .filter((line) => line.length > 0)
            .slice(0, 5); // Limit to top 5 disagreements
    }
    async extractNewInsights(message1, message2, context) {
        const prompt = `
Given this context: ${truncate(context, 300)}

Analyze these two messages for new insights or ideas that weren't present in the original context:

Message 1: ${truncate(message1, 400)}
Message 2: ${truncate(message2, 400)}

Identify novel insights, creative connections, or new perspectives that emerged from this exchange.
Return only the new insights, one per line.
		`.trim();
        const response = await this.langchain.generateWithFallback(prompt);
        return response
            .split('\n')
            .map((line) => line.trim())
            .filter((line) => line.length > 0 && line.length < 200)
            .slice(0, 3); // Limit to top 3 insights
    }
}
//# sourceMappingURL=base-agent.js.map