#!/usr/bin/env node
/**
 * Self-Correcting Memory Architecture - Critic Engine
 * 
 * Flow:
 * 1. Generate response → 2. Critic checks against correction store → 
 * 3. If violations → Block & regenerate → 4. If new pattern → Auto-store
 * 
 * Principles:
 * - ALL output validated against correction store before delivery
 * - Quantitative claims without proof → auto-correction
 * - Corrections immediately block future occurrences
 * - System learns from its own mistakes
 */

const fs = require('fs');
const path = require('path');

class CriticEngine {
  constructor(options = {}) {
    this.correctionStorePath = options.correctionStorePath || '/home/jcbeasley/.openclaw/workspace/memory/corrections';
    this.autoLearnEnabled = options.autoLearn !== false;
    this.strictMode = options.strictMode || false; // Block on warnings too
    
    // Correction patterns (loaded from store + hardcoded rules)
    this.corrections = [];
    this.patternRules = this.getPatternRules();
  }

  /**
   * Pattern Rules - Auto-detect uncorrected issues
   */
  getPatternRules() {
    return [
      {
        id: 'quantitative_without_proof',
        name: 'Quantitative Claim Without Evidence',
        pattern: /\b\d+\s*(%|percent|times|x|percent|fold)\b/i,
        exclude: /\b(according to|based on|from|measured|tested|verified|source:|citation|data:)\b/i,
        severity: 'auto-correct',
        message: 'Quantitative claim requires evidence',
        autoStore: true
      },
      {
        id: 'unverified_performance',
        name: 'Performance Claim Without Benchmark',
        pattern: /\b(faster|slower|better|worse|improved|optimized)\s+(than|by)\b/i,
        exclude: /\b(measured|benchmarked|tested|profiled|verified)\b/i,
        severity: 'auto-correct',
        message: 'Performance claims require benchmarks',
        autoStore: true
      },
      {
        id: 'absolute_without_qualification',
        name: 'Absolute Statement Without Qualification',
        pattern: /\b(always|never|all|none|every|impossible)\b/i,
        exclude: /\b(in this case|for this|under these|given the|based on)\b/i,
        severity: 'warning',
        message: 'Absolute statements need qualification',
        autoStore: false // Manual review first
      },
      {
        id: 'vague_quantification',
        name: 'Vague Quantification',
        pattern: /\b(many|few|several|some|most|lots|a lot)\b/i,
        exclude: /\b(specifically|exactly|precisely)\b/i,
        severity: 'auto-correct',
        message: 'Use specific numbers or omit',
        autoStore: true
      },
      {
        id: 'unverified_completion',
        name: '"Done" Without Verification',
        pattern: /\b(done|complete|finished|working)\b/i,
        exclude: /\b(verified|tested|confirmed|validated|checked)\b/i,
        severity: 'error',
        message: 'Completion claims require verification evidence',
        autoStore: true
      }
    ];
  }

  /**
   * Load corrections from store
   */
  async loadCorrections() {
    // Load from NocoDB (to be implemented) or local JSON
    const correctionsFile = path.join(this.correctionStorePath, '_index.json');
    try {
      if (fs.existsSync(correctionsFile)) {
        const data = JSON.parse(fs.readFileSync(correctionsFile, 'utf8'));
        this.corrections = data.corrections || [];
      }
    } catch (err) {
      console.warn('Could not load corrections:', err.message);
    }
    return this;
  }

  /**
   * Main Critic Check
   * Returns: { passed: bool, violations: [], newPatterns: [], corrected: string }
   */
  async critique(response, context = {}) {
    await this.loadCorrections();
    
    const violations = [];
    const newPatterns = [];
    
    // 1. Check against known corrections (blocking)
    const knownViolations = this.checkKnownCorrections(response);
    violations.push(...knownViolations);
    
    // 2. Auto-detect new patterns (if enabled)
    if (this.autoLearnEnabled && violations.length === 0) {
      const detectedPatterns = this.detectNewPatterns(response, context);
      
      for (const pattern of detectedPatterns) {
        if (pattern.severity === 'auto-correct') {
          // Auto-store as new correction
          await this.storeCorrection(pattern);
          violations.push({
            ...pattern,
            message: `${pattern.message} [AUTO-STORED AS CORRECTION]`,
            autoStored: true
          });
        } else if (pattern.severity === 'warning') {
          newPatterns.push(pattern);
        }
      }
    }
    
    // 3. Generate corrected version if violations found
    let corrected = response;
    if (violations.length > 0) {
      corrected = await this.generateCorrection(response, violations);
    }
    
    return {
      passed: violations.length === 0,
      blocked: violations.length > 0 && violations.some(v => v.blocking),
      violations,
      newPatterns,
      corrected,
      original: response
    };
  }

  /**
   * Check against known corrections (from store)
   */
  checkKnownCorrections(response) {
    const violations = [];
    
    for (const correction of this.corrections) {
      const pattern = new RegExp(correction.pattern, 'i');
      
      if (pattern.test(response)) {
        // Check if exclusion applies
        if (correction.excludePattern) {
          const exclude = new RegExp(correction.excludePattern, 'i');
          if (exclude.test(response)) {
            continue; // Has exclusion, skip
          }
        }
        
        violations.push({
          id: correction.id,
          type: 'known-correction',
          severity: correction.severity || 'error',
          blocking: correction.blocking !== false,
          message: correction.message,
          suggestion: correction.suggestion,
          originalPattern: correction.pattern
        });
      }
    }
    
    return violations;
  }

  /**
   * Detect new patterns that should become corrections
   */
  detectNewPatterns(response, context) {
    const detected = [];
    
    for (const rule of this.patternRules) {
      if (rule.pattern.test(response)) {
        // Check if exclusion applies
        if (rule.exclude && rule.exclude.test(response)) {
          continue;
        }
        
        detected.push({
          id: rule.id,
          type: 'auto-detected',
          severity: rule.severity,
          message: rule.message,
          pattern: rule.pattern.source,
          autoStore: rule.autoStore,
          context: context.task || 'unknown'
        });
      }
    }
    
    return detected;
  }

  /**
   * Store new correction (immediate write)
   */
  async storeCorrection(pattern) {
    const correction = {
      id: `correction_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
      pattern: pattern.pattern,
      severity: pattern.severity,
      message: pattern.message,
      suggestion: `Add evidence or qualification`,
      blocking: true,
      autoDetected: true,
      createdAt: new Date().toISOString(),
      context: pattern.context
    };
    
    // Add to memory
    this.corrections.push(correction);
    
    // Persist to disk (immediate)
    await this.persistCorrections();
    
    // Log for audit
    console.log(`[CRITIC] Auto-stored correction: ${correction.id} - ${pattern.message}`);
    
    return correction;
  }

  /**
   * Persist corrections to disk (will move to NocoDB)
   */
  async persistCorrections() {
    try {
      if (!fs.existsSync(this.correctionStorePath)) {
        fs.mkdirSync(this.correctionStorePath, { recursive: true });
      }
      
      const indexFile = path.join(this.correctionStorePath, '_index.json');
      fs.writeFileSync(indexFile, JSON.stringify({
        version: 1,
        updatedAt: new Date().toISOString(),
        corrections: this.corrections
      }, null, 2));
      
      // Also write individual correction files for inspection
      for (const correction of this.corrections.slice(-10)) { // Last 10
        const filePath = path.join(this.correctionStorePath, `${correction.id}.json`);
        fs.writeFileSync(filePath, JSON.stringify(correction, null, 2));
      }
    } catch (err) {
      console.error('[CRITIC] Failed to persist corrections:', err);
    }
  }

  /**
   * Generate corrected version of response
   */
  async generateCorrection(original, violations) {
    // Simple correction: add warning header
    const warnings = violations.map(v => `- ${v.message}`).join('\n');
    
    return `⚠️ CORRECTIONS REQUIRED:\n${warnings}\n\n---\n\n${original}`;
    
    // Future: Use LLM to actually fix the content
    // const fixPrompt = buildFixPrompt(original, violations);
    // return await llm.generate(fixPrompt);
  }

  /**
   * Manual correction entry (for user feedback)
   */
  async addManualCorrection(originalText, correctionText, reason) {
    const correction = {
      id: `manual_${Date.now()}`,
      pattern: this.escapeRegex(originalText.substring(0, 100)),
      severity: 'error',
      message: reason,
      suggestion: correctionText,
      blocking: true,
      manualEntry: true,
      createdAt: new Date().toISOString()
    };
    
    this.corrections.push(correction);
    await this.persistCorrections();
    
    return correction;
  }

  /**
   * Escape regex special chars
   */
  escapeRegex(str) {
    return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
  }
}

// Export
module.exports = CriticEngine;

// CLI usage
if (require.main === module) {
  const responseFile = process.argv[2];
  if (!responseFile) {
    console.error('Usage: node critic.js <response-file>');
    process.exit(1);
  }
  
  const response = fs.readFileSync(responseFile, 'utf8');
  const critic = new CriticEngine();
  
  critic.critique(response)
    .then(result => {
      console.log(JSON.stringify(result, null, 2));
    })
    .catch(err => {
      console.error('Critic error:', err);
      process.exit(1);
    });
}
