import { execFileSync } from 'child_process';
import fs from 'fs';
import os from 'os';
import path from 'path';

import type { RoomRoleContext } from './room-role-context.js';

// Codex app-server does not expose a BashTool-style pre-use hook, so reviewer
// mode can only hard-block mutating git via PATH interception here. Non-git
// shell mutation commands remain a known gap when REVIEWER_AGENT_TYPE=codex.
const BLOCKED_GIT_SUBCOMMANDS = new Set([
  'add',
  'am',
  'apply',
  'branch',
  'checkout',
  'cherry-pick',
  'clean',
  'commit',
  'merge',
  'push',
  'rebase',
  'reset',
  'restore',
  'stash',
  'switch',
  'tag',
  'worktree',
]);

export function isReviewerRuntime(
  roomRoleContext?: RoomRoleContext,
): boolean {
  return roomRoleContext?.role === 'reviewer';
}

function resolveGitBinary(baseEnv: NodeJS.ProcessEnv): string {
  return execFileSync('bash', ['-lc', 'command -v git'], {
    encoding: 'utf-8',
    env: baseEnv,
    stdio: ['ignore', 'pipe', 'pipe'],
  }).trim();
}

export function buildReviewerGitGuardEnv(
  baseEnv: NodeJS.ProcessEnv,
  reviewerRuntime: boolean,
): NodeJS.ProcessEnv {
  if (!reviewerRuntime) {
    return baseEnv;
  }

  const realGitPath = resolveGitBinary(baseEnv);
  const protectedWorkDir = baseEnv.EJCLAW_WORK_DIR || '';
  const wrapperDir = fs.mkdtempSync(
    path.join(os.tmpdir(), 'ejclaw-reviewer-git-'),
  );
  const wrapperPath = path.join(wrapperDir, 'git');
  const blocked = [...BLOCKED_GIT_SUBCOMMANDS]
    .map((value) => `'${value}'`)
    .join(' ');

  const script = `#!/usr/bin/env bash
set -euo pipefail
real_git=${JSON.stringify(realGitPath)}
protected_work_dir=${JSON.stringify(protectedWorkDir)}
blocked_subcommands=(${blocked})
subcmd=""
skip_next=0
target_dir="$(pwd -P)"
capture_next_dir=0
for arg in "$@"; do
  if [[ "$capture_next_dir" == "1" ]]; then
    if [[ "$arg" == /* ]]; then
      target_dir="$arg"
    else
      target_dir="$target_dir/$arg"
    fi
    target_dir="$(cd "$target_dir" 2>/dev/null && pwd -P || printf '%s' "$target_dir")"
    capture_next_dir=0
    continue
  fi
  if [[ "$skip_next" == "1" ]]; then
    skip_next=0
    continue
  fi
  case "$arg" in
    -C)
      capture_next_dir=1
      continue
      ;;
    -C*)
      target_dir="\${arg#-C}"
      target_dir="$(cd "$target_dir" 2>/dev/null && pwd -P || printf '%s' "$target_dir")"
      continue
      ;;
    --work-tree)
      capture_next_dir=1
      continue
      ;;
    --work-tree=*)
      target_dir="\${arg#--work-tree=}"
      target_dir="$(cd "$target_dir" 2>/dev/null && pwd -P || printf '%s' "$target_dir")"
      continue
      ;;
    -c|-C|--git-dir|--work-tree|--namespace|--exec-path|--config-env)
      skip_next=1
      continue
      ;;
    -c*|-C*|--git-dir=*|--work-tree=*|--namespace=*|--exec-path=*|--config-env=*)
      continue
      ;;
    --*)
      continue
      ;;
    -*)
      continue
      ;;
    *)
      subcmd="$arg"
      break
      ;;
  esac
done
is_protected_target=0
if [[ -n "$protected_work_dir" ]]; then
  protected_real="$(cd "$protected_work_dir" 2>/dev/null && pwd -P || printf '%s' "$protected_work_dir")"
  case "$target_dir" in
    "$protected_real"|"$protected_real"/*)
      is_protected_target=1
      ;;
  esac
fi
for blocked in "\${blocked_subcommands[@]}"; do
  if [[ "$is_protected_target" == "1" && "$subcmd" == "$blocked" ]]; then
    echo "EJClaw reviewer runtime blocks mutating git subcommands: $subcmd" >&2
    exit 1
  fi
done
exec "$real_git" "$@"
`;

  fs.writeFileSync(wrapperPath, script, { mode: 0o755 });
  return {
    ...baseEnv,
    EJCLAW_REAL_GIT: realGitPath,
    EJCLAW_PROTECTED_WORK_DIR: protectedWorkDir,
    PATH: `${wrapperDir}:${baseEnv.PATH || ''}`,
  };
}
