'use strict';

/**
 * Heuristic knowledge extractor: regex-based, no LLM needed.
 * Extracts decisions, preferences, milestones, problems, and emotional patterns.
 * Returns knowledge entries with extraction_method: 'heuristic'.
 */

const PATTERNS = {
  decision: {
    regex: /\b(decided|chose|going with|switching to|moved to|opted for|selected|picked|settled on|let's use|we decided|instead of|trade-off|went with|choosing|commit to|committing to|final call|architecture|stack choice)\b/i,
    predicate: 'decided',
    category: 'decision',
  },
  preference: {
    regex: /\b(prefer|prefers|preferred|like|likes|love|loves|hate|hates|favorite|favourite|rather use|rather have|always use|never use|my rule is|my go-to|I tend to|better than|worse than|fan of|not a fan)\b/i,
    predicate: 'prefers',
    category: 'preference',
  },
  milestone: {
    regex: /\b(shipped|launched|deployed|completed|released|finished|published|went live|merged|delivered|it works|got it working|fixed|breakthrough|v\d+\.\d+|\d+x faster|production ready|first release|milestone|achieved|accomplished|resolved|closed|done)\b/i,
    predicate: 'achieved',
    category: 'milestone',
  },
  problem: {
    regex: /\b(bug|broken|crash|crashes|crashing|error|errors|blocked|stuck|failing|failed|outage|down|degraded|doesn't work|root cause|the fix is|regression|flaky|timeout|memory leak|race condition|deadlock|corrupted|can't reproduce)\b/i,
    predicate: 'encountered',
    category: 'problem',
  },
  emotional: {
    regex: /\b(frustrated|frustrating|happy|excited|worried|anxious|stressed|annoyed|thrilled|proud|disappointed|relieved|overwhelmed|scared|grateful|miss|love this|hate this|proud of|feel like|burned out|motivated|confident|uncertain|hopeful|dreading)\b/i,
    predicate: 'felt',
    category: 'emotional',
  },
};

/**
 * Extract the sentence containing the matched keyword.
 * Returns the surrounding sentence (delimited by . ! ? or newlines).
 */
function extractSentence(content, matchIndex) {
  // Find sentence boundaries around the match
  const before = content.lastIndexOf('.', matchIndex);
  const beforeBang = content.lastIndexOf('!', matchIndex);
  const beforeQ = content.lastIndexOf('?', matchIndex);
  const beforeNl = content.lastIndexOf('\n', matchIndex);
  const start = Math.max(before, beforeBang, beforeQ, beforeNl, 0);

  const after = content.indexOf('.', matchIndex);
  const afterBang = content.indexOf('!', matchIndex);
  const afterQ = content.indexOf('?', matchIndex);
  const afterNl = content.indexOf('\n', matchIndex);
  const candidates = [after, afterBang, afterQ, afterNl].filter(i => i > matchIndex);
  const end = candidates.length > 0 ? Math.min(...candidates) + 1 : Math.min(content.length, matchIndex + 200);

  return content.slice(start === 0 ? 0 : start + 1, end).trim().slice(0, 300);
}

// Regex patterns to detect code lines (skip these for knowledge extraction)
const CODE_LINE_PATTERNS = [
  /^\s*(import |from |require\(|const |let |var |function |class |def |async |export |module\.)/,
  /^\s*(if |else |for |while |switch |return |throw |try |catch )/,
  /^\s*[A-Z_]{2,}\s*=/, // ENV_VAR=value
  /^\s*[{}[\]();,]$/, // lone brackets
  /^\s*(cd |source |echo |export |pip |npm |git |docker |kubectl |curl |wget )/,
  /^\s*```/, // code fences
  /^\s*[#\/]{2,}/, // comments
  /^\s*<[a-z][\w-]*[\s/>]/, // HTML/XML tags
];

/**
 * Check if a line is likely code rather than prose.
 */
function isCodeLine(line) {
  return CODE_LINE_PATTERNS.some(p => p.test(line));
}

/**
 * Filter out code lines, keeping only prose content.
 */
function filterProseOnly(content) {
  return content.split('\n')
    .filter(line => !isCodeLine(line))
    .join('\n');
}

const POSITIVE_WORDS = new Set([
  'pride', 'proud', 'joy', 'happy', 'love', 'beautiful', 'amazing', 'great', 'excellent',
  'wonderful', 'fantastic', 'awesome', 'perfect', 'brilliant', 'solved', 'fixed', 'working',
  'success', 'breakthrough', 'improved', 'better', 'faster', 'stronger', 'achievement',
]);

const NEGATIVE_WORDS = new Set([
  'bug', 'error', 'crash', 'fail', 'broken', 'issue', 'problem', 'terrible', 'awful',
  'panic', 'disaster', 'frustrated', 'angry', 'annoyed', 'stuck', 'blocked', 'confused',
  'worried', 'anxious', 'stressed', 'overwhelmed', 'disappointed', 'regression', 'flaky',
]);

/**
 * Analyze sentiment of text. Returns 'positive', 'negative', or 'neutral'.
 */
function analyzeSentiment(text) {
  const words = text.toLowerCase().split(/\W+/);
  let pos = 0, neg = 0;
  for (const w of words) {
    if (POSITIVE_WORDS.has(w)) pos++;
    if (NEGATIVE_WORDS.has(w)) neg++;
  }
  if (pos > neg) return 'positive';
  if (neg > pos) return 'negative';
  return 'neutral';
}

/**
 * Disambiguate category when both problem and milestone/emotional patterns match.
 */
function disambiguate(category, sentence) {
  if (category !== 'problem') return category;
  const sentiment = analyzeSentiment(sentence);
  // "fixed the bug" = milestone, not problem
  if (/\b(fixed|solved|resolved|working now|got it working)\b/i.test(sentence)) {
    return sentiment === 'positive' ? 'milestone' : 'problem';
  }
  if (sentiment === 'positive') return 'milestone';
  return category;
}

/**
 * Extract heuristic knowledge from a memory.
 * @param {object} memory - Memory object with { id, content, source, timestamp }
 * @param {string} [ownerName] - Owner name to use as default subject
 * @returns {Array<object>} Array of knowledge entries
 */
function extractHeuristics(memory, ownerName) {
  if (!memory || !memory.content) return [];
  const content = memory.content;
  if (content.length < 20) return [];

  // Filter out code lines for cleaner extraction
  const prose = filterProseOnly(content);
  if (prose.length < 20) return [];

  const results = [];
  const seen = new Set();

  for (const [type, config] of Object.entries(PATTERNS)) {
    const match = config.regex.exec(prose);
    if (!match) continue;

    const sentence = extractSentence(prose, match.index);
    if (!sentence || sentence.length < 10) continue;

    // Skip if sentence is mostly code
    if (isCodeLine(sentence)) continue;

    // Dedup key: category + first 50 chars of sentence
    const key = `${type}:${sentence.slice(0, 50)}`;
    if (seen.has(key)) continue;
    seen.add(key);

    // Disambiguate category (e.g., "fixed the bug" → milestone, not problem)
    const finalCategory = disambiguate(config.category, sentence);

    // Variable confidence based on pattern strength and sentence length
    let confidence = 0.4;
    if (sentence.length > 50) confidence += 0.1; // longer = more context
    if (sentence.length > 100) confidence += 0.1;
    confidence = Math.min(confidence, 0.8);

    results.push({
      category: finalCategory,
      subject: ownerName || 'owner',
      predicate: PATTERNS[type].predicate, // use original type's predicate
      object: sentence,
      confidence,
      extraction_method: 'heuristic',
      source_memory_ids: [memory.id],
    });
  }

  return results;
}

module.exports = { extractHeuristics, PATTERNS, extractSentence, isCodeLine, filterProseOnly, analyzeSentiment, disambiguate };
