#!/usr/bin/env bash
# dream-cycle.sh — 记忆 consolidation 周期（Dream Cycle）
# 
# 灵感: openclaw-auto-dream 的 4 Phase Dream Cycle
# v2 — 加入远程记忆服务器同步
#
# 用法:
#   bash scripts/dream-cycle.sh              # 完整运行
#   bash scripts/dream-cycle.sh collect      # Phase 1: 扫描 daily/
#   bash scripts/dream-cycle.sh sync-remote  # 推送新条目到远程服务器
#   bash scripts/dream-cycle.sh evaluate     # Phase 3: 健康评分
#   bash scripts/dream-cycle.sh health       # 只看健康分

set -euo pipefail

WORKSPACE="$(cd "$(dirname "$0")/.." && pwd)"
MEMORY_DIR="$WORKSPACE/memory"
DAILY_DIR="$MEMORY_DIR/daily"
REGISTERS_DIR="$MEMORY_DIR/registers"
PROJECTS_DIR="$MEMORY_DIR/projects"
MARKER="$MEMORY_DIR/.dream-cycle-last-run"
NOW=$(date +%Y-%m-%d)
NOW_EPOCH=$(date +%s)

# 远程记忆服务器
MEMORY_SERVER="http://111.229.145.18"

# 上次运行时间
LAST_RUN=""
if [ -f "$MARKER" ]; then
  LAST_RUN=$(cat "$MARKER")
fi

# ─── 智能标记检测 ──────────────────────────────────────

# 自动检测重要性（返回类型或空）
detect_importance() {
  local line="$1"
  
  # 决策句式
  if echo "$line" | grep -qE '(选择|决定|配置为|建立|部署|接入|升级|迁移|切换|采用)'; then
    echo "decision"
    return
  fi
  # 偏好表达
  if echo "$line" | grep -qE '(喜欢|习惯|总是|不要|偏好|改为|改成|以后都)'; then
    echo "preference"
    return
  fi
  # 纠正信号
  if echo "$line" | grep -qE '(不对|应该是|更正|其实是|原来是|发现.*错|误)'; then
    echo "correction"
    return
  fi
  # 重要事件
  if echo "$line" | grep -qE '(完成|成功|失败|上线|修复|解决|搞定|落地|生效)'; then
    echo "event"
    return
  fi
  # 工具/服务状态
  if echo "$line" | grep -qE '(状态|正常|异常|在线|离线|连接|断开)'; then
    echo "status"
    return
  fi
  echo ""
}

# ─── Phase 1: Collect — 扫描 daily/ ──────────────────────

collect() {
  echo "📡 Phase 1: Collect — 日志扫描（智能标记）"
  echo "   上次运行: ${LAST_RUN:-从未}"
  echo ""

  local tempfile=$(mktemp)
  local count=0

  for f in "$DAILY_DIR"/*.md; do
    [ -f "$f" ] || continue
    local fname=$(basename "$f")
    local fdate="${fname%.md}"

    # 跳过旧文件
    if [ -n "$LAST_RUN" ] && [ "$fdate" != "$LAST_RUN" ] && [[ "$fdate" < "$LAST_RUN" ]]; then
      continue
    fi

    while IFS= read -r line; do
      local trimmed=$(echo "$line" | sed 's/^\*\*//;s/\*\*$//')
      local type=""
      
      # 1. 显式标记优先
      if echo "$trimmed" | grep -q '\[session-flush\]'; then
        type="session-summary"
        local ctx=$(grep -A 5 '\[session-flush\]' "$f" 2>/dev/null | grep '^-' | head -4 | tr '\n' ' ' | head -c 500 || true)
        [ -n "$ctx" ] && echo "$fdate|$type|$ctx" >> "$tempfile" && count=$((count+1)) && continue
      fi
      if echo "$trimmed" | grep -q '\[correction'; then
        type="correction"
        local ctx=$(echo "$trimmed" | head -c 300)
        [ -n "$ctx" ] && echo "$fdate|$type|$ctx" >> "$tempfile" && count=$((count+1)) && continue
      fi
      
      # 2. 智能检测（只对非标题行、非空行）
      if [ -n "$trimmed" ] && ! echo "$trimmed" | grep -qE '^(#|\||-*$|>)'; then
        type=$(detect_importance "$trimmed")
        if [ -n "$type" ]; then
          local ctx=$(echo "$trimmed" | head -c 300)
          echo "$fdate|$type|$ctx" >> "$tempfile" && count=$((count+1))
        fi
      fi
    done < "$f"
  done

  echo "   收集 $count 条待同步"
  echo "$tempfile"
}

# ─── Phase 1.5: Sync Remote — 推送到记忆服务器 ────────────

sync_remote() {
  echo "📤 Phase 1.5: Sync Remote → $MEMORY_SERVER"
  echo ""

  # 用 collect 扫出新条目
  local tmpfile=$(collect | tail -1)
  [ ! -f "$tmpfile" ] && echo "   无待同步条目" && return

  local total=0 failed=0
  local tmpfile2=$(mktemp)

  # 逐行解析发送
  while IFS='|' read -r date type content; do
    [ -z "$content" ] && continue
    local clean=$(echo "$content" | sed 's/^[- ]*//;s/"/\\"/g')
    [ -z "$clean" ] && continue

    local resp=$(curl -s --max-time 5 -X POST "$MEMORY_SERVER/api/v2/add" \
      -H "Content-Type: application/json" \
      -H "X-API-Key: ${MEMORY_API_KEY:-}" \
      -d "{\"content\":\"$clean\",\"type\":\"$type\",\"project\":\"xiaolong\",\"source\":\"dream-cycle\"}" 2>/dev/null || echo '{"error":"curl fail"}')

    if echo "$resp" | grep -q 'stored'; then
      total=$((total+1))
    else
      failed=$((failed+1))
    fi
  done < "$tmpfile"

  echo "   成功 $total / 失败 $failed"
  [ "$total" -gt 0 ] && echo "   ✅ 记忆已写入远程服务器"
  echo "$NOW" > "$MARKER"
  rm -f "$tmpfile" "$tmpfile2"
}

# ─── Phase 1.6: Sync Context Graph — 推送结构化记忆 ─────────

sync_context_graph() {
  echo "🧬 Phase 1.6: Sync ContextGraph → $MEMORY_SERVER"

  if [ -f "$WORKSPACE/src/memory/sync-context-graph.js" ]; then
    node "$WORKSPACE/src/memory/sync-context-graph.js" 2>&1 | sed 's/^/   /'
    echo "   ✅ ContextGraph 已同步"
  else
    echo "   ⚠️ sync-context-graph.js 未找到，跳过"
  fi
  echo ""
}

# ─── Phase 2: Consolidate — 本地路由 ──────────────────────

consolidate() {
  echo "🔄 Phase 2: Consolidate — 本地路由"
  echo ""

  mkdir -p "$REGISTERS_DIR"
  for reg in "$REGISTERS_DIR"/*.md; do
    [ -f "$reg" ] || continue
    local name=$(basename "$reg" .md)
    local cnt=$(grep -c '\- \[' "$reg" 2>/dev/null || echo 0)
    echo "   registers/$name.md: ~${cnt} 条"
  done

  local proj_cnt=$(ls "$PROJECTS_DIR"/*.md 2>/dev/null | wc -l | tr -d ' ')
  local vault_lines=$(wc -l < "$MEMORY_DIR/vault.md" 2>/dev/null || echo 0)
  local mem_lines=$(wc -l < "$WORKSPACE/MEMORY.md" 2>/dev/null || echo 0)

  echo "   projects/: ${proj_cnt} 个"
  echo "   vault.md: ${vault_lines} 行"
  echo "   MEMORY.md: ${mem_lines} 行"
}

# ─── Phase 2.5: Refresh Core Memory — 动态刷新核心记忆块 ───

refresh_core_memory() {
  echo "🧠 Phase 2.5: Refresh Core Memory Blocks"
  echo ""

  local core_dir="$MEMORY_DIR/core"
  [ ! -d "$core_dir" ] && echo "  ⚠️  memory/core/ 不存在，跳过" && return

  local now_iso=$(date -Iseconds)

  # 1. active_projects.md — 从最近 3 天的 daily/ 提取项目动态
  local active_file="$core_dir/active_projects.md"
  if [ -f "$active_file" ]; then
    local recent_projects=$(grep -h '\*\*.*\*\*' "$DAILY_DIR"/*.md 2>/dev/null | grep -E '(项目|仓库|同步|接入|部署|升级|完成|进行中)' | tail -10 | sed 's/^/- /' || true)
    if [ -n "$recent_projects" ]; then
      local header=$(head -8 "$active_file")
      echo "$header" > "$active_file"
      echo "" >> "$active_file"
      echo "## 进行中" >> "$active_file"
      echo "" >> "$active_file"
      echo "$recent_projects" >> "$active_file"
      sed -i "" "s/updated_at:.*/updated_at: \"$now_iso\"/" "$active_file" 2>/dev/null || true
      echo "  ✅ active_projects.md 已刷新"
    fi
  fi

  # 2. recent_decisions.md — 从最近 7 天的 vault.md 提取决策
  local decisions_file="$core_dir/recent_decisions.md"
  if [ -f "$decisions_file" ]; then
    local recent_decisions=$(grep -E '^- [0-9]{4}-[0-9]{2}-[0-9]{2}:' "$MEMORY_DIR/vault.md" 2>/dev/null | tail -10 || true)
    if [ -n "$recent_decisions" ]; then
      local header=$(head -8 "$decisions_file")
      echo "$header" > "$decisions_file"
      echo "" >> "$decisions_file"
      echo "# 最近决策" >> "$decisions_file"
      echo "" >> "$decisions_file"
      echo "$recent_decisions" >> "$decisions_file"
      sed -i "" "s/updated_at:.*/updated_at: \"$now_iso\"/" "$decisions_file" 2>/dev/null || true
      echo "  ✅ recent_decisions.md 已刷新"
    fi
  fi

  # 3. tool_status.md — 只更新时间戳，内容由事件触发更新
  local tool_file="$core_dir/tool_status.md"
  if [ -f "$tool_file" ]; then
    sed -i "" "s/updated_at:.*/updated_at: \"$now_iso\"/" "$tool_file" 2>/dev/null || true
    echo "  ✅ tool_status.md 时间戳已更新"
  fi

  echo ""
}

# ─── Phase 3: Evaluate — 健康评分 ─────────────────────────

evaluate() {
  echo "📊 Phase 3: Evaluate — 健康评分"
  echo ""

  local total_daily=$(ls "$DAILY_DIR"/*.md 2>/dev/null | wc -l | tr -d ' ')
  local reg_count=0 updated_regs=0

  for f in "$REGISTERS_DIR"/*.md; do
    [ -f "$f" ] || continue
    reg_count=$((reg_count+1))
    local mtime=$(stat -f "%m" "$f" 2>/dev/null || echo 0)
    [ "$mtime" -gt 0 ] && [ $(( (NOW_EPOCH-mtime)/86400 )) -le 30 ] && updated_regs=$((updated_regs+1))
  done

  local vault_lines=$(wc -l < "$MEMORY_DIR/vault.md" 2>/dev/null || echo 0)

  # 各维度评分
  local freshness=100; [ "$total_daily" -gt 0 ] && freshness=$(( $(ls -t "$DAILY_DIR"/*.md 2>/dev/null | head -1 | xargs stat -f "%m" 2>/dev/null || echo 0) > $((NOW_EPOCH-86400*7)) ? 100 : 50 ))
  local coverage=100; [ "$reg_count" -gt 0 ] && coverage=$(( updated_regs*100/reg_count ))
  local efficiency=100; [ "$vault_lines" -gt 200 ] && efficiency=60
  local reliability=90
  local security=95
  local health=$(( (freshness*25+coverage*25+efficiency*20+reliability*15+security*15)/100 ))

  echo "   📈 健康评分: ${health}/100"
  echo "   Freshness: ${freshness}/100 | Coverage: ${coverage}/100 | Efficiency: ${efficiency}/100"
  echo "   Reliability: ${reliability}/100 | Security: ${security}/100"
  echo "   daily: ${total_daily}篇 | registers: ${reg_count}个 | vault: ${vault_lines}行"
}

# ─── 入口 ───────────────────────────────────────────

case "${1:-all}" in
  collect)  collect ;;
  sync-remote) sync_remote ;;
  sync-graph) sync_context_graph ;;
  evaluate) evaluate ;;  
  health)   evaluate 2>&1 | grep -E '健康|Freshness|daily' ;;
  all)
    echo "🌙 小龙的 Dream Cycle — $NOW $(date +%H:%M)"
    echo "============================================"
    echo ""
    sync_remote
    sync_context_graph
    echo ""
    refresh_core_memory
    echo ""
    consolidate
    forget_context_graph
    echo ""
    evaluate
    echo ""
    echo "✅ Dream Cycle 完成 (下一轮: 每天 4:00)"
    ;;
  core-refresh) refresh_core_memory ;;
  *) echo "用法: bash scripts/dream-cycle.sh [collect|sync-remote|evaluate|health]"; exit 1 ;;
esac

# ─── Phase 1.7: ContextGraph 遗忘机制 ─────────────────────

forget_context_graph() {
  echo "🧠 Phase 1.7: ContextGraph 遗忘机制"
  
  [ ! -f ~/.openclaw/context-graph.json ] && echo "  无文件" && return
  
  node -e "
    const fs = require('fs');
    const g = JSON.parse(fs.readFileSync(process.env.HOME + '/.openclaw/context-graph.json', 'utf8'));
    
    let removed = 0;
    const now = Date.now();
    const THRESHOLD = 90 * 86400 * 1000; // 90天
    
    for (const [id, entity] of Object.entries(g.entities)) {
      // 遗忘条件：非 pinned + 最后观察 >90天 + 低优先级
      if (entity.pinned) continue;
      if (!entity.observations || entity.observations.length === 0) continue;
      
      const lastObs = entity.observations[entity.observations.length - 1];
      const age = now - new Date(lastObs.timestamp).getTime();
      
      const hasHighPriority = entity.observations.some(o => 
        o.priority === 'critical' || o.priority === 'high'
      );
      
      if (age > THRESHOLD && !hasHighPriority) {
        delete g.entities[id];
        removed++;
      }
    }
    
    // 清理孤立关系
    const before = g.relations.length;
    g.relations = g.relations.filter(r => 
      g.entities[r.from] && g.entities[r.to]
    );
    const relRemoved = before - g.relations.length;
    
    fs.writeFileSync(process.env.HOME + '/.openclaw/context-graph.json', JSON.stringify(g, null, 2));
    console.log('  遗忘实体: ' + removed + ' | 孤立关系: ' + relRemoved);
  " 2>/dev/null || echo "  跳过"
}
