#!/usr/bin/env bash
# session-compact.sh — 会话压缩与快速存档 + Write Gate 前置检查
#
# v3 — 加入 pre-compact 钩子：上下文压缩前自动扫描未保存的纠正/决策/承诺
# v2 — 加入 Write Gate 自动路由：preference/correction/decision 触发寄存器写入
#
# 用法:
#   bash scripts/session-compact.sh save "任何想记住的内容"
#   bash scripts/session-compact.sh save --type preference "偏好内容"
#   bash scripts/session-compact.sh save --type correction "纠正内容"
#   bash scripts/session-compact.sh save --type decision "决策内容"
#   bash scripts/session-compact.sh pre-compact  # 上下文压缩前扫描 + 同步
#   bash scripts/session-compact.sh status    # 看会话统计
#   bash scripts/session-compact.sh flush     # 触发 Dream Cycle 同步
#   bash scripts/session-compact.sh check     # Write Gate 诊断

set -euo pipefail

WORKSPACE="$(cd "$(dirname "$0")/.." && pwd)"
MEMORY_SERVER="http://111.229.145.18"

# 加载环境变量（MEMORY_API_KEY 等）
if [ -z "${MEMORY_API_KEY:-}" ] && [ -f ~/.zshrc ]; then
  MEMORY_API_KEY=$(grep '^export MEMORY_API_KEY=' ~/.zshrc 2>/dev/null | cut -d'"' -f2 || true)
fi
export MEMORY_API_KEY
DAILY="$WORKSPACE/memory/daily/$(date +%Y-%m-%d).md"
REGISTERS="$WORKSPACE/memory/registers"
SESSION_LOG="$WORKSPACE/memory/.session-compact.log"

# ── Write Gate 路由 ──────────────────────────────────────

write_to_register() {
  local type="$1" content="$2" timestamp
  timestamp=$(date '+%Y-%m-%d %H:%M')
  
  case "$type" in
    preference)
      printf "\n| %s | %s | %s | session-compact | medium |\n" \
        "$(wc -l < "$REGISTERS/preferences.md" | tr -d ' ')" \
        "$content" "$timestamp" >> "$REGISTERS/preferences.md"
      echo "  📝 → preferences 寄存器"
      ;;
    correction)
      printf "\n[correction: %s]\n- **new**: %s\n- **source**: session-compact\n" \
        "$timestamp" "$content" >> "$REGISTERS/open-loops.md"
      echo "  🔧 → open-loops 寄存器 (correction)"
      ;;
    decision)
      printf "\n- %s: %s (session-compact)\n" "$timestamp" "$content" \
        >> "$WORKSPACE/memory/vault.md"
      echo "  📋 → vault.md (decision)"
      ;;
    event)
      # 事件记录到 daily/ 已足够，不写寄存器
      echo "  📌 → daily/ (event，不写寄存器)"
      ;;
    fact|behavior|code|rule|tool|project)
      # 新类型：写入 vault.md 的决策时间线
      printf "\n- %s: [%s] %s (session-compact)\n" "$timestamp" "$type" "$content" \
        >> "$WORKSPACE/memory/vault.md"
      echo "  📋 → vault.md ($type)"
      ;;
    *)
      echo "  ⚠️  未知类型 '$type'，仅写入 daily/"
      ;;
  esac
}

# ── 命令处理 ─────────────────────────────────────────────

case "${1:-help}" in
  save)
    shift
    # 解析参数
    type=""
    content=""
    recall_context=""
    while [ $# -gt 0 ]; do
      case "$1" in
        --type) type="$2"; shift 2 ;;
        --type=*) type="${1#*=}"; shift ;;
        --recall-context) recall_context="$2"; shift 2 ;;
        --recall-context=*) recall_context="${1#*=}"; shift ;;
        *) content="$1"; shift ;;
      esac
    done

    if [ -z "$content" ]; then
      echo "用法: bash scripts/session-compact.sh save [--type <type>] [--recall-context <context>] <内容>"
      exit 1
    fi

    # ── Step 1: LLM Write Gate 判断（如果未指定 type）──
    if [ -z "$type" ] && [ -f "$WORKSPACE/scripts/memory-write-gate.sh" ]; then
      echo "  🧠 Write Gate LLM 判断中..."
      GATE_RESULT=$(echo "$content" | bash "$WORKSPACE/scripts/memory-write-gate.sh" 2>/dev/null || echo '{"should_remember":false}')
      SHOULD_REMEMBER=$(echo "$GATE_RESULT" | jq -r '.should_remember // false' 2>/dev/null || echo "false")

      if [ "$SHOULD_REMEMBER" = "true" ]; then
        type=$(echo "$GATE_RESULT" | jq -r '.type // ""' 2>/dev/null || echo "")
        importance=$(echo "$GATE_RESULT" | jq -r '.importance // 0.5' 2>/dev/null || echo "0.5")
        gate_content=$(echo "$GATE_RESULT" | jq -r '.content // ""' 2>/dev/null || echo "")
        gate_recall=$(echo "$GATE_RESULT" | jq -r '.recall_context // [] | join(",")' 2>/dev/null || echo "")
        gate_tags=$(echo "$GATE_RESULT" | jq -r '.tags // [] | join(",")' 2>/dev/null || echo "")

        # 用 LLM 精炼的内容覆盖原始内容
        [ -n "$gate_content" ] && [ "$gate_content" != "null" ] && content="$gate_content"
        # 用 LLM 的 recall_context（如果用户没指定）
        [ -z "$recall_context" ] && [ -n "$gate_recall" ] && [ "$gate_recall" != "null" ] && recall_context="$gate_recall"

        echo "  ✅ Write Gate: 值得记 (type=$type, importance=$importance)"
      else
        gate_reason=$(echo "$GATE_RESULT" | jq -r '.reason // "not_important"' 2>/dev/null || echo "not_important")
        echo "  ℹ️  Write Gate: 不值得记 ($gate_reason)，仅写入 daily/"
      fi
    fi

    # ── Step 2: 写入 daily/（带 YAML frontmatter）──
    mkdir -p "$(dirname "$DAILY")"
    memory_id="mem_$(date +%Y%m%d)_$(openssl rand -hex 3 2>/dev/null || date +%s)"

    # 如果有 recall_context，用 YAML frontmatter 格式
    if [ -n "$type" ] || [ -n "$recall_context" ]; then
      {
        echo ""
        echo "---"
        echo "memory_id: $memory_id"
        [ -n "$type" ] && echo "type: $type"
        echo "created_at: $(date -Iseconds)"
        [ -n "$recall_context" ] && echo "recall_context: [$recall_context]"
        echo "---"
        echo "[session-flush] $(date +%H:%M) — $content"
      } >> "$DAILY"
    else
      echo "" >> "$DAILY"
      echo "[session-flush] $(date +%H:%M) — $content" >> "$DAILY"
    fi
    echo "  ✅ 已写入 $DAILY (id: $memory_id)"

    # ── Step 3: Write Gate — 写寄存器 ──
    if [ -n "$type" ]; then
      write_to_register "$type" "$content"
    fi

    # ── Step 4: ContextGraph 集成 ──
    if [ "$type" = "decision" ] || [ "$type" = "correction" ] || [ "$type" = "event" ]; then
      node -e "
        try {
          const { ContextGraph } = require('$WORKSPACE/src/memory/context-graph');
          const g = new ContextGraph();
          const entityId = 'session:$(date +%Y%m%d)';
          g.entity(entityId, { type: 'session', name: 'Session $(date +%Y-%m-%d)' });
          g.observe(entityId, {
            type: '$type',
            priority: '$type' === 'decision' ? 'high' : 'normal',
            summary: process.argv[1].substring(0, 200),
            details: { source: 'session-compact', memory_id: '$memory_id' }
          });
          g.save();
          console.log('  🕸️  → ContextGraph');
        } catch(e) { console.log('  ⚠️  ContextGraph: ' + e.message); }
      " "$content" 2>/dev/null || echo "  ⚠️  ContextGraph 写入失败"
    fi

    # ── Step 5: 推到远程服务器（带新字段）──
    clean=$(echo "$content" | sed 's/"/\\"/g' | head -c 400)
    remote_payload=$(jq -n \
      --arg c "$clean" \
      --arg t "${type:-session-flush}" \
      --arg rc "${recall_context:-}" \
      --arg mid "$memory_id" \
      '{content:$c, type:$t, project:"xiaolong", source:"session-compact", memory_id:$mid, recall_context:($rc | split(",") | map(select(. != "")))}')

    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 "$remote_payload" 2>/dev/null) || true

    if echo "$resp" | grep -q 'stored\|ok'; then
      echo "  ✅ 已同步到远程服务器"
    else
      echo "  ⚠️  远程同步: ${resp:-无响应}"
    fi
    echo "$(date '+%Y-%m-%d %H:%M') | ${type:-general} | $content" >> "$SESSION_LOG"
    ;;

  check)
    echo "🔍 Write Gate 诊断"
    echo ""
    echo "  寄存器状态:"
    for f in "$REGISTERS"/*.md; do
      [ -f "$f" ] || continue
      name=$(basename "$f" .md)
      lines=$(wc -l < "$f" | tr -d ' ')
      size=$(wc -c < "$f" | tr -d ' ')
      echo "    $name: ${lines}行 / ${size}字节"
    done
    echo ""
    echo "  最近 5 条 compact:"
    [ -f "$SESSION_LOG" ] && tail -5 "$SESSION_LOG" || echo "    无记录"
    echo ""
    echo "  今天 daily:"
    [ -f "$DAILY" ] && wc -l < "$DAILY" | xargs echo "    lines:" || echo "    (空)"
    ;;

  status)
    echo "📊 会话状态"
    echo "  每日日志: $DAILY"
    echo "  寄存器:"
    ls "$REGISTERS"/*.md 2>/dev/null | while read -r f; do
      echo "    $(basename "$f" .md): $(wc -l < "$f") 行"
    done
    echo "  Compact 历史:"
    [ -f "$SESSION_LOG" ] && tail -5 "$SESSION_LOG" || echo "    无记录"
    ;;

  flush)
    echo "🔄 触发立即存档..."
    bash "$WORKSPACE/scripts/dream-cycle.sh" sync-remote 2>&1
    ;;

  pre-compact)
    echo "🛡️  Pre-Compaction Hook — 上下文压缩前安全检查"
    echo ""
    
    has_flush=false
    if [ -f "$DAILY" ]; then
      if grep -q '\[session-flush\]' "$DAILY"; then
        echo "  ✅ 今天已有 [session-flush] 标记"
        has_flush=true
        grep '\[session-flush\]' "$DAILY" | tail -3 | while read -r line; do
          echo "     $(echo "$line" | cut -c1-80)"
        done
      fi
    fi
    
    if [ "$has_flush" = false ]; then
      echo "  ⚠️  今天 daily/ 无 [session-flush] 标记 — 压缩前请先存档关键信息"
    fi
    
    echo ""
    echo "  🌐 远程服务器:"
    remote_resp=$(curl -s --max-time 5 "$MEMORY_SERVER/api/v2/stats" 2>/dev/null || echo '{"error":"unreachable"}')
    if echo "$remote_resp" | grep -q 'xiaolong'; then
      echo "     ✅ 在线"
    else
      echo "     ⚠️  不可达"
    fi
    
    echo ""
    echo "  📤 触发同步..."
    bash "$WORKSPACE/scripts/dream-cycle.sh" sync-remote 2>&1 | sed 's/^/     /'
    echo ""
    echo "  ✅ Pre-Compaction Hook 完成"
    ;;

  *)
    echo "用法:"
    echo "  bash scripts/session-compact.sh save <内容>                    — 快速存关键信息"
    echo "  bash scripts/session-compact.sh save --type preference <内容>   — 记偏好（→registers）"
    echo "  bash scripts/session-compact.sh save --type correction <内容>   — 记纠正（→registers）"
    echo "  bash scripts/session-compact.sh save --type decision <内容>     — 记决策（→vault）"
    echo "  bash scripts/session-compact.sh pre-compact                    — 压缩前安全检查 + 同步"
    echo "  bash scripts/session-compact.sh status                         — 查看状态"
    echo "  bash scripts/session-compact.sh check                          — Write Gate 诊断"
    echo "  bash scripts/session-compact.sh flush                          — 触发全部同步"
    ;;
esac
