#!/usr/bin/env bash
# agent-heartbeat.sh — 后台心跳守护
# 由 agent-claim.sh fork 启动，lock 文件被删则自动退出。
set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck source=../lib/agent-pool-lib.sh
source "${SCRIPT_DIR}/../lib/agent-pool-lib.sh"

SLOT="${1:?usage: agent-heartbeat.sh <slot-num>}"
LOCK_FILE="$(ap_lock_file "${SLOT}")"
INTERVAL="${AP_HEARTBEAT_INTERVAL:-30}"
# 检查 agent_ppid 活性的频率（次/心跳）。每 N 个心跳检查一次。
# 默认 1（每次都查）——kill -0 是 syscall 级别开销，可以忽略。
AP_PPID_CHECK_EVERY="${AP_PPID_CHECK_EVERY:-1}"

# 把自己 PID 写进 lock，便于 release/sweep 杀
ap_update_lock_field "${SLOT}" heartbeat_pid "$$"

tick=0
while [[ -f "${LOCK_FILE}" ]]; do
  ap_update_lock_field "${SLOT}" heartbeat_at "$(ap_now_iso)" 2>/dev/null || break

  # 自检：父 agent 死了就触发分级释放然后自杀。
  # 跳过 abandoned slot——已经被标记了，没必要每 30s 重判一次。
  if (( tick % AP_PPID_CHECK_EVERY == 0 )); then
    cur_state="$(ap_read_lock "${SLOT}" state 2>/dev/null || true)"
    if [[ "${cur_state}" != "abandoned" ]] && ! ap_agent_alive "${SLOT}"; then
      # agent_ppid 字段缺失或主进程已死。
      # 仅当字段存在才触发释放——避免老 lock（无 agent_ppid）被误杀。
      if [[ -n "$(ap_read_lock "${SLOT}" agent_ppid || true)" ]]; then
        # 调 release：clean → 真释放回 free；dirty/unpushed → 转 abandoned；
        # 任一路径 release 都会 kill 自己，跳出循环让脚本退出。
        SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
        bash "${SCRIPT_DIR}/agent-release.sh" "${SLOT}" >/dev/null 2>&1 || true
        break
      fi
    fi
  fi
  tick=$((tick + 1))

  # 同步 task_branch：slot 内切分支后让 lock + .code-workspace 自动跟上。
  # 失败容忍（git 偶发抢锁 / stash 期间 HEAD 短暂 detached 等忽略）。
  worktree_dir="$(ap_read_lock "${SLOT}" worktree_path 2>/dev/null || true)"
  if [[ -n "${worktree_dir}" && -e "${worktree_dir}/.git" ]]; then
    actual_branch="$(git -C "${worktree_dir}" rev-parse --abbrev-ref HEAD 2>/dev/null || true)"
    locked_branch="$(ap_read_lock "${SLOT}" task_branch 2>/dev/null || true)"
    if [[ -n "${actual_branch}" && "${actual_branch}" != "HEAD" && "${actual_branch}" != "${locked_branch}" ]]; then
      ap_update_lock_field "${SLOT}" task_branch "${actual_branch}" 2>/dev/null || true
      ap_write_code_workspace 2>/dev/null || true
    fi
  fi

  sleep "${INTERVAL}"
done
