#!/usr/bin/env node
// Node version gate — uses only ES5 syntax so it parses on any Node version.
// On old Node: walks through curl/wget install, nvm install, then Node 22, then reinstall.
var nodeVersion = parseInt(process.versions.node, 10);

if (nodeVersion < 22) {
  // Prevent infinite loop: if we're inside a self-triggered reinstall, bail
  if (process.env.OA_SELF_UPGRADE === "1") {
    console.error("  [launcher] Skipping — already inside a self-upgrade.");
    console.error("  Open a new terminal with Node 22 and run: oa");
    process.exit(1);
  }

  var os = require("os");
  var path = require("path");
  var fs = require("fs");
  var childProcess = require("child_process");
  var readline = require("readline");

  var platform = os.platform();
  var arch = os.arch();

  console.log("");
  console.log("  ┌─────────────────────────────────────────────────┐");
  console.log("  │         open-agents — Node.js Upgrade           │");
  console.log("  └─────────────────────────────────────────────────┘");
  console.log("");
  console.log("  Your Node.js:  " + process.version);
  console.log("  Required:      >= 22.0.0");
  console.log("  Platform:      " + platform + "/" + arch);
  console.log("");

  // Detect if nvm is already installed
  var nvmDir = process.env.NVM_DIR || path.join(os.homedir(), ".nvm");
  var hasNvm = false;
  try { fs.statSync(path.join(nvmDir, "nvm.sh")); hasNvm = true; } catch (e) {}

  var rl = readline.createInterface({ input: process.stdin, output: process.stdout });

  function ask(question, cb) {
    rl.question(question, function(answer) {
      cb(answer.trim().toLowerCase());
    });
  }

  function hasCmd(cmd) {
    try {
      childProcess.execSync("which " + cmd, { stdio: "pipe", timeout: 3000 });
      return true;
    } catch (e) {
      return false;
    }
  }

  function run(cmd) {
    console.log("  $ " + cmd);
    try {
      childProcess.execSync(cmd, { stdio: "inherit" });
      return true;
    } catch (e) {
      console.error("  Command failed: " + (e.message || e));
      return false;
    }
  }

  function bail(msg) {
    console.log(msg);
    rl.close();
    process.exit(1);
  }

  // Returns "curl -o-" or "wget -qO-" or null
  function getDownloader() {
    if (hasCmd("curl")) return "curl -o-";
    if (hasCmd("wget")) return "wget -qO-";
    return null;
  }

  // Try to install curl if neither curl nor wget is available
  function ensureDownloader(next) {
    var dl = getDownloader();
    if (dl) { next(dl); return; }

    console.log("  Neither curl nor wget found.");
    console.log("");

    ask("  Install curl now? [Y/n] ", function(a) {
      if (a === "n" || a === "no") {
        bail(
          "\n  Install manually:\n" +
          "    sudo apt install curl  (Debian/Ubuntu)\n" +
          "    sudo dnf install curl  (Fedora)\n" +
          "    sudo pacman -S curl    (Arch)\n"
        );
        return;
      }
      console.log("");
      var ok = false;
      if (hasCmd("apt-get")) {
        ok = run("sudo -n apt-get install -y curl 2>/dev/null || sudo apt-get install -y curl");
      } else if (hasCmd("dnf")) {
        ok = run("sudo -n dnf install -y curl 2>/dev/null || sudo dnf install -y curl");
      } else if (hasCmd("pacman")) {
        ok = run("sudo -n pacman -S --noconfirm curl 2>/dev/null || sudo pacman -S --noconfirm curl");
      } else if (hasCmd("apk")) {
        ok = run("apk add curl");
      } else {
        console.log("  No supported package manager found (tried apt, dnf, pacman, apk).");
      }

      if (ok && hasCmd("curl")) {
        console.log("");
        next("curl -o-");
      } else {
        bail(
          "\n  Could not install curl. Install it manually, then re-run:\n" +
          "    npm i -g open-agents-ai\n"
        );
      }
    });
  }

  function doInstallNvm(dl, next) {
    if (hasNvm) {
      console.log("  nvm found at " + nvmDir);
      console.log("");
      next();
      return;
    }
    ask("  nvm is not installed. Install nvm now? [Y/n] ", function(a) {
      if (a === "n" || a === "no") {
        bail(
          "\n  Install nvm manually:\n" +
          "    curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash\n" +
          "    # or: wget -qO- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash\n" +
          "    source ~/.bashrc\n" +
          "    nvm install 22\n" +
          "    npm i -g open-agents-ai\n"
        );
        return;
      }
      console.log("");
      var ok = run(dl + " https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash");
      if (!ok) {
        bail("\n  nvm install failed. See https://github.com/nvm-sh/nvm#installing-and-updating\n");
        return;
      }
      nvmDir = process.env.NVM_DIR || path.join(os.homedir(), ".nvm");
      hasNvm = true;
      console.log("");
      next();
    });
  }

  function doInstallNode(next) {
    ask("  Install Node.js 22 via nvm? [Y/n] ", function(a) {
      if (a === "n" || a === "no") {
        bail(
          "\n  Install manually:\n" +
          "    nvm install 22 && nvm alias default 22\n" +
          "    npm i -g open-agents-ai\n"
        );
        return;
      }
      console.log("");
      var shell = process.env.SHELL || "/bin/bash";
      var cmd = 'export NVM_DIR="' + nvmDir + '" && ' +
        '[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh" && ' +
        "nvm install 22 && nvm use 22 && nvm alias default 22";
      var ok = run(shell + " -c '" + cmd + "'");
      if (!ok) {
        bail("\n  Node 22 install failed.\n");
        return;
      }
      console.log("");
      next();
    });
  }

  // Find the Node 22 binary installed by nvm
  function findNode22Bin() {
    // Check nvm versions directory for a v22.x binary
    var versionsDir = path.join(nvmDir, "versions", "node");
    try {
      var versions = fs.readdirSync(versionsDir).filter(function(v) {
        return v.startsWith("v22");
      }).sort().reverse();
      if (versions.length > 0) {
        var bin = path.join(versionsDir, versions[0], "bin", "node");
        try { fs.statSync(bin); return bin; } catch (e) {}
      }
    } catch (e) {}
    // Fallback: ask nvm which node 22 is
    try {
      var shell = process.env.SHELL || "/bin/bash";
      var result = childProcess.execSync(
        shell + ' -c \'export NVM_DIR="' + nvmDir + '" && [ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh" && nvm which 22\'',
        { encoding: "utf8", stdio: ["pipe", "pipe", "pipe"], timeout: 10000 }
      ).trim();
      if (result && fs.existsSync(result)) return result;
    } catch (e) {}
    return null;
  }

  // Find the oa launcher script under a given node prefix
  function findOaBin(nodeBin) {
    // nodeBin = .../bin/node → prefix = .../
    var binDir = path.dirname(nodeBin);
    var candidates = [
      path.join(binDir, "oa"),
      path.join(binDir, "open-agents"),
    ];
    for (var i = 0; i < candidates.length; i++) {
      try { fs.statSync(candidates[i]); return candidates[i]; } catch (e) {}
    }
    return null;
  }

  function doReinstall() {
    ask("  Reinstall open-agents under Node 22? [Y/n] ", function(a) {
      if (a === "n" || a === "no") {
        console.log(
          "\n  Open a new terminal, then run:\n" +
          "    npm i -g open-agents-ai\n"
        );
        rl.close();
        process.exit(0);
        return;
      }
      console.log("");
      var shell = process.env.SHELL || "/bin/bash";
      var cmd = 'export OA_SELF_UPGRADE=1 && export NVM_DIR="' + nvmDir + '" && ' +
        '[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh" && ' +
        "nvm use 22 && npm i -g open-agents-ai";
      var ok = run(shell + " -c '" + cmd + "'");
      if (!ok) {
        console.log(
          "\n  Reinstall failed. Open a new terminal, then run:\n" +
          "    npm i -g open-agents-ai\n"
        );
        rl.close();
        process.exit(1);
        return;
      }
      console.log("");
      rl.close();

      // Try to auto-relaunch under Node 22 — no new terminal needed
      var node22 = findNode22Bin();
      if (node22) {
        var oaBin = findOaBin(node22);
        if (oaBin) {
          console.log("  ┌─────────────────────────────────────────────────┐");
          console.log("  │  Done! Launching open-agents with Node 22...    │");
          console.log("  └─────────────────────────────────────────────────┘");
          console.log("");
          // Replace this process with Node 22 running the new oa launcher
          try {
            childProcess.execFileSync(node22, [oaBin], {
              stdio: "inherit",
              env: process.env,
            });
          } catch (e) {
            // execFileSync throws when the child exits — that's normal
          }
          process.exit(0);
        }
      }

      // Fallback if we can't find Node 22 binary or oa script
      console.log("  ┌─────────────────────────────────────────────────┐");
      console.log("  │  Done! Open a new terminal and run: oa          │");
      console.log("  └─────────────────────────────────────────────────┘");
      console.log("");
      process.exit(0);
    });
  }

  // Walk through the steps
  ask("  Upgrade to Node.js 22 now? [Y/n] ", function(a) {
    if (a === "n" || a === "no") {
      bail(
        "\n  To install manually:\n" +
        "    curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash\n" +
          "    # or: wget -qO- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash\n" +
        "    source ~/.bashrc\n" +
        "    nvm install 22\n" +
        "    nvm alias default 22\n" +
        "    npm i -g open-agents-ai\n"
      );
      return;
    }
    console.log("");
    // Ensure curl or wget is available first
    ensureDownloader(function(dl) {
      doInstallNvm(dl, function() {
        doInstallNode(function() {
          doReinstall();
        });
      });
    });
  });
} else {
  // Node >= 22 — check sub-dependencies before loading the ESM bundle
  var _path = require("path");
  var _fs = require("fs");
  var _cp = require("child_process");

  // Resolve the package root (where package.json lives)
  var _pkgDir = _path.dirname(__dirname.endsWith("dist") ? _path.dirname(__dirname) : __dirname);
  // If we're in .../lib/node_modules/open-agents-ai/dist/, go up to the package root
  var _pkgJson = _path.join(_pkgDir, "package.json");
  if (!_fs.existsSync(_pkgJson)) {
    _pkgDir = _path.resolve(__dirname, "..");
    _pkgJson = _path.join(_pkgDir, "package.json");
  }

  // Dependencies to verify — both required and optional
  var _criticalDeps = ["better-sqlite3", "ws", "zod", "glob", "ignore"];
  var _optionalDeps = ["open-agents-nexus", "nats.ws", "moondream", "aiwg"];
  var _missing = [];
  var _missingOptional = [];

  function _canResolve(name) {
    try {
      // Check if the module exists in the package's node_modules
      var modDir = _path.join(_pkgDir, "node_modules", name);
      if (_fs.existsSync(modDir)) return true;
      // Also try require.resolve from the package dir
      require.resolve(name, { paths: [_pkgDir] });
      return true;
    } catch (e) {
      return false;
    }
  }

  for (var _i = 0; _i < _criticalDeps.length; _i++) {
    if (!_canResolve(_criticalDeps[_i])) _missing.push(_criticalDeps[_i]);
  }
  for (var _j = 0; _j < _optionalDeps.length; _j++) {
    if (!_canResolve(_optionalDeps[_j])) _missingOptional.push(_optionalDeps[_j]);
  }

  var _allMissing = _missing.concat(_missingOptional);

  if (_allMissing.length > 0) {
    var _label = _missing.length > 0 ? "required" : "optional";
    console.log("  Installing " + _allMissing.length + " missing " + _label + " sub-dependencies...");
    console.log("  Missing: " + _allMissing.join(", "));
    console.log("");

    // Run npm install in the package directory to restore all deps
    try {
      _cp.execSync("npm install --no-audit --no-fund", {
        cwd: _pkgDir,
        stdio: ["pipe", "pipe", "pipe"],
        timeout: 120000,
        env: Object.assign({}, process.env, { OA_SELF_UPGRADE: "1" }),
      });
      console.log("  Sub-dependencies installed.");
      console.log("");
    } catch (e) {
      // If full install fails, try installing each missing dep individually
      for (var _k = 0; _k < _allMissing.length; _k++) {
        var _dep = _allMissing[_k];
        try {
          var _isOpt = _missingOptional.indexOf(_dep) !== -1;
          console.log("  Installing " + _dep + (_isOpt ? " (optional)" : "") + "...");
          _cp.execSync("npm install " + _dep + " --no-audit --no-fund" + (_isOpt ? " --save-optional" : ""), {
            cwd: _pkgDir,
            stdio: ["pipe", "pipe", "pipe"],
            timeout: 60000,
          });
        } catch (e2) {
          if (_missing.indexOf(_dep) !== -1) {
            console.error("  WARNING: Failed to install required dep: " + _dep);
          }
          // Optional deps failing is fine
        }
      }
    }
  }

  // Restart-aware launcher loop.
  // Exit code 120 = restart requested (from /update).
  // The launcher spawns the ESM bundle as a child process. If it exits with
  // code 120, the launcher resets the terminal and spawns again. This way the
  // restart happens in a CLEAN new Node process with zero inherited state —
  // no mouse tracking, no raw mode, no buffered stdin, no timers.
  var cp = require("child_process");
  var path = require("path");

  function runOA() {
    var bundlePath = path.join(__dirname, "index.js");
    var child = cp.fork(bundlePath, process.argv.slice(2), {
      stdio: "inherit",
      env: process.env,
    });

    child.on("exit", function(code) {
      if (code === 120) {
        // Restart requested — reset terminal then spawn again
        if (process.stdout.isTTY) {
          process.stdout.write(
            "\x1B[?1000l\x1B[?1002l\x1B[?1003l\x1B[?1006l\x1B[?1015l" +
            "\x1B[0m\x1B[r\x1B[?25h\x1B[?1049l\x1B[2J\x1B[3J\x1B[H"
          );
        }
        // Reset terminal line discipline
        try { require("child_process").execSync("stty sane 2>/dev/null", { stdio: "pipe" }); } catch(e) {}
        // Drain any buffered stdin
        if (process.stdin.isTTY && typeof process.stdin.setRawMode === "function") {
          try { process.stdin.setRawMode(false); } catch(e) {}
        }
        // Set __OA_RESUMED for the new instance
        process.env.__OA_RESUMED = "update-only";
        // Brief pause then restart
        setTimeout(runOA, 500);
      } else {
        process.exit(code || 0);
      }
    });

    child.on("error", function(err) {
      console.error("Failed to start open-agents:", err.message || err);
      process.exit(1);
    });
  }

  runOA();
}
