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

PROJECT_ROOT="${CLAUDE_PROJECT_DIR:-$(pwd)}"
SESSION_START_INPUT="$(cat)"

# SessionStart must not synchronously run Doctor. Full diagnostics can touch
# several databases and exceed the lifecycle acknowledgement budget.
PROJECT_NAME="$(basename "$PROJECT_ROOT")"
SUMMARY="DevFlow 已就绪。项目 ${PROJECT_NAME}。使用 /devflow 查看全部已安装能力，并按项目技术栈选择对应的 /devflow:* 领域 Skill；运行 devflow doctor 查看完整健康状态。"

# Generate session token for MCP Gateway authentication
# Clean up stale receipt from previous session
RECEIPT_DIR="${TMPDIR:-/tmp}/.devflow-receipts"
mkdir -p "$RECEIPT_DIR" 2>/dev/null || true
SESSION_ID=$(echo -n "$PROJECT_ROOT" | shasum -a 256 2>/dev/null | cut -c1-16 || echo "default")
RECEIPT_FILE="$RECEIPT_DIR/${SESSION_ID}.json"
OVERRIDE_FILE="$RECEIPT_DIR/${SESSION_ID}.override"
rm -f "$RECEIPT_FILE" "$OVERRIDE_FILE" 2>/dev/null || true

# Determine plugin root directory (same way as superpowers)
if [ -z "${CLAUDE_PLUGIN_ROOT:-}" ]; then
  SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
  PLUGIN_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
else
  PLUGIN_ROOT="$CLAUDE_PLUGIN_ROOT"
fi
source "${PLUGIN_ROOT}/hooks/runtime-node"
DEVFLOW_NODE="$(devflow_resolve_node "$PLUGIN_ROOT")"

# Read DevFlow Constitution from file (avoids shell escaping issues)
CONSTITUTION_FILE="${PLUGIN_ROOT}/skills/devflow/SKILL.md"
if [ -f "${PLUGIN_ROOT}/hooks/constitution.md" ]; then
  CONSTITUTION_FILE="${PLUGIN_ROOT}/hooks/constitution.md"
fi

if [ -f "$CONSTITUTION_FILE" ]; then
  GLOBAL_CONSTRAINT=$(cat "$CONSTITUTION_FILE" 2>/dev/null || echo "DevFlow Constitution 加载失败")
else
  GLOBAL_CONSTRAINT="DevFlow Constitution 文件未找到。使用 /devflow: 命令时请优先调用 MCP 工具。"
fi

# Read the active rule/gate counts from the canonical global database.
GATE_COUNT=4
RULE_COUNT=""
ENFORCER_SUMMARY_JS="${PLUGIN_ROOT}/dist/hooks/enforcer-summary.js"
if [ -f "$ENFORCER_SUMMARY_JS" ] && [ -x "$DEVFLOW_NODE" ]; then
  ENFORCER_SUMMARY=$("$DEVFLOW_NODE" "$ENFORCER_SUMMARY_JS" 2>/dev/null || echo "")
  if [ -n "$ENFORCER_SUMMARY" ] && command -v python3 >/dev/null 2>&1; then
    PARSED_COUNTS=$(printf '%s' "$ENFORCER_SUMMARY" | python3 -c "import json,sys; d=json.load(sys.stdin); g=d.get('gateCount'); r=d.get('ruleCount'); print(f'{g}|{r}' if isinstance(g,int) and isinstance(r,int) else '')" 2>/dev/null || echo "")
    if [[ "$PARSED_COUNTS" =~ ^[0-9]+\|[0-9]+$ ]]; then
      GATE_COUNT="${PARSED_COUNTS%%|*}"
      RULE_COUNT="${PARSED_COUNTS##*|}"
    fi
  fi
fi
GATE_DETAIL="启用规则数未知"
if [ -n "$RULE_COUNT" ]; then
  GATE_DETAIL="启用规则: ${RULE_COUNT}"
fi

# Append Gateway status to summary
GATEWAY_STATUS=$'\n\n━━━━━━ DevFlow Gateway ━━━━━━\n'
# Check if devflow MCP server is configured (token will be auto-generated by ensureSessionToken)
if [ -f ~/.claude/.mcp.json ] && grep -q '"devflow"' ~/.claude/.mcp.json 2>/dev/null; then
  GATEWAY_STATUS="${GATEWAY_STATUS}Status: 🟢 DevFlow MCP 已配置"$'\n'"Gate 层数: ${GATE_COUNT} (${GATE_DETAIL})"$'\n'
elif [ -f .mcp.json ] && grep -q '"devflow"' .mcp.json 2>/dev/null; then
  GATEWAY_STATUS="${GATEWAY_STATUS}Status: 🟢 DevFlow MCP 已配置（项目级）"$'\n'"Gate 层数: ${GATE_COUNT} (${GATE_DETAIL})"$'\n'
else
  GATEWAY_STATUS="${GATEWAY_STATUS}Status: 🔴 未配置 DevFlow MCP Server"$'\n请将 devflow MCP 配置加入 .mcp.json\n'
fi
GATEWAY_STATUS="${GATEWAY_STATUS}━━━━━━━━━━━━━━━━━━━━━━━━━━━"

# ── MCP Tool Usage Protocol (injected into every session) ──
MCP_PROTOCOL="

## DevFlow MCP 工具使用协议（必须遵守）

当用户使用 /devflow: 开头的技能时（如 /devflow:react、/devflow:vue、/devflow:context 等），你必须按以下流程操作：

### Step 1: 获取上下文（必须先执行）
调用 mcp__devflow__get_project_context，参数格式：{\"query\": \"用户的问题描述\", \"projectRoot\": \"\${CLAUDE_PROJECT_DIR}\"}
这个工具返回项目中的相关文件、符号、风险提示和下一步建议。
不要跳过这一步。不要用 Explore、Agent、Grep 或 Bash 代替。

### Step 2: 按需调用领域工具
根据已加载 Skill 和 command registry 调用对应的 MCP 工具。以下仅为高频示例，完整能力以 /devflow 列表和 command registry 为准：
- React 性能问题 → mcp__devflow__react_audit_performance（参数：{\"projectRoot\": \"\${CLAUDE_PROJECT_DIR}\"}）
- React Bug → mcp__devflow__react_diagnose_bug
- React Hooks 审查 → mcp__devflow__react_review_hooks
- Vue 问题 → mcp__devflow__vue_diagnose_bug
- NestJS 问题 → mcp__devflow__nest_diagnose_bug
- 代码搜索 → mcp__devflow__search_symbol
- 知识查询 → mcp__devflow__get_knowledge

### Step 3: 基于工具返回结果进行分析
用 Read 确认工具返回的关键文件，然后给出分析结论。

### 铁律
- 永远不要用 Explore、Agent、Grep 或 Glob 替代 MCP 工具的第一步上下文获取
- Every mcp__devflow__ tool MUST pass projectRoot = \${CLAUDE_PROJECT_DIR}; missing projectRoot is rejected; no cwd fallback.
- 如果 MCP 工具返回错误，重试一次；再失败则回退到直接读文件"

COMPACT_PROTOCOL="

## DevFlow compact protocol
- Project: ${PROJECT_NAME} (${PROJECT_ROOT})
- For /devflow:* tasks, call mcp__devflow__get_project_context before broad native search, then use the applicable domain MCP tool.
- Explicit memory is successful only after a DevFlow canonical receipt. Queue acceptance or host-local memory is not a successful save.
- Static findings are not runtime measurements. Do not claim observed bottlenecks, severity, or quantified benefit without evidence.
- Workflow success requires canonical host-action and verification evidence. Never convert degraded, cancelled, failed, or unknown state into success.
- Full versioned rules: devflow://protocol/core, devflow://protocol/memory, devflow://protocol/evidence.
- Retry MCP transport failure once, then degrade truthfully to direct file inspection."

DEVFLOW_DAEMON_REACHABLE=0
DEVFLOW_DAEMON_LAUNCHING=0
DEVFLOW_SESSION_DURABLE=0
DEVFLOW_SESSION_RECEIPT=""
DAEMON_SESSION_RESPONSE=""
run_lifecycle() {
  local PROJECT_HASH=""
  local log_dir="${HOME}/.devflow/logs"
  local fallback_dir="${TMPDIR:-/tmp}"

  if command -v shasum >/dev/null 2>&1; then
    PROJECT_HASH=$(printf '%s' "$PROJECT_ROOT" | shasum -a 256 2>/dev/null | cut -c1-16) || PROJECT_HASH=""
  fi
  [[ "$PROJECT_HASH" =~ ^[0-9a-f]{16}$ ]] || PROJECT_HASH=""

  if mkdir -p "$log_dir" 2>/dev/null; then
    DAEMON_LOG="${log_dir}/hook-daemon-${PROJECT_HASH:-fallback}.log"
    SERVER_LOG="${HOME}/.devflow/logs/server.log"
  elif [ -d "$fallback_dir" ]; then
    DAEMON_LOG="${fallback_dir}/devflow-hook-daemon-${PROJECT_HASH:-fallback}.log"
    SERVER_LOG="${fallback_dir}/devflow-server.log"
  else
    DAEMON_LOG="/dev/null"
    SERVER_LOG="/dev/null"
  fi

  DAEMON_JS="${PLUGIN_ROOT}/dist/hooks/hook-daemon.js"
  CLIENT_JS="${PLUGIN_ROOT}/dist/hooks/hook-client.js"

  register_session() {
    local suppress_fallback="${1:-1}"
    local response=""
    [ -n "$SESSION_START_INPUT" ] || return 0
    [ -f "$CLIENT_JS" ] || return 1
    if response=$(DEVFLOW_HOOK_CLIENT_TIMEOUT_MS=250 \
      DEVFLOW_HOOK_CLIENT_RETRY_DELAY_MS=0 \
      DEVFLOW_HOOK_CLIENT_SUPPRESS_FALLBACK_LOG="$suppress_fallback" \
      "$DEVFLOW_NODE" "$CLIENT_JS" session-start "$SESSION_START_INPUT" 2>> "$DAEMON_LOG"); then
      DEVFLOW_DAEMON_REACHABLE=1
      DEVFLOW_SESSION_DURABLE=1
      DAEMON_SESSION_RESPONSE="$response"
      return 0
    fi
    return 1
  }

  queue_session_start() {
    [ -n "$SESSION_START_INPUT" ] || return 1
    [ -n "$PROJECT_HASH" ] || return 1
    local queue_dir="${HOME}/.devflow/state/${PROJECT_HASH}/session-start-pending"
    local receipt_id="session-start-spool-${PROJECT_HASH}-$(date +%s)-$$"
    local temp_file="${queue_dir}/.${receipt_id}.tmp"
    local target_file="${queue_dir}/${receipt_id}.json"
    mkdir -p "$queue_dir" 2>/dev/null || return 1
    if (umask 077 && printf '%s' "$SESSION_START_INPUT" > "$temp_file") 2>/dev/null \
      && mv "$temp_file" "$target_file" 2>/dev/null; then
      DEVFLOW_SESSION_DURABLE=1
      DEVFLOW_SESSION_RECEIPT="$receipt_id"
      return 0
    fi
    rm -f "$temp_file" 2>/dev/null || true
    return 1
  }

  if [ -n "$SESSION_START_INPUT" ] && register_session 1; then
    :
  elif [ -f "$DAEMON_JS" ] && command -v nohup >/dev/null 2>&1 && [ -x "$DEVFLOW_NODE" ]; then
    if [ -n "$SESSION_START_INPUT" ] && queue_session_start; then
      nohup "$DEVFLOW_NODE" "$DAEMON_JS" "$PROJECT_ROOT" </dev/null >> "$DAEMON_LOG" 2>&1 &
    elif [ -n "$SESSION_START_INPUT" ]; then
      nohup "$DEVFLOW_NODE" "$DAEMON_JS" "$PROJECT_ROOT" "$SESSION_START_INPUT" </dev/null >> "$DAEMON_LOG" 2>&1 &
    else
      nohup "$DEVFLOW_NODE" "$DAEMON_JS" "$PROJECT_ROOT" </dev/null >> "$DAEMON_LOG" 2>&1 &
    fi
    DAEMON_PID=$!
    DEVFLOW_DAEMON_LAUNCHING=1
    echo "[devflow] Hook daemon started for ${PROJECT_ROOT} (PID ${DAEMON_PID})" >&2 || true

    # Close the startup race between SessionStart and the first user prompt.
    # Registration is idempotent and the durable spool remains authoritative if
    # the bounded readiness window expires on a slow host.
    if [ -n "$SESSION_START_INPUT" ]; then
      for _ in 1 2 3 4 5 6 7 8; do
        if register_session 1; then
          DEVFLOW_DAEMON_LAUNCHING=0
          break
        fi
        sleep 0.05
      done
    fi
  fi

  if [ -n "$SESSION_START_INPUT" ] \
    && [ "$DEVFLOW_DAEMON_REACHABLE" != "1" ] \
    && [ "$DEVFLOW_DAEMON_LAUNCHING" != "1" ]; then
    register_session 0 || true
  fi

  SERVER_BOOTSTRAP_JS="${PLUGIN_ROOT}/dist/hooks/server-bootstrap.js"
  if [ -f "$SERVER_BOOTSTRAP_JS" ] && command -v nohup >/dev/null 2>&1 && [ -x "$DEVFLOW_NODE" ]; then
    nohup "$DEVFLOW_NODE" "$SERVER_BOOTSTRAP_JS" </dev/null >> "$SERVER_LOG" 2>&1 &
  fi
}

run_lifecycle || true

DEGRADED_CONTEXT=""
if [ "$DEVFLOW_DAEMON_LAUNCHING" = "1" ]; then
  if [ "$DEVFLOW_SESSION_DURABLE" = "1" ]; then
    DEGRADED_CONTEXT="

DevFlow SessionStart status=accepted; durable=true; receiptId=${DEVFLOW_SESSION_RECEIPT:-daemon-receipt}; daemon registration is pending while the runtime warms."
  else
    DEGRADED_CONTEXT="

WARNING: DevFlow daemon launch was requested, but SessionStart persistence was not confirmed. Continue in degraded mode and do not treat lifecycle registration as completed."
  fi
elif [ "$DEVFLOW_DAEMON_REACHABLE" != "1" ]; then
  DEGRADED_CONTEXT="

WARNING: DevFlow is in degraded mode (daemon unreachable): 4-Gate enforcement is unavailable, memory prefetch cache is unavailable, and daemon-shared Hook state is unavailable. Run devflow doctor for details."
fi

MEMORY_SNAPSHOT=$'## Project memory\nDevFlow memory snapshot=pending; source=none; reason=runtime_warming. Memory availability is not yet known; do not conclude that the project has no memories.'
CONFIG_FILE="${PROJECT_ROOT}/.devflow/config.json"
MEMORY_RESPONSE=""
if { [ "$DEVFLOW_DAEMON_REACHABLE" = "1" ] || [ -z "$SESSION_START_INPUT" ]; } \
  && [ -f "${CLIENT_JS:-}" ] \
  && [ -x "$DEVFLOW_NODE" ]; then
  MEMORY_RESPONSE=$(DEVFLOW_HOOK_CLIENT_TIMEOUT_MS=75 \
    DEVFLOW_HOOK_CLIENT_RETRY_DELAY_MS=0 \
    "$DEVFLOW_NODE" "$CLIENT_JS" memory-snapshot </dev/null 2>/dev/null || echo "")
fi
MEMORY_CACHE_JS="${PLUGIN_ROOT}/dist/hooks/memory-snapshot-cache.js"
if [ -f "$MEMORY_CACHE_JS" ] && [ -x "$DEVFLOW_NODE" ]; then
  MEMORY_SNAPSHOT=$(DEVFLOW_MEMORY_SNAPSHOT_CLI=1 \
    DEVFLOW_MEMORY_RESPONSE="$MEMORY_RESPONSE" \
    "$DEVFLOW_NODE" "$MEMORY_CACHE_JS" session-start "$PROJECT_ROOT" "$CONFIG_FILE" 2>/dev/null) \
    || MEMORY_SNAPSHOT=$'## Project memory\nDevFlow memory snapshot=degraded; source=none; reason=cache_reader_failed. Memory availability is not yet known; do not conclude that the project has no memories.'
fi

ONBOARDING_SNAPSHOT=""
ONBOARDING_FILE="${PROJECT_ROOT}/.devflow/onboarding-pack.json"
if [ -f "$ONBOARDING_FILE" ] && command -v python3 >/dev/null 2>&1; then
  ONBOARDING_SNAPSHOT=$(python3 - "$ONBOARDING_FILE" 2>/dev/null <<'PY'
import json,sys
def clean(value): return ' '.join(str(value).split())[:120]
try: data=json.load(open(sys.argv[1]))
except Exception: raise SystemExit
modules=[clean(item.get('name','')) for item in data.get('keyModules',[])[:6] if item.get('name')]
layers=[f"{clean(item.get('name',''))} ({int(item.get('files',0))} files)" for item in data.get('architectureLayers',[])[:6] if item.get('name')]
if modules or layers:
  print('## Project map (auto-injected)')
  if modules: print('Key modules: ' + ', '.join(modules))
  if layers: print('Architecture: ' + ', '.join(layers))
PY
  ) || ONBOARDING_SNAPSHOT=""
fi

DAEMON_CONTEXT=""
if [ -n "$DAEMON_SESSION_RESPONSE" ] && command -v python3 >/dev/null 2>&1; then
  DAEMON_CONTEXT=$(DEVFLOW_DAEMON_RESPONSE="$DAEMON_SESSION_RESPONSE" python3 -c "import json,os; d=json.loads(os.environ.get('DEVFLOW_DAEMON_RESPONSE','{}')); status=d.get('status'); receipt=d.get('receiptId'); work=d.get('workState'); parts=[]; parts.append(f'DevFlow SessionStart status={status}; durable=true; receiptId={receipt}; workState={work}.' if status in ('accepted','completed') and receipt else ''); parts.append(str(d.get('additionalContext',''))); print('\\n'.join(x for x in parts if x))" 2>/dev/null || echo "")
  [ -z "$DAEMON_CONTEXT" ] || DAEMON_CONTEXT=$'\n\n'"${DAEMON_CONTEXT}"
fi

COMPACT_SESSION_START_MODE="${DEVFLOW_ROLLOUT_COMPACT_SESSION_START:-}"
if [ -z "$COMPACT_SESSION_START_MODE" ] && [ -f "$CONFIG_FILE" ] && command -v python3 >/dev/null 2>&1; then
  COMPACT_SESSION_START_MODE=$(python3 - "$CONFIG_FILE" 2>/dev/null <<'PY'
import json,sys
try: value=json.load(open(sys.argv[1])).get('rollout',{}).get('compactSessionStart','off')
except Exception: value='off'
print(value if value in ('off','shadow','on') else 'off')
PY
  ) || COMPACT_SESSION_START_MODE="off"
fi
case "$COMPACT_SESSION_START_MODE" in off|shadow|on) ;; *) COMPACT_SESSION_START_MODE="off" ;; esac
if [ "${DEVFLOW_TEST_PROTOCOL_RESOURCE_FAIL:-0}" = "1" ]; then
  COMPACT_SESSION_START_MODE="off"
fi

CURRENT_PROTOCOL_PAYLOAD="${MCP_PROTOCOL}"$'\n\n'"${GLOBAL_CONSTRAINT}"
PROTOCOL_PAYLOAD="$CURRENT_PROTOCOL_PAYLOAD"
if [ "$COMPACT_SESSION_START_MODE" = "on" ]; then
  PROTOCOL_PAYLOAD="$COMPACT_PROTOCOL"
fi
CURRENT_PROTOCOL_BYTES=$(printf '%s' "$CURRENT_PROTOCOL_PAYLOAD" | wc -c | tr -d ' ')
COMPACT_PROTOCOL_BYTES=$(printf '%s' "$COMPACT_PROTOCOL" | wc -c | tr -d ' ')
PAYLOAD_METRIC_DIR="${HOME}/.devflow/state/${SESSION_ID}"
PAYLOAD_METRIC_FILE="${PAYLOAD_METRIC_DIR}/session-start-payload-v1.json"
PAYLOAD_METRIC_TEMP="${PAYLOAD_METRIC_FILE}.$$"
if mkdir -p "$PAYLOAD_METRIC_DIR" 2>/dev/null \
  && printf '{"schemaVersion":"devflow.session-start-payload.v1","mode":"%s","currentBytes":%s,"candidateBytes":%s,"recordedAt":%s}\n' \
    "$COMPACT_SESSION_START_MODE" "$CURRENT_PROTOCOL_BYTES" "$COMPACT_PROTOCOL_BYTES" "$(date +%s000)" > "$PAYLOAD_METRIC_TEMP" 2>/dev/null; then
  mv "$PAYLOAD_METRIC_TEMP" "$PAYLOAD_METRIC_FILE" 2>/dev/null || true
else
  rm -f "$PAYLOAD_METRIC_TEMP" 2>/dev/null || true
fi
printf '[devflow] SessionStart protocol mode=%s currentBytes=%s candidateBytes=%s\n' \
  "$COMPACT_SESSION_START_MODE" "$CURRENT_PROTOCOL_BYTES" "$COMPACT_PROTOCOL_BYTES" >&2 || true

SUMMARY="${SUMMARY}${DEGRADED_CONTEXT}${DAEMON_CONTEXT}"$'\n\n'"${ONBOARDING_SNAPSHOT}"$'\n\n'"${MEMORY_SNAPSHOT}${PROTOCOL_PAYLOAD}${GATEWAY_STATUS}"

if [ -x "$DEVFLOW_NODE" ] \
  && ESCAPED=$(printf '%s\n' "$SUMMARY" | "$DEVFLOW_NODE" -e "let s=''; process.stdin.setEncoding('utf8'); process.stdin.on('data',c=>s+=c); process.stdin.on('end',()=>process.stdout.write(JSON.stringify(s)))" 2>/dev/null); then
  :
elif command -v python3 >/dev/null 2>&1 \
  && ESCAPED=$(printf '%s\n' "$SUMMARY" | python3 -c "import sys,json; print(json.dumps(sys.stdin.read()))" 2>/dev/null); then
  :
else
  ESCAPED='"DevFlow SessionStart status=degraded; memory snapshot=pending; runtime helpers unavailable. Memory availability is not yet known."'
fi

printf '{\n  "hookSpecificOutput": {\n    "hookEventName": "SessionStart",\n    "additionalContext": %s\n  }\n}\n' "$ESCAPED"

exit 0
