#!/usr/bin/env bash
set -euo pipefail

ROOT="${PIPO_ROOT:-$HOME/.openclaw/workspace}"
STACK_DIR="$ROOT/ops/docker-cli-stack"
STATE_DIR="$ROOT/.taskruns"
JOBS_DIR="$STATE_DIR/jobs"
LOGS_DIR="$STATE_DIR/logs"
HOOK="${PIPO_HOOK:-$ROOT/ops/cli-jobs/hooks/on_task_done.sh}"

usage() {
  cat <<'EOF'
Usage:
  jobctl.sh run <gemini|codex|codex-safe> "prompt..."
  jobctl.sh status <job_id>
  jobctl.sh tail <job_id> [lines]
  jobctl.sh list [limit]
  jobctl.sh wait <job_id>

Notes:
  - codex      => network-enabled (danger-full-access) via wrapper 'cn'
  - codex-safe => read-only/safe via wrapper 'c'
  - gemini     => one-shot via wrapper 'g'
EOF
}

now_iso() {
  date -u +"%Y-%m-%dT%H:%M:%SZ"
}

ensure_dirs() {
  mkdir -p "$JOBS_DIR" "$LOGS_DIR"
}

job_file() {
  local id="$1"
  echo "$JOBS_DIR/$id.json"
}

log_file() {
  local id="$1"
  echo "$LOGS_DIR/$id.log"
}

run_job() {
  local runner="${1:-}"
  shift || true
  local prompt="${*:-}"

  if [[ -z "$runner" || -z "$prompt" ]]; then
    usage
    exit 1
  fi

  local cmd_runner
  case "$runner" in
    gemini) cmd_runner="g" ;;
    codex) cmd_runner="cn" ;;
    codex-safe) cmd_runner="c" ;;
    *)
      echo "Runner inválido: $runner"
      usage
      exit 1
      ;;
  esac

  if [[ ! -d "$STACK_DIR" ]]; then
    echo "No encuentro stack docker en: $STACK_DIR"
    exit 1
  fi

  local -a docker_cmd
  if docker info >/dev/null 2>&1; then
    docker_cmd=(docker)
  elif sudo -n docker info >/dev/null 2>&1; then
    docker_cmd=(sudo docker)
  else
    echo "No tengo acceso a Docker (ni directo ni sudo -n)."
    echo "Tip: re-login SSH para refrescar grupo docker, o usa sudo habilitado."
    exit 1
  fi

  ensure_dirs

  local id
  id="$(date -u +%Y%m%dT%H%M%SZ)-${runner}-${RANDOM}"
  local jf lf start
  jf="$(job_file "$id")"
  lf="$(log_file "$id")"
  start="$(now_iso)"

  jq -n \
    --arg id "$id" \
    --arg runner "$runner" \
    --arg prompt "$prompt" \
    --arg started_at "$start" \
    --arg status "running" \
    --arg log_file "$lf" \
    '{id:$id,runner:$runner,prompt:$prompt,status:$status,started_at:$started_at,ended_at:null,exit_code:null,pid:null,log_file:$log_file}' > "$jf"

  (
    set +e
    cd "$STACK_DIR"
    "${docker_cmd[@]}" compose run --rm cli "$cmd_runner" "$prompt" > "$lf" 2>&1
    local exit_code=$?
    local ended_at
    ended_at="$(now_iso)"

    local tmp_done
    tmp_done="$(mktemp)"
    jq \
      --arg ended_at "$ended_at" \
      --arg status "done" \
      --argjson exit_code "$exit_code" \
      '.ended_at=$ended_at | .status=$status | .exit_code=$exit_code' "$jf" > "$tmp_done" && mv "$tmp_done" "$jf"

    if [[ -x "$HOOK" ]]; then
      "$HOOK" "$id" "$runner" "$exit_code" "$lf" "$jf" >> "$STATE_DIR/hook.log" 2>&1 || true
    fi
  ) &

  local pid=$!
  local tmp
  tmp="$(mktemp)"
  jq --argjson pid "$pid" '.pid=$pid' "$jf" > "$tmp" && mv "$tmp" "$jf"

  echo "$id"
}

status_job() {
  local id="${1:-}"
  [[ -n "$id" ]] || { usage; exit 1; }
  local jf
  jf="$(job_file "$id")"
  [[ -f "$jf" ]] || { echo "Job no encontrado: $id"; exit 1; }
  jq '.' "$jf"
}

tail_job() {
  local id="${1:-}"
  local lines="${2:-80}"
  [[ -n "$id" ]] || { usage; exit 1; }
  local lf
  lf="$(log_file "$id")"
  [[ -f "$lf" ]] || { echo "Log no encontrado: $lf"; exit 1; }
  tail -n "$lines" "$lf"
}

list_jobs() {
  local limit="${1:-20}"
  ensure_dirs
  local files
  files=$(ls -1t "$JOBS_DIR"/*.json 2>/dev/null | head -n "$limit" || true)

  if [[ -z "$files" ]]; then
    echo "Sin jobs todavía."
    return 0
  fi

  while IFS= read -r f; do
    [[ -n "$f" ]] || continue
    jq -r '"\(.id)\t\(.runner)\t\(.status)\texit=\(.exit_code // "-")\t\(.started_at)\t\(.ended_at // "-")"' "$f"
  done <<< "$files"
}

wait_job() {
  local id="${1:-}"
  [[ -n "$id" ]] || { usage; exit 1; }
  local jf
  jf="$(job_file "$id")"
  [[ -f "$jf" ]] || { echo "Job no encontrado: $id"; exit 1; }

  while true; do
    local status
    status="$(jq -r '.status' "$jf")"
    if [[ "$status" != "running" ]]; then
      break
    fi
    sleep 2
  done

  jq '.' "$jf"
}

cmd="${1:-}"
shift || true

case "$cmd" in
  run) run_job "$@" ;;
  status) status_job "$@" ;;
  tail) tail_job "$@" ;;
  list) list_jobs "$@" ;;
  wait) wait_job "$@" ;;
  *)
    usage
    exit 1
    ;;
esac
