#!/usr/bin/env node
/**
 * Intelligence Layer Stub (ADR-050)
 * Minimal fallback — full version is copied from package source.
 * Provides: init, getContext, recordEdit, feedback, consolidate
 */
'use strict';

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

const DATA_DIR = path.join(process.cwd(), '.claude-flow', 'data');
const STORE_PATH = path.join(DATA_DIR, 'auto-memory-store.json');
const RANKED_PATH = path.join(DATA_DIR, 'ranked-context.json');
const PENDING_PATH = path.join(DATA_DIR, 'pending-insights.jsonl');
const SESSION_DIR = path.join(process.cwd(), '.claude-flow', 'sessions');
const SESSION_FILE = path.join(SESSION_DIR, 'current.json');

// ── Safety limits (fixes #1530, #1531) ─────────────────────────────────────
var MAX_DATA_FILE_SIZE = 10 * 1024 * 1024; // 10 MB — skip files larger than this
var MAX_GRAPH_NODES = 5000;                 // skip PageRank if graph exceeds this

function ensureDir(dir) {
  if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
}

function readJSON(p) {
  // Safety: skip files exceeding MAX_DATA_FILE_SIZE (#1531)
  try { var stat = fs.statSync(p); if (stat.size > MAX_DATA_FILE_SIZE) { process.stderr.write("[INTELLIGENCE] WARN: Skipping " + path.basename(p) + " (" + Math.round(stat.size / 1048576) + "MB exceeds 10MB limit)\n"); return null; } } catch(e) { /* file may not exist */ }
  try { return fs.existsSync(p) ? JSON.parse(fs.readFileSync(p, "utf-8")) : null; }
  catch { return null; }
}

function writeJSON(p, data) {
  ensureDir(path.dirname(p));
  fs.writeFileSync(p, JSON.stringify(data, null, 2), "utf-8");
}

function sessionGet(key) {
  var session = readJSON(SESSION_FILE);
  if (!session) return null;
  return key ? (session.context || {})[key] : session.context;
}

function sessionSet(key, value) {
  var session = readJSON(SESSION_FILE);
  if (!session) return;
  if (!session.context) session.context = {};
  session.context[key] = value;
  writeJSON(SESSION_FILE, session);
}

function tokenize(text) {
  if (!text) return [];
  return text.toLowerCase().replace(/[^a-z0-9\s]/g, " ").split(/\s+/).filter(function(w) { return w.length > 2; });
}

// ── Truncation transparency (FIX 4) ─────────────────────────────────────────
// Silently severing stored values means later reasoning can be built on
// incomplete text. Warn (debug-gated) and mark the cut point with an ellipsis
// so it is visible that the value was clipped.
var DEBUG = !!(process.env.RUFLO_DEBUG || process.env.DEBUG);
function clip(text, max, label) {
  text = text == null ? "" : String(text);
  if (text.length <= max) return text;
  if (DEBUG) {
    process.stderr.write("[INTELLIGENCE] WARN: truncated " + (label || "value") +
      " from " + text.length + " to " + max + " chars\n");
  }
  return text.slice(0, max - 1) + "…";
}

// ── Deduplication helper (fixes #1518) ──────────────────────────────────────
function deduplicateById(entries) {
  if (!entries || !Array.isArray(entries)) return entries;
  var seen = new Map();
  for (var i = 0; i < entries.length; i++) {
    var id = entries[i].id || entries[i].key;
    if (id) {
      seen.set(id, entries[i]);
    } else {
      seen.set('__no_id_' + seen.size, entries[i]);
    }
  }
  return Array.from(seen.values());
}

function bootstrapFromMemoryFiles() {
  var entries = [];
  // Scope to current project only (not all 51+ project dirs).
  // Match Claude Code's project-dir slug convention: every non-alphanumeric char
  // becomes '-' (e.g. "G:\\My Drive\\TJ_Vault" -> "G--My-Drive-TJ-Vault"). The old
  // version only stripped a leading '/' and replaced '/', so on Windows the slug
  // kept ':' and '\\' and never matched the real ~/.claude/projects/<slug> dir —
  // memory bootstrap then silently found nothing (FIX 5). Runs are NOT collapsed:
  // Claude emits "G--My" because ':' and '\\' each map to their own '-'.
  var projectSlug = process.cwd().replace(/[^a-zA-Z0-9]/g, '-');
  var candidates = [
    path.join(os.homedir(), ".claude", "projects", projectSlug, "memory"),
    path.join(process.cwd(), ".claude-flow", "memory"),
    path.join(process.cwd(), ".claude", "memory"),
  ];
  for (var i = 0; i < candidates.length; i++) {
    try {
      if (!fs.existsSync(candidates[i])) continue;
      var files = [];
      try {
        var items = fs.readdirSync(candidates[i], { withFileTypes: true, recursive: true });
        for (var j = 0; j < items.length; j++) {
          if (items[j].name === "MEMORY.md") {
            var parentDir = items[j].parentPath || items[j].path || candidates[i];
            var fp = path.join(parentDir, items[j].name);
            files.push(fp);
          }
        }
      } catch (e) { continue; }
      for (var k = 0; k < files.length; k++) {
        try {
          var content = fs.readFileSync(files[k], "utf-8");
          var sections = content.split(/^##\s+/m).filter(function(s) { return s.trim().length > 20; });
          for (var s = 0; s < sections.length; s++) {
            var lines = sections[s].split("\n");
            var title = lines[0] ? lines[0].trim() : "section-" + s;
            var clippedContent = clip(sections[s], 500, "memory content");
            entries.push({
              id: "mem-" + entries.length,
              content: clippedContent,
              summary: clip(title, 100, "memory summary"),
              category: "memory",
              confidence: 0.5,
              sourceFile: files[k],
              words: tokenize(clippedContent),
            });
          }
        } catch (e) { /* skip */ }
      }
    } catch (e) { /* skip */ }
  }
  return entries;
}

function loadEntries() {
  var store = readJSON(STORE_PATH);
  // Support both formats: flat array or { entries: [...] }
  var entries = null;
  if (store) {
    if (Array.isArray(store) && store.length > 0) {
      entries = store;
    } else if (store.entries && store.entries.length > 0) {
      entries = store.entries;
    }
  }
  if (entries) {
    return entries.map(function(e, i) {
      return {
        id: e.id || ("entry-" + i),
        content: e.content || e.value || "",
        summary: e.summary || e.key || "",
        category: e.category || e.namespace || "default",
        confidence: e.confidence || 0.5,
        sourceFile: e.sourceFile || (e.metadata && e.metadata.sourceFile) || "",
        words: tokenize((e.content || e.value || "") + " " + (e.summary || e.key || "")),
      };
    });
  }
  return bootstrapFromMemoryFiles();
}

function matchScore(promptWords, entryWords) {
  if (!promptWords.length || !entryWords.length) return 0;
  var entrySet = {};
  for (var i = 0; i < entryWords.length; i++) entrySet[entryWords[i]] = true;
  var overlap = 0;
  for (var j = 0; j < promptWords.length; j++) {
    if (entrySet[promptWords[j]]) overlap++;
  }
  var union = Object.keys(entrySet).length + promptWords.length - overlap;
  return union > 0 ? overlap / union : 0;
}

var cachedEntries = null;

module.exports = {
  init: function() {
    cachedEntries = deduplicateById(loadEntries());
    var ranked = cachedEntries.map(function(e) {
      return { id: e.id, content: e.content, summary: e.summary, category: e.category, confidence: e.confidence, words: e.words };
    });
    writeJSON(RANKED_PATH, { version: 1, computedAt: Date.now(), entries: ranked });
    return { nodes: cachedEntries.length, edges: 0 };
  },

  getContext: function(prompt) {
    if (!prompt) return null;
    var ranked = readJSON(RANKED_PATH);
    var entries = (ranked && ranked.entries) || (cachedEntries || []);
    if (!entries.length) return null;
    var promptWords = tokenize(prompt);
    if (!promptWords.length) return null;
    var scored = entries.map(function(e) {
      return { entry: e, score: matchScore(promptWords, e.words || tokenize(e.content + " " + e.summary)) };
    }).filter(function(s) { return s.score > 0.05; });
    scored.sort(function(a, b) { return b.score - a.score; });
    var top = scored.slice(0, 5);
    if (!top.length) return null;
    var prevMatched = sessionGet("lastMatchedPatterns");
    var matchedIds = top.map(function(s) { return s.entry.id; });
    sessionSet("lastMatchedPatterns", matchedIds);
    var lines = ["[INTELLIGENCE] Relevant patterns for this task:"];
    for (var j = 0; j < top.length; j++) {
      var e = top[j];
      var conf = e.entry.confidence || 0.5;
      var summary = (e.entry.summary || e.entry.content || "").substring(0, 80);
      lines.push("  * (" + conf.toFixed(2) + ") " + summary);
    }
    return lines.join("\n");
  },

  recordEdit: function(file) {
    if (!file) return;
    ensureDir(DATA_DIR);
    var line = JSON.stringify({ type: "edit", file: file, timestamp: Date.now() }) + "\n";
    fs.appendFileSync(PENDING_PATH, line, "utf-8");
  },

  feedback: function(success) {
    // Stub: no-op in minimal version
  },

  consolidate: function() {
    var count = 0;
    if (fs.existsSync(PENDING_PATH)) {
      try {
        var content = fs.readFileSync(PENDING_PATH, "utf-8").trim();
        count = content ? content.split("\n").length : 0;
        fs.writeFileSync(PENDING_PATH, "", "utf-8");
      } catch (e) { /* skip */ }
    }
    return { entries: count, edges: 0, newEntries: 0 };
  },

  stats: function(json) {
    var ranked = readJSON(RANKED_PATH);
    var count = ranked && ranked.entries ? ranked.entries.length : 0;
    if (json) {
      console.log(JSON.stringify({ entries: count, computedAt: ranked ? ranked.computedAt : null }));
    } else {
      console.log('[INTELLIGENCE] Stats: ' + count + ' entries loaded');
    }
  },
};
