'use strict';
/**
 * Sandbox registry — stub.
 *
 * Borrowed shape from OpenClaw's sandbox backend registry
 * (src/agents/sandbox/backend.ts):
 *   - Map<id, RegisteredSandboxBackend> keyed by backend id
 *   - registerSandbox(id, registration) returns deregister closure
 *   - requireSandbox throws with remediation hint
 *
 * Wall-E's adaptation is INTENTIONALLY a stub. OpenClaw is a multi-tenant
 * agent platform where untrusted plugin code may run; sandbox isolation
 * (Docker, Podman, SSH-tunnel, noVNC browser) is core to the threat model.
 *
 * Wall-E is a personal-daemon model: tools run on the owner's machine
 * with the owner's credentials, on the owner's data. The threat model
 * doesn't justify the cost of full container plumbing (~40h LOC). This
 * file lands the SHAPE — registry + default 'inprocess' backend that is
 * a pure pass-through descriptor (no actual execution, no shell). When/if
 * Wall-E becomes multi-tenant, plugins can register a real Docker backend
 * without touching skill-executor.
 *
 * Today: tools continue to execute through wall-e/skills/tool-executor.js
 * directly. The 'inprocess' backend here is a registry placeholder so
 * tool dispatch can be sandbox-aware in the future without an interface
 * break.
 *
 * Item E of skill-dispatch deep-dive (stub).
 */

const _backends = new Map();

function registerSandbox(id, registration) {
  if (!id || typeof id !== 'string') {
    throw new Error('registerSandbox requires a non-empty string id');
  }
  if (!registration || typeof registration !== 'object') {
    throw new Error(`registerSandbox("${id}") requires an object`);
  }
  if (typeof registration.exec !== 'function') {
    throw new Error(`registerSandbox("${id}") requires an exec(spec) function`);
  }
  const previous = _backends.get(id);
  _backends.set(id, registration);
  return () => {
    if (previous) _backends.set(id, previous);
    else _backends.delete(id);
  };
}

function getSandbox(id) {
  return _backends.get(id) || null;
}

function requireSandbox(id) {
  const backend = _backends.get(id);
  if (backend) return backend;
  throw new Error(
    `Sandbox backend "${id}" is not registered.\n`
    + `  - Default 'inprocess' is registered automatically (pass-through stub).\n`
    + `  - To add Docker/Podman/SSH isolation, register a backend via\n`
    + `    registerSandbox(id, { exec: async (spec) => result }).`
  );
}

function listSandboxes() {
  return Array.from(_backends.keys()).sort();
}

function clearSandboxRegistry() {
  _backends.clear();
}

// ── Bundled default: 'inprocess' (descriptor stub, NOT a shell wrapper) ──
//
// This is intentionally NOT a working exec implementation. It's a
// declarative descriptor: "tools dispatch through this id" + a no-op
// exec that throws if anyone tries to call it directly. Tool execution
// today goes through wall-e/skills/tool-executor.js, which already
// handles MCP / brain / Slack / Claude-code-reader tools.
//
// Future plugins (e.g. registerSandbox('docker', { exec: docker-run })) will
// implement real isolation. The shape ensures they slot in without
// changing tool-executor's call sites.
registerSandbox('inprocess', {
  id: 'inprocess',
  label: 'In-process (no isolation, descriptor stub)',
  isolated: false,
  exec: async () => {
    throw new Error(
      'inprocess.exec is a stub. Use wall-e/skills/tool-executor.js for '
      + 'actual tool dispatch. This sandbox descriptor exists to declare '
      + 'the registry shape for future Docker/Podman/SSH plugins.'
    );
  },
});

module.exports = {
  registerSandbox,
  getSandbox,
  requireSandbox,
  listSandboxes,
  clearSandboxRegistry,
};
