// ---------------------------------------------------------------------------
// questionGenerator -- analyzes evolution context (signals, session transcripts,
// recent events) and generates proactive questions for the Hub bounty system.
//
// Questions are sent via the A2A fetch payload.questions field. The Hub creates
// bounties from them, enabling multi-agent collaborative problem solving.
// ---------------------------------------------------------------------------

const fs = require('fs');
const path = require('path');
const { getEvolutionDir } = require('./paths');

const QUESTION_STATE_FILE = path.join(getEvolutionDir(), 'question_generator_state.json');
const MIN_INTERVAL_MS = 3 * 60 * 60 * 1000; // at most once per 3 hours
const MAX_QUESTIONS_PER_CYCLE = 2;

function readState() {
  try {
    if (fs.existsSync(QUESTION_STATE_FILE)) {
      return JSON.parse(fs.readFileSync(QUESTION_STATE_FILE, 'utf8'));
    }
  } catch (_) {}
  return { lastAskedAt: null, recentQuestions: [] };
}

function writeState(state) {
  try {
    const dir = path.dirname(QUESTION_STATE_FILE);
    if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
    fs.writeFileSync(QUESTION_STATE_FILE, JSON.stringify(state, null, 2) + '\n');
  } catch (_) {}
}

function isDuplicate(question, recentQuestions) {
  var qLower = question.toLowerCase();
  for (var i = 0; i < recentQuestions.length; i++) {
    var prev = String(recentQuestions[i] || '').toLowerCase();
    if (prev === qLower) return true;
    // fuzzy: if >70% overlap by word set
    var qWords = new Set(qLower.split(/\s+/).filter(function(w) { return w.length > 2; }));
    var pWords = new Set(prev.split(/\s+/).filter(function(w) { return w.length > 2; }));
    if (qWords.size === 0 || pWords.size === 0) continue;
    var overlap = 0;
    qWords.forEach(function(w) { if (pWords.has(w)) overlap++; });
    if (overlap / Math.max(qWords.size, pWords.size) > 0.7) return true;
  }
  return false;
}

/**
 * Generate proactive questions based on evolution context.
 *
 * @param {object} opts
 * @param {string[]} opts.signals - current cycle signals
 * @param {object[]} opts.recentEvents - recent EvolutionEvent objects
 * @param {string} opts.sessionTranscript - recent session transcript
 * @param {string} opts.memorySnippet - MEMORY.md content
 * @returns {Array<{ question: string, amount: number, signals: string[] }>}
 */
function generateQuestions(opts) {
  var o = opts || {};
  var signals = Array.isArray(o.signals) ? o.signals : [];
  var recentEvents = Array.isArray(o.recentEvents) ? o.recentEvents : [];
  var transcript = String(o.sessionTranscript || '');
  var memory = String(o.memorySnippet || '');

  var state = readState();

  // Rate limit: don't ask too frequently
  if (state.lastAskedAt) {
    var elapsed = Date.now() - new Date(state.lastAskedAt).getTime();
    if (elapsed < MIN_INTERVAL_MS) return [];
  }

  var candidates = [];
  var signalSet = new Set(signals);

  // --- Strategy 1: Recurring errors the agent cannot resolve ---
  if (signalSet.has('recurring_error') || signalSet.has('high_failure_ratio')) {
    var errSig = signals.find(function(s) { return s.startsWith('recurring_errsig'); });
    if (errSig) {
      var errDetail = errSig.replace(/^recurring_errsig\(\d+x\):/, '').trim().slice(0, 120);
      candidates.push({
        question: 'Recurring error in evolution cycle that auto-repair cannot resolve: ' + errDetail + ' -- What approaches or patches have worked for similar issues?',
        amount: 0,
        signals: ['recurring_error', 'auto_repair_failed'],
        priority: 3,
      });
    }
  }

  // --- Strategy 2: Capability gaps detected from user conversations ---
  if (signalSet.has('capability_gap') || signalSet.has('unsupported_input_type')) {
    var gapContext = '';
    var lines = transcript.split('\n');
    for (var i = 0; i < lines.length; i++) {
      if (/not supported|cannot|unsupported|not implemented/i.test(lines[i])) {
        gapContext = lines[i].replace(/\s+/g, ' ').trim().slice(0, 150);
        break;
      }
    }
    if (gapContext) {
      candidates.push({
        question: 'Capability gap detected in agent environment: ' + gapContext + ' -- How can this be addressed or what alternative approaches exist?',
        amount: 0,
        signals: ['capability_gap'],
        priority: 2,
      });
    }
  }

  // --- Strategy 3: Stagnation / saturation -- seek new directions ---
  if (signalSet.has('evolution_saturation') || signalSet.has('force_steady_state')) {
    var recentGenes = [];
    var last5 = recentEvents.slice(-5);
    for (var j = 0; j < last5.length; j++) {
      var genes = last5[j].genes_used;
      if (Array.isArray(genes) && genes.length > 0) {
        recentGenes.push(genes[0]);
      }
    }
    var uniqueGenes = Array.from(new Set(recentGenes));
    candidates.push({
      question: 'Agent evolution has reached saturation after exhausting genes: [' + uniqueGenes.join(', ') + ']. What new evolution directions, automation patterns, or capability genes would be most valuable?',
      amount: 0,
      signals: ['evolution_saturation', 'innovation_needed'],
      priority: 1,
    });
  }

  // --- Strategy 4: Consecutive failure streak -- seek external help ---
  var failStreak = signals.find(function(s) { return s.startsWith('consecutive_failure_streak_'); });
  if (failStreak) {
    var streakCount = parseInt(failStreak.replace('consecutive_failure_streak_', ''), 10) || 0;
    if (streakCount >= 4) {
      var failGene = signals.find(function(s) { return s.startsWith('ban_gene:'); });
      var failGeneId = failGene ? failGene.replace('ban_gene:', '') : 'unknown';
      candidates.push({
        question: 'Agent has failed ' + streakCount + ' consecutive evolution cycles (last gene: ' + failGeneId + '). The current approach is exhausted. What alternative strategies or environmental fixes should be tried?',
        amount: 0,
        signals: ['failure_streak', 'external_help_needed'],
        priority: 3,
      });
    }
  }

  // --- Strategy 5: User feature requests the agent can amplify ---
  if (signalSet.has('user_feature_request') || signals.some(function (s) { return String(s).startsWith('user_feature_request:'); })) {
    var featureLines = transcript.split('\n').filter(function(l) {
      return /\b(add|implement|create|build|i want|i need|please add)\b/i.test(l);
    });
    if (featureLines.length > 0) {
      var featureContext = featureLines[0].replace(/\s+/g, ' ').trim().slice(0, 150);
      candidates.push({
        question: 'User requested a feature that may benefit from community solutions: ' + featureContext + ' -- Are there existing implementations or best practices for this?',
        amount: 0,
        signals: ['user_feature_request', 'community_solution_sought'],
        priority: 1,
      });
    }
  }

  // --- Strategy 6: Performance bottleneck -- seek optimization patterns ---
  if (signalSet.has('perf_bottleneck')) {
    var perfLines = transcript.split('\n').filter(function(l) {
      return /\b(slow|timeout|latency|bottleneck|high cpu|high memory)\b/i.test(l);
    });
    if (perfLines.length > 0) {
      var perfContext = perfLines[0].replace(/\s+/g, ' ').trim().slice(0, 150);
      candidates.push({
        question: 'Performance bottleneck detected: ' + perfContext + ' -- What optimization strategies or architectural patterns address this?',
        amount: 0,
        signals: ['perf_bottleneck', 'optimization_sought'],
        priority: 2,
      });
    }
  }

  if (candidates.length === 0) return [];

  // Sort by priority (higher = more urgent)
  candidates.sort(function(a, b) { return b.priority - a.priority; });

  // De-duplicate against recently asked questions
  var recentQTexts = Array.isArray(state.recentQuestions) ? state.recentQuestions : [];
  var filtered = [];
  for (var fi = 0; fi < candidates.length && filtered.length < MAX_QUESTIONS_PER_CYCLE; fi++) {
    if (!isDuplicate(candidates[fi].question, recentQTexts)) {
      filtered.push(candidates[fi]);
    }
  }

  if (filtered.length === 0) return [];

  // Update state
  var newRecentQuestions = recentQTexts.concat(filtered.map(function(q) { return q.question; }));
  // Keep only last 20 questions in history
  if (newRecentQuestions.length > 20) {
    newRecentQuestions = newRecentQuestions.slice(-20);
  }
  writeState({
    lastAskedAt: new Date().toISOString(),
    recentQuestions: newRecentQuestions,
  });

  // Strip internal priority field before returning
  return filtered.map(function(q) {
    return { question: q.question, amount: q.amount, signals: q.signals };
  });
}

module.exports = { generateQuestions };
