'use strict';
// Devcontainer launch wrapper.
//
// When a CTM session is flagged as devcontainer-backed, we don't spawn
// `claude` (or whatever) directly in the current PTY — we wrap it with
// two commands:
//
//   1. `devcontainer up --workspace-folder <cwd>`    (one-shot, host-side)
//   2. `devcontainer exec --workspace-folder <cwd> -- <cmd> <...args>`  (PTY'd)
//
// The original cwd stays the host path (so project-config and git ops work
// on the host) while the actual agent runs inside the container. Approval
// prompts still flow through the host PTY since the container process's
// stdout streams back through `devcontainer exec`.
//
// This file produces the spawn spec. The caller is responsible for:
//   - running the `up` command (pre-spawn, one-shot)
//   - spawning the `exec` command via node-pty
//
// Off by default — gate behind a per-session flag (msg.devcontainer) or
// per-project setting (.ctm.json devcontainer: { up: "...", exec: "..." }).

const DEFAULT_DEVC_BIN = 'devcontainer';

function _argsStr(args) {
  return (args || []).map(a => String(a));
}

// Build the one-shot `devcontainer up` command. Returns the spec the caller
// would pass to execFile/spawn:
//   { cmd, args, description }
function buildUpCommand({ devcBin, cwd, extraArgs } = {}) {
  const bin = devcBin || DEFAULT_DEVC_BIN;
  const args = ['up', '--workspace-folder', cwd || process.cwd()];
  if (Array.isArray(extraArgs)) args.push(..._argsStr(extraArgs));
  return {
    cmd: bin,
    args,
    description: `devcontainer up (${cwd || process.cwd()})`,
  };
}

// Build the PTY-backed `devcontainer exec -- <cmd> <...args>` spec.
// innerCmd/innerArgs are the command that would have been spawned directly
// on the host if devcontainer weren't in the loop.
function buildExecCommand({ devcBin, cwd, innerCmd, innerArgs, extraArgs } = {}) {
  const bin = devcBin || DEFAULT_DEVC_BIN;
  if (!innerCmd) throw new Error('devcontainer-wrapper: innerCmd is required');
  const args = ['exec', '--workspace-folder', cwd || process.cwd()];
  if (Array.isArray(extraArgs)) args.push(..._argsStr(extraArgs));
  args.push('--', innerCmd, ..._argsStr(innerArgs));
  return {
    cmd: bin,
    args,
    description: `devcontainer exec — ${innerCmd} ${innerArgs ? innerArgs.join(' ') : ''}`,
  };
}

// Decide whether to devcontainer-wrap a given session spec.
// Precedence (most specific wins):
//   - msg.devcontainer === false  → never wrap
//   - msg.devcontainer === true   → always wrap (use cwd as workspace folder)
//   - msg.devcontainer === {...}  → wrap with supplied config
//   - project .ctm.json { devcontainer: {...} } → wrap
//   - no devcontainer.json in cwd → don't wrap
function shouldWrap({ msg = {}, projectConfig, cwdHasDevcontainer }) {
  if (msg.devcontainer === false) return false;
  if (msg.devcontainer === true) return true;
  if (msg.devcontainer && typeof msg.devcontainer === 'object') return true;
  if (projectConfig && projectConfig.devcontainer && typeof projectConfig.devcontainer === 'object') return true;
  // Fall back to presence of a devcontainer.json in the workspace.
  return !!cwdHasDevcontainer;
}

// Utility: merge msg + project settings into a single resolved config.
function resolveDevcontainerConfig({ msg = {}, projectConfig = {} }) {
  const m = (typeof msg.devcontainer === 'object' && msg.devcontainer) ? msg.devcontainer : {};
  const p = (projectConfig && projectConfig.devcontainer && typeof projectConfig.devcontainer === 'object')
    ? projectConfig.devcontainer : {};
  return {
    devcBin: m.bin || p.bin || DEFAULT_DEVC_BIN,
    upArgs: Array.isArray(m.upArgs) ? m.upArgs : (Array.isArray(p.upArgs) ? p.upArgs : []),
    execArgs: Array.isArray(m.execArgs) ? m.execArgs : (Array.isArray(p.execArgs) ? p.execArgs : []),
  };
}

module.exports = {
  DEFAULT_DEVC_BIN,
  buildUpCommand,
  buildExecCommand,
  shouldWrap,
  resolveDevcontainerConfig,
};
