import { EventEmitter } from 'events';
import {
  runPlannerAgent,
  runResearcherAgent,
  runSynthesizerAgent,
  runCriticAgent,
} from './agents.js';
import type {
  ResearchState,
  ResearchOptions,
  ResearchPlan,
  ResearchFinding,
  ResearchReport,
  CriticFeedback,
} from './types.js';
import { createLogger, type Logger } from '../utils/logger.js';

const DEFAULT_OPTIONS: Required<ResearchOptions> = {
  depth: 'medium',
  maxIterations: 3,
  model: 'claude-sonnet-4-20250514',
  includeCodeExamples: true,
  enableWebSearch: false,
};

/**
 * Research Controller
 *
 * Orchestrates the multi-agent research workflow:
 * 1. Planner creates a research plan with sub-questions
 * 2. Researcher investigates each question
 * 3. Synthesizer creates a report from findings
 * 4. Critic validates the report (may trigger revision)
 */
export class ResearchController extends EventEmitter {
  private repoRoot: string;
  private logger: Logger;
  private options: Required<ResearchOptions>;
  private state: ResearchState | null = null;

  constructor(options: { repoRoot: string; logger?: Logger } & ResearchOptions) {
    super();
    this.repoRoot = options.repoRoot;
    this.logger = options.logger ?? createLogger({ name: 'research' });
    this.options = { ...DEFAULT_OPTIONS, ...options };
  }

  /**
   * Run a research session
   */
  async run(goal: string): Promise<ResearchState> {
    const sessionId = `research-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;

    this.state = {
      id: sessionId,
      goal,
      status: 'planning',
      findings: [],
      iterations: 0,
      startedAt: new Date().toISOString(),
    };

    this.emit('start', { sessionId, goal });

    try {
      // Phase 1: Planning
      this.logger.info({ goal }, 'Starting research planning');
      this.emit('phase', { phase: 'planning' });

      const plan = await this.runPlanning(goal);
      this.state.plan = plan;
      this.state.status = 'researching';

      this.emit('plan', { plan });

      // Phase 2: Research
      this.logger.info({ questionCount: plan.questions.length }, 'Starting research');
      this.emit('phase', { phase: 'researching' });

      const findings = await this.runResearch(plan);
      this.state.findings = findings;
      this.state.status = 'synthesizing';

      this.emit('findings', { count: findings.length });

      // Phase 3: Synthesis
      this.logger.info({ findingCount: findings.length }, 'Starting synthesis');
      this.emit('phase', { phase: 'synthesizing' });

      let report = await this.runSynthesis(plan, findings);
      this.state.report = report;
      this.state.status = 'reviewing';

      this.emit('report', { report });

      // Phase 4: Critic loop
      this.logger.info('Starting critic review');
      this.emit('phase', { phase: 'reviewing' });

      let feedback = await this.runCritic(report, findings);
      this.state.criticFeedback = feedback;
      this.state.iterations++;

      // Revision loop if needed
      while (feedback.requiresRevision && this.state.iterations < this.options.maxIterations) {
        this.logger.info(
          { iteration: this.state.iterations, score: feedback.score },
          'Revision required'
        );
        this.emit('revision', { iteration: this.state.iterations, feedback });

        // Re-synthesize with critic feedback
        report = await this.runRevision(plan, findings, report, feedback);
        this.state.report = report;

        // Re-evaluate
        feedback = await this.runCritic(report, findings);
        this.state.criticFeedback = feedback;
        this.state.iterations++;
      }

      // Complete
      this.state.status = 'completed';
      this.state.completedAt = new Date().toISOString();

      this.logger.info(
        { iterations: this.state.iterations, score: feedback.score },
        'Research completed'
      );
      this.emit('complete', { state: this.state });

      return this.state;
    } catch (err) {
      this.state.status = 'failed';
      this.state.error = err instanceof Error ? err.message : String(err);
      this.state.completedAt = new Date().toISOString();

      this.logger.error({ error: this.state.error }, 'Research failed');
      this.emit('error', { error: this.state.error });

      return this.state;
    }
  }

  /**
   * Get current state
   */
  getState(): ResearchState | null {
    return this.state;
  }

  /**
   * Run planning phase
   */
  private async runPlanning(goal: string): Promise<ResearchPlan> {
    return runPlannerAgent(goal, {
      repoRoot: this.repoRoot,
      model: this.options.model,
      logger: this.logger,
    });
  }

  /**
   * Run research phase
   */
  private async runResearch(plan: ResearchPlan): Promise<ResearchFinding[]> {
    const allFindings: ResearchFinding[] = [];

    // Determine how many questions to investigate based on depth
    const questionLimit =
      this.options.depth === 'shallow' ? 3 : this.options.depth === 'medium' ? 5 : plan.questions.length;

    const questionsToInvestigate = plan.questions
      .sort((a, b) => {
        const priorityOrder = { high: 0, medium: 1, low: 2 };
        return priorityOrder[a.priority] - priorityOrder[b.priority];
      })
      .slice(0, questionLimit);

    for (const question of questionsToInvestigate) {
      this.logger.debug({ question: question.question }, 'Investigating question');
      this.emit('question', { question });

      question.status = 'researching';

      try {
        const findings = await runResearcherAgent(
          question,
          { goal: plan.goal, previousFindings: allFindings },
          {
            repoRoot: this.repoRoot,
            model: this.options.model,
            logger: this.logger,
          }
        );

        allFindings.push(...findings);
        question.status = 'completed';

        this.emit('questionComplete', { question, findingCount: findings.length });
      } catch (err) {
        question.status = 'failed';
        this.logger.error({ question: question.question, error: err }, 'Question investigation failed');
      }
    }

    return allFindings;
  }

  /**
   * Run synthesis phase
   */
  private async runSynthesis(
    plan: ResearchPlan,
    findings: ResearchFinding[]
  ): Promise<ResearchReport> {
    return runSynthesizerAgent(plan, findings, {
      repoRoot: this.repoRoot,
      model: this.options.model,
      logger: this.logger,
    });
  }

  /**
   * Run critic phase
   */
  private async runCritic(
    report: ResearchReport,
    findings: ResearchFinding[]
  ): Promise<CriticFeedback> {
    return runCriticAgent(report, findings, {
      repoRoot: this.repoRoot,
      model: this.options.model,
      logger: this.logger,
    });
  }

  /**
   * Run revision based on critic feedback
   */
  private async runRevision(
    plan: ResearchPlan,
    findings: ResearchFinding[],
    previousReport: ResearchReport,
    feedback: CriticFeedback
  ): Promise<ResearchReport> {
    // For revision, we re-run synthesis with the feedback as additional context
    // This could be enhanced to do targeted re-research
    const enhancedPlan = {
      ...plan,
      approach: `${plan.approach}\n\nCritic feedback to address:\n- Weaknesses: ${feedback.weaknesses.join(', ')}\n- Suggestions: ${feedback.suggestions.join(', ')}`,
    };

    return runSynthesizerAgent(enhancedPlan, findings, {
      repoRoot: this.repoRoot,
      model: this.options.model,
      logger: this.logger,
    });
  }
}
