#!/usr/bin/env bash
# /root/bin/opc-sync.sh
# 监听所有 agent 工作目录，自动同步新增输出文件到 /root/hb-opc/<agent>/
# 并自动 git commit + push

set -uo pipefail

OUT_DIR="/root/hb-opc"
LOG_FILE="/var/log/opc-sync.log"
PENDING_FILE="/tmp/opc-sync-pending.txt"
COMMIT_INTERVAL=30   # 秒：积攒多少秒再一次性 commit
GIT_USER_NAME="opc-sync"
GIT_USER_EMAIL="sync@opc"

# ── 系统配置文件，永不同步 ─────────────────────────────────────
EXCLUDED_NAMES=(
  TOOLS.md SOUL.md IDENTITY.md AGENTS.md HEARTBEAT.md
  MEMORY.md MEMORY_SYSTEM.md CLAUDE.md BOOTSTRAP.md
  README.md USER.md SKILL.md SKILLS.md OPC-AGENTS.md
)

# 路径中包含以下片段的，跳过
EXCLUDED_PATH_PARTS=(
  /memory/ /skills/ /sessions/ /.__pycache__ /.git/
  /.openclaw/agents/ /node_modules/
)

# ── 运维/测试文件名模式，永不同步（glob 匹配文件名）──────────
EXCLUDED_PATTERNS=(
  "*autotest*"          # 自动化测试文件
  "opc-sync-test*"      # opc-sync 测试文件
  "opc_sync_test*"
  "opc_sync_autotest*"
  "*__test__*"          # 带 __test__ 标记的文件
)

# ── 同步的文件扩展名（白名单，宽松版）──────────────────────────
SYNC_EXTS_RE='\.(md|pdf|txt|csv|pptx|docx|xlsx|xls|html|htm|png|jpg|jpeg|gif|webp|svg|epub|mp4|mp3|zip|json)$'

# ── 日志 ─────────────────────────────────────────────────────
log() {
  echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" | tee -a "$LOG_FILE"
}

# ── 判断文件名是否在排除列表 ──────────────────────────────────
is_excluded_name() {
  local fname="$1"
  for ex in "${EXCLUDED_NAMES[@]}"; do
    [[ "$fname" == "$ex" ]] && return 0
  done
  for pat in "${EXCLUDED_PATTERNS[@]}"; do
    [[ "$fname" == $pat ]] && return 0
  done
  return 1
}

# ── 判断路径是否命中排除片段 ─────────────────────────────────
is_excluded_path() {
  local fullpath="$1"
  for part in "${EXCLUDED_PATH_PARTS[@]}"; do
    [[ "$fullpath" == *"$part"* ]] && return 0
  done
  [[ "$(basename "$fullpath")" == .* ]] && return 0
  return 1
}

# ── workspace dir → agent_id ──────────────────────────────────
get_agent_id() {
  local dir="$1"
  if [[ "$dir" =~ /workspace-([^/]+)(/|$) ]]; then
    echo "${BASH_REMATCH[1]}"; return
  fi
  if [[ "$dir" =~ /workspace(/|$) ]]; then
    echo "main"; return
  fi
  if [[ "$dir" =~ ^/root/([^/]+)(/|$) ]]; then
    echo "${BASH_REMATCH[1]}"; return
  fi
  echo ""
}

# ── 处理一个文件事件 ─────────────────────────────────────────
handle_file() {
  local event_dir="$1"
  local filename="$2"
  local fullpath="${event_dir}${filename}"

  echo "$filename" | grep -qiE "$SYNC_EXTS_RE" || return 0
  is_excluded_name "$filename" && return 0
  is_excluded_path "$fullpath" && return 0
  [ -f "$fullpath" ] || return 0

  local agent_id
  agent_id=$(get_agent_id "$event_dir")
  [ -z "$agent_id" ] && return 0

  local dest_dir="$OUT_DIR/$agent_id"
  mkdir -p "$dest_dir"

  local dest_file="$dest_dir/$filename"
  if [ -f "$dest_file" ] && cmp -s "$fullpath" "$dest_file"; then
    return 0
  fi

  cp -p "$fullpath" "$dest_file" 2>/dev/null || {
    log "ERROR: copy failed $fullpath → $dest_file"
    return 0
  }

  log "SYNC  $agent_id/$filename  ←  $fullpath"
  echo "$dest_file" >> "$PENDING_FILE"
}

# ── Git commit + push 后台循环 ────────────────────────────────
git_commit_loop() {
  while true; do
    sleep "$COMMIT_INTERVAL"

    cd "$OUT_DIR" || continue

    # 铲平：把 agent 子目录里的文件全部移到一级目录
    for agent_dir in */; do
      agent_dir="${agent_dir%/}"
      [ -d "$agent_dir" ] || continue
      find "$agent_dir" -mindepth 2 -type f ! -path ".git/*" 2>/dev/null | while read -r f; do
        fname=$(basename "$f")
        dest="$agent_dir/$fname"
        if [ ! -f "$dest" ]; then
          mv "$f" "$dest" && log "FLATTEN $f → $agent_dir/$fname"
        else
          rm -f "$f"
        fi
      done
      find "$agent_dir" -mindepth 1 -type d -empty -delete 2>/dev/null || true
    done

    # git add -A：兜底抓所有新增/修改（含 agent 直接写入 hb-opc 的文件）
    git add -A 2>/dev/null || true

    # 检查是否有东西要提交
    git diff --cached --quiet 2>/dev/null && {
      > "$PENDING_FILE"
      continue
    }

    # 读 pending 列表生成 commit message
    local files=()
    [ -s "$PENDING_FILE" ] && mapfile -t files < "$PENDING_FILE"
    > "$PENDING_FILE"

    local msg
    if [ ${#files[@]} -eq 1 ]; then
      msg="sync: ${files[0]#$OUT_DIR/}"
    elif [ ${#files[@]} -gt 1 ]; then
      msg="sync: ${#files[@]} files from agents"
    else
      # 直接写入 hb-opc 的文件（不经 opc-sync 中转）
      local changed
      changed=$(git diff --cached --name-only 2>/dev/null | head -3 | tr '\n' ' ')
      msg="sync: $changed"
    fi

    GIT_AUTHOR_NAME="$GIT_USER_NAME" \
    GIT_AUTHOR_EMAIL="$GIT_USER_EMAIL" \
    GIT_COMMITTER_NAME="$GIT_USER_NAME" \
    GIT_COMMITTER_EMAIL="$GIT_USER_EMAIL" \
    git commit -m "$msg" 2>&1 | tee -a "$LOG_FILE" || { continue; }

    log "COMMIT done"

    # push 前先 rebase 远端，避免远端 divergence 导致 push 一直被拒、提交积压
    git pull --rebase >> "$LOG_FILE" 2>&1
    if [ $? -ne 0 ]; then
      log "PULL --rebase failed; abort rebase to keep repo clean, retry next cycle"
      git rebase --abort 2>/dev/null || true
      continue
    fi

    # 自动 push
    git push 2>&1 | tee -a "$LOG_FILE" && log "PUSH done" || log "PUSH failed, will retry next cycle"
  done
}

# ── 构建监听目录列表 ─────────────────────────────────────────
build_watch_dirs() {
  local dirs=()
  while IFS= read -r d; do
    dirs+=("$d")
  done < <(find /root/.openclaw -maxdepth 1 -name "workspace*" -type d 2>/dev/null | sort)

  for d in /root/xiao* /root/mini*; do
    [ -d "$d" ] || continue
    [[ "$d" == "$OUT_DIR" ]] && continue
    dirs+=("$d")
  done

  printf '%s\n' "${dirs[@]}"
}

# ── 主入口 ───────────────────────────────────────────────────
main() {
  log "=== opc-sync starting ==="
  > "$PENDING_FILE"

  mapfile -t WATCH_DIRS < <(build_watch_dirs)
  log "Watching ${#WATCH_DIRS[@]} dirs"
  for d in "${WATCH_DIRS[@]}"; do log "  $d"; done

  git_commit_loop &
  local commit_pid=$!
  trap "kill $commit_pid 2>/dev/null; exit 0" SIGTERM SIGINT

  inotifywait -m -r -e close_write -e moved_to \
    --format '%w %f' \
    "${WATCH_DIRS[@]}" 2>/dev/null \
  | while IFS=' ' read -r event_dir filename; do
      handle_file "$event_dir" "$filename"
    done

  wait "$commit_pid"
}

main
