'use strict';
// Local-only Node.js version guard for the CTM + Wall-E monorepo.
//
// Why: the daemon, /ctm-dev, and skill subprocesses share a single compiled
// better-sqlite3 / sqlite-vec / tree-sitter binary, which is built for ONE Node
// major (ABI). Launching under a different major corrupts that binary and
// crash-loops the daemon ("MCP never became ready" + skill-failure storm).
//
// This guard activates ONLY inside the owner's repo, identified by a
// `.node-version` pin file sitting beside `wall-e/` and `claude-task-manager/`.
// In a shipped create-walle install the pin is excluded from the package
// (see create-walle/build.sh), so findPinRoot() returns null and this is a
// silent no-op — it never constrains other users' Node version.

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

// Walk up from startDir until we find the monorepo root: a directory that has a
// `.node-version` pin AND both project dirs. The triple condition prevents us
// from hijacking an unrelated `.node-version` somewhere above a user's install.
function findPinRoot(startDir) {
  let dir = startDir;
  for (;;) {
    if (
      fs.existsSync(path.join(dir, '.node-version')) &&
      fs.existsSync(path.join(dir, 'wall-e')) &&
      fs.existsSync(path.join(dir, 'claude-task-manager'))
    ) {
      return dir;
    }
    const parent = path.dirname(dir);
    if (parent === dir) return null;
    dir = parent;
  }
}

function readPin(root) {
  try {
    return fs.readFileSync(path.join(root, '.node-version'), 'utf8').replace(/[v\s]/g, '');
  } catch {
    return '';
  }
}

// Assert the running Node matches the pinned MAJOR (the ABI-critical part — a
// patch bump within the line keeps the same NODE_MODULE_VERSION). On mismatch:
// hard-fail with remediation (default) or throw when opts.throwInstead is set.
// Returns {pinned:false} as a no-op when there is no pin (shipped / non-owner).
function assertPinnedNode(opts = {}) {
  const startDir = opts.startDir || __dirname;
  const root = findPinRoot(startDir);
  if (!root) return { pinned: false };
  const pin = readPin(root);
  if (!pin) return { pinned: false };

  const pinnedMajor = pin.split('.')[0];
  const runningMajor = process.versions.node.split('.')[0];
  if (runningMajor === pinnedMajor) {
    return { pinned: true, ok: true, root, pin, running: process.versions.node };
  }

  if (opts.throwInstead) {
    const err = new Error(
      `Node version mismatch: pinned v${pin} (major ${pinnedMajor}), running v${process.versions.node}`
    );
    err.code = 'NODE_PIN_MISMATCH';
    throw err;
  }

  const resolver = path.join(root, 'bin', 'node-bin.sh');
  const devSh = path.join(root, 'bin', 'dev.sh');
  process.stderr.write(
    [
      '',
      '  ✖ Wrong Node.js version for CTM + Wall-E.',
      `      pinned : v${pin}  (ABI line ${pinnedMajor}.x)`,
      `      running: v${process.versions.node}  (NODE_MODULE_VERSION ${process.versions.modules})`,
      '',
      '  The shared native modules (better-sqlite3, sqlite-vec, tree-sitter) are',
      '  compiled for ONE Node major. Launching under another major corrupts that',
      '  binary and crash-loops the daemon. Start through the pinned launcher:',
      '',
      `      "$(bash ${resolver})" <script>      # resolves the pinned node`,
      `      bash ${devSh}                        # isolated dev instance`,
      `      fnm use ${pin}   |   nvm use ${pin}    # then re-run`,
      '',
    ].join('\n') + '\n'
  );
  process.exit(78); // EX_CONFIG
  return { pinned: true, ok: false }; // unreachable
}

module.exports = { assertPinnedNode, findPinRoot, readPin };
