// PRE Web GUI — Calibrated Self-Knowledge System
// Tracks model accuracy across task categories, building a confidence profile
// that tells the model what it's good at and where it needs extra care.
//
// Data sources:
//   - Tool success/failure rates per category
//   - User corrections (detected by negation patterns in follow-up messages)
//   - Experience ledger outcomes (success vs. failure lessons)
//   - Explicit user feedback ("good job" / "that's wrong")
//
// Output: A confidence profile injected into the system prompt, e.g.:
//   "You excel at file operations and bash scripting. You tend to struggle with
//    complex calendar date math — take extra care and verify dates."

const fs = require('fs');
const path = require('path');
const { PRE_DIR } = require('./constants');

const CALIBRATION_FILE = path.join(PRE_DIR, 'calibration.json');

// ── Task Categories ──────────────────────────────────────────────────────────

const CATEGORIES = {
  file_ops:      { label: 'File operations',     tools: ['read_file', 'write_file', 'edit_file', 'list_files', 'search_files', 'move_file', 'delete_file'] },
  bash:          { label: 'Shell commands',       tools: ['bash', 'process_list', 'process_kill'] },
  web:           { label: 'Web operations',       tools: ['web_search', 'web_fetch', 'browser'] },
  memory:        { label: 'Memory management',    tools: ['memory_save', 'memory_search', 'memory_delete', 'memory_health'] },
  code:          { label: 'Code analysis',        tools: ['read_file', 'edit_file', 'bash'] },
  calendar:      { label: 'Calendar & scheduling',tools: ['apple_calendar', 'apple_reminders', 'cron'] },
  email:         { label: 'Email',                tools: ['apple_mail', 'google_mail', 'slack'] },
  cloud:         { label: 'Cloud integrations',   tools: ['github', 'jira', 'confluence', 'sharepoint', 'smartsheet', 'linear', 'asana'] },
  image:         { label: 'Image generation',     tools: ['image_generate'] },
  documents:     { label: 'Document creation',    tools: ['create_artifact', 'create_document', 'export'] },
  rag:           { label: 'RAG & search',         tools: ['rag'] },
  computer:      { label: 'Desktop automation',   tools: ['computer', 'workflow'] },
};

// Positive/negative feedback patterns from user messages
const POSITIVE_PATTERNS = [
  /\b(perfect|excellent|great|good job|well done|nice|exactly|correct|thanks|thank you)\b/i,
  /^(yes|yep|yeah|yup|right|ok)\b/i,
  /\bthat'?s (right|correct|what i wanted|perfect)\b/i,
];

const NEGATIVE_PATTERNS = [
  /\bno[,.]?\s+(that'?s|not|wrong|incorrect)/i,
  /\bdon'?t\b.*\bthat\b/i,
  /\btry again\b/i,
  /\bthat'?s not (what|right|correct)/i,
  /\bwrong\b/i,
  /\bincorrect\b/i,
  /\bstop\b.*\bdoing\b/i,
];


// ── Profile Data Structure ───────────────────────────────────────────────────

/**
 * Default calibration profile.
 * categories: { [key]: { successes, failures, corrections, total, lastUpdated } }
 */
function defaultProfile() {
  const cats = {};
  for (const key of Object.keys(CATEGORIES)) {
    cats[key] = { successes: 0, failures: 0, corrections: 0, total: 0, lastUpdated: null };
  }
  return {
    categories: cats,
    overall: { successes: 0, failures: 0, corrections: 0, total: 0 },
    lastUpdated: null,
    version: 1,
  };
}

function loadProfile() {
  try {
    if (fs.existsSync(CALIBRATION_FILE)) {
      const data = JSON.parse(fs.readFileSync(CALIBRATION_FILE, 'utf-8'));
      // Ensure all categories exist (forward compatibility)
      for (const key of Object.keys(CATEGORIES)) {
        if (!data.categories[key]) {
          data.categories[key] = { successes: 0, failures: 0, corrections: 0, total: 0, lastUpdated: null };
        }
      }
      return data;
    }
  } catch (_e) {}
  return defaultProfile();
}

function saveProfile(profile) {
  profile.lastUpdated = new Date().toISOString();
  try {
    fs.writeFileSync(CALIBRATION_FILE, JSON.stringify(profile, null, 2));
  } catch (err) {
    console.log(`[calibration] Failed to save profile: ${err.message}`);
  }
}


// ── Tool-to-Category Mapping ─────────────────────────────────────────────────

/**
 * Given a tool name, return the matching category key(s).
 * A tool can belong to multiple categories.
 */
function categorize(toolName) {
  const matches = [];
  for (const [key, cat] of Object.entries(CATEGORIES)) {
    if (cat.tools.some(t => toolName.includes(t) || t.includes(toolName))) {
      matches.push(key);
    }
  }
  return matches.length > 0 ? matches : ['general'];
}


// ── Recording Events ─────────────────────────────────────────────────────────

/**
 * Record a tool execution outcome.
 * @param {string} toolName - Tool that was executed
 * @param {boolean} success - Whether the tool succeeded
 */
function recordToolOutcome(toolName, success) {
  const profile = loadProfile();
  const cats = categorize(toolName);

  for (const cat of cats) {
    if (!profile.categories[cat]) {
      profile.categories[cat] = { successes: 0, failures: 0, corrections: 0, total: 0, lastUpdated: null };
    }
    const c = profile.categories[cat];
    c.total++;
    if (success) c.successes++;
    else c.failures++;
    c.lastUpdated = new Date().toISOString();
  }

  profile.overall.total++;
  if (success) profile.overall.successes++;
  else profile.overall.failures++;

  saveProfile(profile);
}

/**
 * Record a user correction for the most recent category.
 * @param {string[]} recentTools - Tools used in the turn that was corrected
 */
function recordCorrection(recentTools) {
  const profile = loadProfile();
  const cats = new Set();
  for (const t of recentTools) {
    for (const c of categorize(t)) cats.add(c);
  }

  for (const cat of cats) {
    if (!profile.categories[cat]) {
      profile.categories[cat] = { successes: 0, failures: 0, corrections: 0, total: 0, lastUpdated: null };
    }
    profile.categories[cat].corrections++;
    profile.categories[cat].lastUpdated = new Date().toISOString();
  }

  profile.overall.corrections++;
  saveProfile(profile);
}

/**
 * Analyze a user message for feedback signals and record accordingly.
 * @param {string} userMessage - The user's message
 * @param {string[]} recentTools - Tools from the previous assistant turn
 * @returns {'positive'|'negative'|null}
 */
function detectFeedback(userMessage, recentTools) {
  if (!userMessage || !recentTools || recentTools.length === 0) return null;

  if (NEGATIVE_PATTERNS.some(p => p.test(userMessage))) {
    recordCorrection(recentTools);
    return 'negative';
  }

  if (POSITIVE_PATTERNS.some(p => p.test(userMessage))) {
    // Boost success for the relevant categories
    const profile = loadProfile();
    const cats = new Set();
    for (const t of recentTools) {
      for (const c of categorize(t)) cats.add(c);
    }
    for (const cat of cats) {
      if (profile.categories[cat]) {
        profile.categories[cat].successes++;
        profile.categories[cat].total++;
        profile.categories[cat].lastUpdated = new Date().toISOString();
      }
    }
    profile.overall.successes++;
    profile.overall.total++;
    saveProfile(profile);
    return 'positive';
  }

  return null;
}


// ── Confidence Scoring ───────────────────────────────────────────────────────

/**
 * Calculate confidence score for a category (0.0 to 1.0).
 * Weights: corrections penalized 2x, success rate is primary signal.
 * Minimum 5 observations needed for meaningful confidence.
 */
function confidence(cat) {
  if (!cat || cat.total < 5) return null; // Not enough data
  const correctionPenalty = cat.corrections * 2;
  const effectiveTotal = cat.total + correctionPenalty;
  return Math.max(0, Math.min(1, cat.successes / effectiveTotal));
}

/**
 * Get the full confidence report across all categories.
 */
function getConfidenceReport() {
  const profile = loadProfile();
  const report = {};

  for (const [key, cat] of Object.entries(profile.categories)) {
    const conf = confidence(cat);
    if (conf === null) continue; // Skip categories with insufficient data
    report[key] = {
      label: CATEGORIES[key]?.label || key,
      confidence: Math.round(conf * 100),
      successes: cat.successes,
      failures: cat.failures,
      corrections: cat.corrections,
      total: cat.total,
    };
  }

  return {
    categories: report,
    overall: {
      confidence: confidence(profile.overall),
      ...profile.overall,
    },
    lastUpdated: profile.lastUpdated,
  };
}


// ── System Prompt Injection ──────────────────────────────────────────────────

/**
 * Build a self-knowledge context block for the system prompt.
 * Only includes categories with enough data (5+ observations).
 * Highlights strengths and weaknesses.
 */
function buildCalibrationContext() {
  const profile = loadProfile();
  const strengths = [];
  const weaknesses = [];

  for (const [key, cat] of Object.entries(profile.categories)) {
    const conf = confidence(cat);
    if (conf === null) continue;

    const label = CATEGORIES[key]?.label || key;
    if (conf >= 0.85) {
      strengths.push(label);
    } else if (conf < 0.60) {
      weaknesses.push({ label, confidence: Math.round(conf * 100), corrections: cat.corrections });
    }
  }

  if (strengths.length === 0 && weaknesses.length === 0) return '';

  const lines = ['SELF-KNOWLEDGE (calibrated from your past performance):'];
  if (strengths.length > 0) {
    lines.push(`- Strengths: ${strengths.join(', ')}. You consistently succeed with these.`);
  }
  if (weaknesses.length > 0) {
    for (const w of weaknesses) {
      lines.push(`- Caution area: ${w.label} (${w.confidence}% success rate, ${w.corrections} user correction${w.corrections !== 1 ? 's' : ''}). Take extra care, verify results, and consider alternative approaches.`);
    }
  }

  return lines.join('\n');
}


// ── Skill Recommendation ─────────────────────────────────────────────────────

/**
 * Given tools that are about to be used, warn if they're in a low-confidence area.
 * Returns an advisory string or null.
 */
function getAdvisory(toolNames) {
  const profile = loadProfile();
  const warnings = [];

  for (const toolName of toolNames) {
    const cats = categorize(toolName);
    for (const catKey of cats) {
      const cat = profile.categories[catKey];
      const conf = confidence(cat);
      if (conf !== null && conf < 0.60) {
        const label = CATEGORIES[catKey]?.label || catKey;
        warnings.push(`${label} (${Math.round(conf * 100)}% confidence)`);
      }
    }
  }

  if (warnings.length === 0) return null;
  const unique = [...new Set(warnings)];
  return `Note: You have lower confidence in: ${unique.join(', ')}. Consider verifying results carefully.`;
}


// ── Public API ───────────────────────────────────────────────────────────────

module.exports = {
  recordToolOutcome,
  recordCorrection,
  detectFeedback,
  getConfidenceReport,
  buildCalibrationContext,
  getAdvisory,
  loadProfile,
  saveProfile,
  defaultProfile,
  categorize,
  confidence,
  // Constants for testing
  CATEGORIES,
  POSITIVE_PATTERNS,
  NEGATIVE_PATTERNS,
};
