// Plugin Isolation script supports OpenClaw repository automation.
import { shellQuote } from "./host-command.ts";
import { providerIdFromModelId } from "./provider-auth.ts";

interface PluginIsolationOptions {
  fallbackPluginId: string;
  homeFallback?: string;
  modelId: string;
  nodeCommand?: string;
}

export function posixCodexPlatformPackageRepairFunction(): string {
  return `repair_missing_codex_platform_package() {
  output_file="$1"
  grep -F 'Missing optional dependency @openai/codex-' "$output_file" >/dev/null 2>&1 || return 1
  state_home="\${OPENCLAW_PARALLELS_HOME:-\${HOME:-}}"
  codex_manifest=""
  for candidate in "$state_home"/.openclaw/npm/projects/*/node_modules/@openclaw/codex/package.json; do
    [ -f "$candidate" ] || continue
    codex_manifest="$candidate"
    break
  done
  if [ -z "$codex_manifest" ]; then
    echo "codex-platform-repair: managed Codex project not found" >&2
    return 1
  fi
  project_root="\${codex_manifest%/node_modules/@openclaw/codex/package.json}"
  cache_dir="$(mktemp -d "\${TMPDIR:-/tmp}/openclaw-npm-cache.XXXXXX")"
  echo "codex-platform-repair: retrying managed npm install once with a fresh cache" >&2
  repair_rc=0
  (
    cd "$project_root"
    NPM_CONFIG_CACHE="$cache_dir" npm_config_cache="$cache_dir" npm install --omit=dev --omit=peer --legacy-peer-deps --ignore-scripts --no-audit --no-fund
  ) || repair_rc=$?
  rm -rf "$cache_dir"
  if [ "$repair_rc" -ne 0 ]; then
    echo "codex-platform-repair: npm install failed with exit code $repair_rc" >&2
    return "$repair_rc"
  fi
  echo "codex-platform-repair: managed npm install completed" >&2
}`;
}

export function windowsCodexPlatformPackageRepairFunction(): string {
  return String.raw`function Repair-MissingCodexPlatformPackage {
  param([object[]] $Output)
  $outputText = $Output | Out-String
  if ($outputText -notmatch [regex]::Escape('Missing optional dependency @openai/codex-')) {
    return $false
  }
  $projectsRoot = Join-Path $env:USERPROFILE '.openclaw\npm\projects'
  $codexManifest = Get-ChildItem -Path $projectsRoot -Filter package.json -File -Recurse -ErrorAction SilentlyContinue |
    Where-Object { $_.FullName -match 'node_modules[\\/]@openclaw[\\/]codex[\\/]package\.json$' } |
    Select-Object -First 1
  if (-not $codexManifest) {
    Write-Warning 'codex-platform-repair: managed Codex project not found'
    return $false
  }
  $projectRoot = $codexManifest.Directory.Parent.Parent.Parent.FullName
  $cacheDir = Join-Path ([System.IO.Path]::GetTempPath()) ('openclaw-npm-cache-' + [guid]::NewGuid().ToString('N'))
  $oldUpperCache = [Environment]::GetEnvironmentVariable('NPM_CONFIG_CACHE', 'Process')
  $oldLowerCache = [Environment]::GetEnvironmentVariable('npm_config_cache', 'Process')
  $pushedLocation = $false
  $repairExit = 1
  try {
    New-Item -ItemType Directory -Path $cacheDir -Force | Out-Null
    [Environment]::SetEnvironmentVariable('NPM_CONFIG_CACHE', $cacheDir, 'Process')
    [Environment]::SetEnvironmentVariable('npm_config_cache', $cacheDir, 'Process')
    Push-Location $projectRoot
    $pushedLocation = $true
    Write-Host 'codex-platform-repair: retrying managed npm install once with a fresh cache'
    $repairOutput = & npm.cmd install --omit=dev --omit=peer --legacy-peer-deps --ignore-scripts --no-audit --no-fund 2>&1
    $repairExit = $LASTEXITCODE
    if ($null -ne $repairOutput) { $repairOutput | ForEach-Object { Write-Host $_ } }
  } finally {
    if ($pushedLocation) { Pop-Location }
    [Environment]::SetEnvironmentVariable('NPM_CONFIG_CACHE', $oldUpperCache, 'Process')
    [Environment]::SetEnvironmentVariable('npm_config_cache', $oldLowerCache, 'Process')
    Remove-Item $cacheDir -Force -Recurse -ErrorAction SilentlyContinue
  }
  if ($repairExit -ne 0) {
    Write-Warning "codex-platform-repair: npm install failed with exit code $repairExit"
    return $false
  }
  Write-Host 'codex-platform-repair: managed npm install completed'
  return $true
}`;
}

export function providerOnlyPluginId(modelId: string, fallbackPluginId: string): string {
  return providerIdFromModelId(modelId) || fallbackPluginId;
}

export function posixProviderOnlyPluginIsolationScript(options: PluginIsolationOptions): string {
  const nodeCommand = shellQuote(options.nodeCommand ?? "node");
  const homeEnv = options.homeFallback
    ? `OPENCLAW_PARALLELS_HOME=${shellQuote(options.homeFallback)} `
    : "";
  return `/usr/bin/env ${homeEnv}${nodeCommand} - <<'JS'
${providerOnlyPluginIsolationNodeScript(options)}
JS`;
}

export function windowsProviderOnlyPluginIsolationScript(options: PluginIsolationOptions): string {
  const payloadJson = JSON.stringify({
    modelId: options.modelId,
    pluginId: providerOnlyPluginId(options.modelId, options.fallbackPluginId),
  });
  return `$env:OPENCLAW_PARALLELS_PLUGIN_ISOLATION = @'
${payloadJson}
'@
$isolationScriptPath = Join-Path ([System.IO.Path]::GetTempPath()) ('openclaw-parallels-plugin-isolation-' + [guid]::NewGuid().ToString('N') + '.cjs')
try {
@'
${providerOnlyPluginIsolationNodeSource()}
'@ | Set-Content -Path $isolationScriptPath -Encoding UTF8
node.exe $isolationScriptPath
if ($LASTEXITCODE -ne 0) { throw "plugin isolation failed with exit code $LASTEXITCODE" }
} finally {
  Remove-Item $isolationScriptPath -Force -ErrorAction SilentlyContinue
  Remove-Item Env:OPENCLAW_PARALLELS_PLUGIN_ISOLATION -Force -ErrorAction SilentlyContinue
}`;
}

function providerOnlyPluginIsolationNodeScript(options: PluginIsolationOptions): string {
  const payloadJson = JSON.stringify({
    homeFallback: options.homeFallback,
    modelId: options.modelId,
    pluginId: providerOnlyPluginId(options.modelId, options.fallbackPluginId),
  });
  return `process.env.OPENCLAW_PARALLELS_PLUGIN_ISOLATION = ${JSON.stringify(payloadJson)};
${providerOnlyPluginIsolationNodeSource()}`;
}

function providerOnlyPluginIsolationNodeSource(): string {
  return String.raw`const fs = require("node:fs");
const path = require("node:path");

const payload = JSON.parse(process.env.OPENCLAW_PARALLELS_PLUGIN_ISOLATION || "{}");
const home =
  process.env.OPENCLAW_PARALLELS_HOME ||
  payload.homeFallback ||
  process.env.HOME ||
  process.env.USERPROFILE ||
  "/root";
const configPath = path.join(home, ".openclaw", "openclaw.json");
const stateDir = path.dirname(configPath);
const modelId = String(payload.modelId || "");
const allowedPluginId = String(payload.pluginId || "").trim();
if (!allowedPluginId || !modelId) {
  throw new Error("missing plugin isolation payload");
}

const readConfig = () => {
  if (!fs.existsSync(configPath)) {
    return {};
  }
  return JSON.parse(fs.readFileSync(configPath, "utf8"));
};
const objectRecord = (value) =>
  value && typeof value === "object" && !Array.isArray(value) ? value : {};

const config = readConfig();
config.plugins = objectRecord(config.plugins);
config.plugins.entries = { [allowedPluginId]: { enabled: true } };
config.plugins.allow = [allowedPluginId];

config.agents = objectRecord(config.agents);
config.agents.defaults = objectRecord(config.agents.defaults);
config.agents.defaults.model = {
  ...objectRecord(config.agents.defaults.model),
  primary: modelId,
};
config.agents.defaults.models = objectRecord(config.agents.defaults.models);
const selectedModelEntry = config.agents.defaults.models[modelId];
if (selectedModelEntry && typeof selectedModelEntry === "object" && !Array.isArray(selectedModelEntry)) {
  delete selectedModelEntry.agentRuntime;
}

const providerId = modelId.split("/", 1)[0] || "";
const providerModelId = modelId.slice(providerId.length + 1);
const providers = objectRecord(objectRecord(config.models).providers);
const providerEntry = providers[providerId];
if (providerEntry && typeof providerEntry === "object" && !Array.isArray(providerEntry)) {
  delete providerEntry.agentRuntime;
  if (Array.isArray(providerEntry.models)) {
    for (const model of providerEntry.models) {
      if (
        model &&
        typeof model === "object" &&
        (model.id === providerModelId ||
          model.id === modelId ||
          model.name === providerModelId ||
          model.name === modelId)
      ) {
        delete model.agentRuntime;
      }
    }
  }
}

fs.rmSync(path.join(stateDir, "npm", "node_modules", "@openclaw", "codex"), {
  recursive: true,
  force: true,
});
fs.mkdirSync(stateDir, { recursive: true });
fs.writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");`;
}
