#!/usr/bin/env bash
# ─────────────────────────────────────────────────────────────────────────────
# skill-fog CLI
# ─────────────────────────────────────────────────────────────────────────────
set -euo pipefail

SKILL_FOG_DIR="$HOME/.skill-fog"
PATTERNS_FILE="$SKILL_FOG_DIR/patterns.json"
PENDING_DIR="$SKILL_FOG_DIR/pending"
CLAUDE_DIR="$HOME/.claude"
EXPECTED_HOOK_CMD="bash $HOME/.skill-fog/hooks/stop.sh"

GREEN='\033[0;32m'
YELLOW='\033[1;33m'
RED='\033[0;31m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
BOLD='\033[1m'
DIM='\033[2m'
NC='\033[0m'

info()    { echo -e "${BLUE}[skill-fog]${NC} $*"; }
success() { echo -e "${GREEN}✓${NC} $*"; }
warn()    { echo -e "${YELLOW}⚠${NC}  $*"; }
error()   { echo -e "${RED}✗${NC}  $*" >&2; }
dim()     { echo -e "${DIM}$*${NC}"; }

SOURCE_PATH="${BASH_SOURCE[0]}"
while [ -L "$SOURCE_PATH" ]; do
  SOURCE_DIR="$(cd -P "$(dirname "$SOURCE_PATH")" && pwd)"
  SOURCE_PATH="$(readlink "$SOURCE_PATH")"
  case "$SOURCE_PATH" in
    /*) ;;
    *) SOURCE_PATH="$SOURCE_DIR/$SOURCE_PATH" ;;
  esac
done
SCRIPT_DIR="$(cd -P "$(dirname "$SOURCE_PATH")" && pwd)"
if [ -f "$SCRIPT_DIR/../package.json" ]; then
  VERSION=$(python3 -c "import json,sys; print(json.load(sys.stdin).get('version','2.0.1'))" < "$SCRIPT_DIR/../package.json" 2>/dev/null || echo "2.0.1")
else
  VERSION="2.0.1"
fi

# ─────────────────────────────────────────────
# 의존성 확인
# ─────────────────────────────────────────────
check_jq_or_python() {
  if command -v jq &>/dev/null; then
    echo "jq"
  elif command -v python3 &>/dev/null; then
    echo "python3"
  else
    echo ""
  fi
}

JSON_TOOL=$(check_jq_or_python)

jq_query() {
  local filter="$1"
  local file="${2:-$PATTERNS_FILE}"
  case "$file" in
    "$PATTERNS_FILE"|"$PENDING_DIR"/*.json) ;;
    *)
      echo ""
      return 1
      ;;
  esac
  if [ "$JSON_TOOL" = "jq" ]; then
    jq -r "$filter" "$file" 2>/dev/null || echo ""
  elif [ "$JSON_TOOL" = "python3" ]; then
    SF_FILE="$file" python3 - <<'PYEOF'
import os, json, sys
file_path = os.environ.get('SF_FILE', '')
try:
    with open(file_path) as f:
        data = json.load(f)
    # 단순 패턴만 지원
    print(str(data))
except Exception:
    print('')
PYEOF
  else
    echo ""
  fi
}

pattern_status() {
  local pattern_id="$1"

  if [ ! -f "$PATTERNS_FILE" ]; then
    echo ""
    return
  fi

  if [ "$JSON_TOOL" = "jq" ]; then
    jq -r --arg id "$pattern_id" '.patterns[$id].status // ""' "$PATTERNS_FILE" 2>/dev/null || echo ""
  elif [ "$JSON_TOOL" = "python3" ]; then
    SF_PATTERN_ID="$pattern_id" SF_PATTERNS_FILE="$PATTERNS_FILE" python3 - <<'PYEOF'
import json, os

pattern_id = os.environ['SF_PATTERN_ID']
patterns_file = os.environ['SF_PATTERNS_FILE']

try:
    with open(patterns_file) as f:
        data = json.load(f)
except Exception:
    raise SystemExit(0)

print(data.get('patterns', {}).get(pattern_id, {}).get('status', ''))
PYEOF
  else
    echo ""
  fi
}

cleanup_stale_pending() {
  local pending_file="$1"
  local pattern_id
  pattern_id=$(basename "$pending_file" .json)

  case "$(pattern_status "$pattern_id")" in
    accepted|rejected)
      rm -f "$pending_file"
      return 0
      ;;
  esac

  return 1
}

repo_install_script() {
  local candidate="$SCRIPT_DIR/../install.sh"

  if [ -f "$candidate" ]; then
    local install_dir
    install_dir="$(cd "$(dirname "$candidate")" && pwd -P)"
    echo "$install_dir/install.sh"
  fi
}

print_install_remediation() {
  local install_script
  install_script="$(repo_install_script)"

  if [ -n "$install_script" ]; then
    echo -e "    bash $(printf '%q' "$install_script")"
  else
    echo -e "    Reinstall from a skill-fog repository checkout: cd /path/to/skill-fog && bash install.sh"
  fi
}

cmd_doctor_self_test_layout_probe() {
  local source_root="$SCRIPT_DIR/.."
  local source_install
  source_install="$(repo_install_script)"

  assert_probe_path() {
    local label="$1"
    local path="$2"
    if [ ! -e "$path" ]; then
      error "Installed layout probe failed: missing $label ($path)"
      return 1
    fi
  }

  assert_probe_executable() {
    local label="$1"
    local path="$2"
    if [ ! -x "$path" ]; then
      error "Installed layout probe failed: $label is not executable ($path)"
      return 1
    fi
  }

  resolve_command_path() {
    local path="$1"
    while [ -L "$path" ]; do
      local path_dir
      path_dir="$(cd -P "$(dirname "$path")" && pwd)"
      path="$(readlink "$path")"
      case "$path" in
        /*) ;;
        *) path="$path_dir/$path" ;;
      esac
    done
    local resolved_dir
    resolved_dir="$(cd -P "$(dirname "$path")" && pwd)"
    echo "$resolved_dir/$(basename "$path")"
  }

  if [ -z "$source_install" ]; then
    error "Installed layout probe failed: install.sh not found next to installed bin/."
    return 1
  fi

  assert_probe_path "install.sh" "$source_root/install.sh" || return 1
  assert_probe_path "uninstall.sh" "$source_root/uninstall.sh" || return 1
  assert_probe_executable "uninstall.sh" "$source_root/uninstall.sh" || return 1
  assert_probe_path "SKILL.md" "$source_root/SKILL.md" || return 1
  assert_probe_path "hooks/stop.sh" "$source_root/hooks/stop.sh" || return 1
  assert_probe_path "references/" "$source_root/references" || return 1

  local path_cli resolved_path_cli resolved_this
  path_cli="$(command -v skill-fog 2>/dev/null || true)"
  if [ -z "$path_cli" ]; then
    error "Installed layout probe failed: skill-fog is not available in PATH"
    return 1
  fi
  resolved_path_cli="$(resolve_command_path "$path_cli")"
  resolved_this="$(resolve_command_path "$SCRIPT_DIR/skill-fog")"
  if [ "$resolved_path_cli" != "$resolved_this" ]; then
    error "Installed layout probe failed: PATH skill-fog resolves to $resolved_path_cli, expected $resolved_this"
    return 1
  fi

  if ! skill-fog help | grep -q '../uninstall.sh'; then
    error "Installed layout probe failed: help output does not include the installed uninstall.sh path"
    return 1
  fi

  success "Installed layout probe passed: PATH CLI and runtime bundle verified."
}

cmd_doctor_self_test() {
  if [ "${SKILL_FOG_SELF_TEST_LAYOUT_PROBE:-}" = "1" ]; then
    cmd_doctor_self_test_layout_probe
    return
  fi

  local source_root="$SCRIPT_DIR/.."
  local source_install
  source_install="$(repo_install_script)"

  if [ -z "$source_install" ]; then
    error "Self-test requires a skill-fog repository checkout with install.sh next to bin/."
    return 1
  fi

  local temp_home
  temp_home="$(mktemp -d)"
  local test_repo="$temp_home/repo"
  local first_log="$temp_home/install-first.log"
  local second_log="$temp_home/install-second.log"
  local doctor_log="$temp_home/doctor.log"
  local layout_probe_log="$temp_home/layout-probe.log"
  local uninstall_first_log="$temp_home/uninstall-first.log"
  local uninstall_second_log="$temp_home/uninstall-second.log"
  local regular_cli_log="$temp_home/regular-cli-install.log"
  local unrelated_symlink_log="$temp_home/unrelated-symlink-uninstall.log"
  local fake_hook_install_log="$temp_home/fake-hook-install.log"
  local fake_hook_doctor_log="$temp_home/fake-hook-doctor.log"
  SELF_TEST_TEMP_HOME="$temp_home"

  cleanup_self_test() {
    if [ -n "${SELF_TEST_TEMP_HOME:-}" ]; then
      rm -rf "$SELF_TEST_TEMP_HOME"
    fi
  }
  trap cleanup_self_test EXIT

  mkdir -p "$test_repo/bin" "$test_repo/hooks"
  cp "$source_root/install.sh" "$test_repo/install.sh"
  cp "$source_root/uninstall.sh" "$test_repo/uninstall.sh"
  cp "$source_root/SKILL.md" "$test_repo/SKILL.md"
  cp "$source_root/bin/skill-fog" "$test_repo/bin/skill-fog"
  cp "$source_root/hooks/stop.sh" "$test_repo/hooks/stop.sh"
  cp -R "$source_root/references" "$test_repo/references"
  if [ -f "$source_root/package.json" ]; then
    cp "$source_root/package.json" "$test_repo/package.json"
  fi

  assert_path() {
    local label="$1"
    local path="$2"
    if [ ! -e "$path" ]; then
      error "Self-test failed: missing $label ($path)"
      return 1
    fi
  }

  assert_executable() {
    local label="$1"
    local path="$2"
    if [ ! -x "$path" ]; then
      error "Self-test failed: $label is not executable ($path)"
      return 1
    fi
  }

  assert_absent() {
    local label="$1"
    local path="$2"
    if [ -e "$path" ]; then
      error "Self-test failed: $label still exists ($path)"
      return 1
    fi
  }

  assert_file_content() {
    local label="$1"
    local path="$2"
    local expected="$3"
    local actual
    actual="$(cat "$path" 2>/dev/null || true)"
    if [ "$actual" != "$expected" ]; then
      error "Self-test failed: $label changed unexpectedly"
      return 1
    fi
  }

  assert_symlink() {
    local label="$1"
    local path="$2"
    if [ ! -L "$path" ]; then
      error "Self-test failed: $label is not a symlink ($path)"
      return 1
    fi
  }

  assert_installed_skill_references() {
    local skill_file="$temp_home/.claude/skills/skill-fog/SKILL.md"
    local skill_dir="$temp_home/.claude/skills/skill-fog"
    local refs

    refs=$(grep -oE 'references/[^)[:space:]]+' "$skill_file" 2>/dev/null | sort -u || true)
    if [ -z "$refs" ]; then
      error "Self-test failed: SKILL.md does not reference any runtime reference files"
      return 1
    fi

    local ref
    while IFS= read -r ref; do
      [ -n "$ref" ] || continue
      assert_path "runtime reference $ref" "$skill_dir/$ref" || return 1
    done <<< "$refs"
  }

  count_stop_hooks() {
    local settings_file="$1"
    local expected_cmd="$2"
    if command -v jq &>/dev/null; then
      jq --arg cmd "$expected_cmd" \
        '[.hooks.Stop[]?.hooks[]? | select(.type == "command" and .command == $cmd)] | length' \
        "$settings_file"
    else
      SF_SETTINGS_FILE="$settings_file" SF_EXPECTED_HOOK_CMD="$expected_cmd" python3 - <<'PYEOF'
import json, os

with open(os.environ['SF_SETTINGS_FILE']) as f:
    settings = json.load(f)

count = 0
expected = os.environ['SF_EXPECTED_HOOK_CMD']
for entry in settings.get('hooks', {}).get('Stop', []):
    for hook in entry.get('hooks', []):
        if hook.get('type') == 'command' and hook.get('command') == expected:
            count += 1
print(count)
PYEOF
    fi
  }

  run_regression_probes() {
    local regular_home="$temp_home/probe-regular-home"
    local unrelated_home="$temp_home/probe-unrelated-home"
    local fake_hook_home="$temp_home/probe-fake-hook-home"

    mkdir -p "$regular_home/.local/bin"
    printf 'external cli\n' > "$regular_home/.local/bin/skill-fog"
    if ! HOME="$regular_home" PATH="$regular_home/.local/bin:$PATH" bash "$test_repo/install.sh" >"$regular_cli_log" 2>&1; then
      error "Self-test failed: install.sh failed with pre-existing regular CLI"
      sed -n '1,160p' "$regular_cli_log" >&2
      return 1
    fi
    assert_file_content "pre-existing regular CLI" "$regular_home/.local/bin/skill-fog" "external cli" || return 1
    assert_absent "unexpected skill-fog.bak" "$regular_home/.local/bin/skill-fog.bak" || return 1

    mkdir -p "$unrelated_home/.local/bin" "$unrelated_home/other/bin"
    printf '#!/usr/bin/env bash\n' > "$unrelated_home/other/bin/skill-fog"
    ln -s "$unrelated_home/other/bin/skill-fog" "$unrelated_home/.local/bin/skill-fog"
    if ! HOME="$unrelated_home" PATH="$unrelated_home/.local/bin:$PATH" bash "$test_repo/uninstall.sh" --yes --keep-data >"$unrelated_symlink_log" 2>&1; then
      error "Self-test failed: uninstall.sh failed with unrelated skill-fog symlink"
      sed -n '1,160p' "$unrelated_symlink_log" >&2
      return 1
    fi
    assert_symlink "unrelated skill-fog symlink" "$unrelated_home/.local/bin/skill-fog" || return 1

    if ! HOME="$fake_hook_home" PATH="$fake_hook_home/.local/bin:$PATH" bash "$test_repo/install.sh" >"$fake_hook_install_log" 2>&1; then
      error "Self-test failed: install.sh failed for fake hook probe"
      sed -n '1,160p' "$fake_hook_install_log" >&2
      return 1
    fi
    cat > "$fake_hook_home/.claude/settings.json" <<'JSONEOF'
{"hooks":{"Stop":[{"matcher":"","hooks":[{"type":"command","command":"echo skill-fog"}]}]}}
JSONEOF
    HOME="$fake_hook_home" PATH="$fake_hook_home/.local/bin:$PATH" bash "$test_repo/bin/skill-fog" doctor >"$fake_hook_doctor_log" 2>&1 || true
    if grep -q "0 failures" "$fake_hook_doctor_log"; then
      error "Self-test failed: fake Stop hook containing skill-fog was accepted as healthy"
      sed -n '1,200p' "$fake_hook_doctor_log" >&2
      return 1
    fi
    if ! grep -q "Stop hook registered in settings.json" "$fake_hook_doctor_log"; then
      error "Self-test failed: fake Stop hook probe did not exercise hook detection"
      sed -n '1,200p' "$fake_hook_doctor_log" >&2
      return 1
    fi
  }

  echo ""
  info "Running skill-fog self-test with temporary HOME: $temp_home"

  run_regression_probes || return 1

  if ! HOME="$temp_home" PATH="$temp_home/.local/bin:$PATH" bash "$test_repo/install.sh" >"$first_log" 2>&1; then
    error "Self-test failed: first install.sh run failed"
    sed -n '1,160p' "$first_log" >&2
    return 1
  fi

  assert_path "~/.skill-fog directory" "$temp_home/.skill-fog" || return 1
  assert_path "~/.skill-fog/pending directory" "$temp_home/.skill-fog/pending" || return 1
  assert_path "~/.skill-fog/patterns.json" "$temp_home/.skill-fog/patterns.json" || return 1
  assert_path "~/.skill-fog/hooks/stop.sh" "$temp_home/.skill-fog/hooks/stop.sh" || return 1
  assert_executable "~/.skill-fog/hooks/stop.sh" "$temp_home/.skill-fog/hooks/stop.sh" || return 1
  assert_path "~/.skill-fog/install.sh" "$temp_home/.skill-fog/install.sh" || return 1
  assert_path "~/.skill-fog/uninstall.sh" "$temp_home/.skill-fog/uninstall.sh" || return 1
  assert_executable "~/.skill-fog/uninstall.sh" "$temp_home/.skill-fog/uninstall.sh" || return 1
  assert_path "~/.skill-fog/SKILL.md" "$temp_home/.skill-fog/SKILL.md" || return 1
  assert_path "~/.skill-fog/references" "$temp_home/.skill-fog/references" || return 1
  assert_path "~/.claude/skills/skill-fog/SKILL.md" "$temp_home/.claude/skills/skill-fog/SKILL.md" || return 1
  assert_installed_skill_references || return 1
  assert_path "~/.claude/settings.json" "$temp_home/.claude/settings.json" || return 1
  assert_path "~/.local/bin/skill-fog" "$temp_home/.local/bin/skill-fog" || return 1

  if ! HOME="$temp_home" PATH="$temp_home/.local/bin:$PATH" SKILL_FOG_SELF_TEST_LAYOUT_PROBE=1 skill-fog doctor --self-test >"$layout_probe_log" 2>&1; then
    error "Self-test failed: installed PATH skill-fog layout probe failed"
    sed -n '1,200p' "$layout_probe_log" >&2
    return 1
  fi

  if ! HOME="$temp_home" PATH="$temp_home/.local/bin:$PATH" skill-fog doctor >"$doctor_log" 2>&1; then
    error "Self-test failed: doctor exited nonzero"
    sed -n '1,200p' "$doctor_log" >&2
    return 1
  fi

  if ! grep -q "0 failures" "$doctor_log"; then
    error "Self-test failed: doctor did not report 0 failures"
    sed -n '1,200p' "$doctor_log" >&2
    return 1
  fi

  if ! HOME="$temp_home" PATH="$temp_home/.local/bin:$PATH" bash "$test_repo/install.sh" >"$second_log" 2>&1; then
    error "Self-test failed: second install.sh run failed"
    sed -n '1,160p' "$second_log" >&2
    return 1
  fi

  local hook_count
  hook_count="$(count_stop_hooks "$temp_home/.claude/settings.json" "bash $temp_home/.skill-fog/hooks/stop.sh")"
  if [ "$hook_count" != "1" ]; then
    error "Self-test failed: expected 1 skill-fog Stop hook after reinstall, found $hook_count"
    return 1
  fi

  if ! HOME="$temp_home" PATH="$temp_home/.local/bin:$PATH" bash "$test_repo/uninstall.sh" --yes --keep-data >"$uninstall_first_log" 2>&1; then
    error "Self-test failed: first uninstall.sh run failed"
    sed -n '1,200p' "$uninstall_first_log" >&2
    return 1
  fi

  if ! HOME="$temp_home" PATH="$temp_home/.local/bin:$PATH" bash "$test_repo/uninstall.sh" --yes --keep-data >"$uninstall_second_log" 2>&1; then
    error "Self-test failed: second uninstall.sh run failed"
    sed -n '1,200p' "$uninstall_second_log" >&2
    return 1
  fi

  assert_absent "~/.claude/skills/skill-fog directory" "$temp_home/.claude/skills/skill-fog" || return 1
  assert_absent "~/.local/bin/skill-fog" "$temp_home/.local/bin/skill-fog" || return 1
  assert_path "~/.skill-fog directory after uninstall --keep-data" "$temp_home/.skill-fog" || return 1

  hook_count="$(count_stop_hooks "$temp_home/.claude/settings.json" "bash $temp_home/.skill-fog/hooks/stop.sh")"
  if [ "$hook_count" != "0" ]; then
    error "Self-test failed: expected 0 skill-fog Stop hooks after uninstall, found $hook_count"
    return 1
  fi

  success "Self-test passed: install, doctor, reinstall, and idempotent uninstall verified."
}

pending_count() {
  local count=0

  if [ ! -d "$PENDING_DIR" ]; then
    echo "0"
    return
  fi

  local f
  for f in "$PENDING_DIR"/*.json; do
    [ -e "$f" ] || continue
    if cleanup_stale_pending "$f"; then
      continue
    fi
    count=$((count + 1))
  done

  echo "$count"
}

mark_pending_review_choice() {
  local pending_file="$1"
  local desired_type="$2"
  local reviewed_at
  reviewed_at=$(date -u +%Y-%m-%dT%H:%M:%SZ)

  if [ "$JSON_TOOL" = "jq" ]; then
    jq --arg desired_type "$desired_type" --arg reviewed_at "$reviewed_at" \
      '.desired_type = $desired_type | .reviewed_at = $reviewed_at' \
      "$pending_file" > "${pending_file}.tmp" 2>/dev/null \
      && mv "${pending_file}.tmp" "$pending_file"
  elif [ "$JSON_TOOL" = "python3" ]; then
    SF_FILE="$pending_file" SF_DESIRED_TYPE="$desired_type" SF_REVIEWED_AT="$reviewed_at" python3 - <<'PYEOF'
import json, os

pending_file = os.environ['SF_FILE']
desired_type = os.environ['SF_DESIRED_TYPE']
reviewed_at = os.environ['SF_REVIEWED_AT']

try:
    with open(pending_file) as f:
        data = json.load(f)
except Exception:
    raise SystemExit(0)

data['desired_type'] = desired_type
data['reviewed_at'] = reviewed_at
tmp_file = pending_file + '.tmp'
with open(tmp_file, 'w') as f:
    json.dump(data, f, indent=2, ensure_ascii=False)
    f.write('\n')
os.replace(tmp_file, pending_file)
PYEOF
  fi
}

# ─────────────────────────────────────────────
# 서브커맨드: status
# ─────────────────────────────────────────────
cmd_status() {
  echo ""
  echo -e "${BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
  echo -e "${BOLD}  skill-fog — Pattern Status${NC}"
  echo -e "${BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
  echo ""

  if [ ! -f "$PATTERNS_FILE" ]; then
    warn "No patterns.json found. Run some Claude Code sessions first."
    return
  fi

  if [ "$JSON_TOOL" = "jq" ]; then
    local total active pending accepted rejected

    total=$(jq '.patterns | length' "$PATTERNS_FILE" 2>/dev/null || echo "0")
    active=$(jq '[.patterns[] | select(.status == "active")] | length' "$PATTERNS_FILE" 2>/dev/null || echo "0")
    pending=$(pending_count)
    accepted=$(jq '[.patterns[] | select(.status == "accepted")] | length' "$PATTERNS_FILE" 2>/dev/null || echo "0")
    rejected=$(jq '[.patterns[] | select(.status == "rejected")] | length' "$PATTERNS_FILE" 2>/dev/null || echo "0")

    echo -e "  Total patterns : ${BOLD}$total${NC}"
    echo -e "  Active         : ${CYAN}$active${NC}"
    echo -e "  Pending review : ${YELLOW}$pending${NC}"
    echo -e "  Accepted       : ${GREEN}$accepted${NC}"
    echo -e "  Rejected       : ${RED}$rejected${NC}"
    echo ""

    # 상위 패턴 테이블
    echo -e "${BOLD}  Top patterns (by count):${NC}"
    echo -e "  ${DIM}$(printf '%-14s %-6s %-8s %-10s %s' 'ID' 'Count' 'Sessions' 'Status' 'Pattern')${NC}"
    echo -e "  ${DIM}$(printf '%-14s %-6s %-8s %-10s %s' '──────────────' '──────' '────────' '──────────' '───────────────────────────────────────────────────')${NC}"

    jq -r '
      .patterns | to_entries
      | sort_by(-.value.count)
      | .[:15][]
      | [.key[0:12], (.value.count | tostring), (.value.sessions | length | tostring), .value.status, (.value.canonical[0:50])]
      | @tsv
    ' "$PATTERNS_FILE" 2>/dev/null | while IFS=$'\t' read -r id count sessions status canonical; do
      local status_color="$NC"
      case "$status" in
        "active")   status_color="$CYAN" ;;
        "proposed") status_color="$YELLOW" ;;
        "accepted") status_color="$GREEN" ;;
        "rejected") status_color="$RED" ;;
      esac
      printf "  %-14s %-6s %-8s ${status_color}%-10s${NC} %s\n" \
        "$id" "$count" "$sessions" "$status" "$canonical"
    done

  elif [ "$JSON_TOOL" = "python3" ]; then
    SF_PATTERNS_FILE="$PATTERNS_FILE" SF_PENDING_COUNT="$(pending_count)" python3 - <<'PYEOF'
import json, os

patterns_file = os.environ['SF_PATTERNS_FILE']
try:
    with open(patterns_file) as f:
        data = json.load(f)
except Exception:
    print("  Error reading patterns.json")
    exit(0)

patterns = data.get('patterns', {})
total = len(patterns)
counts = {'active': 0, 'accepted': 0, 'rejected': 0}
for p in patterns.values():
    counts[p.get('status', 'active')] = counts.get(p.get('status', 'active'), 0) + 1
pending = int(os.environ.get('SF_PENDING_COUNT', '0'))

print(f"  Total patterns : {total}")
print(f"  Active         : {counts.get('active', 0)}")
print(f"  Pending review : {pending}")
print(f"  Accepted       : {counts.get('accepted', 0)}")
print(f"  Rejected       : {counts.get('rejected', 0)}")
print()
print("  Top patterns (by count):")
print(f"  {'ID':<14} {'Count':<6} {'Sessions':<9} {'Status':<11} {'Pattern'}")
print(f"  {'-'*14} {'-'*6} {'-'*8} {'-'*10} {'-'*50}")

sorted_p = sorted(patterns.items(), key=lambda x: -x[1].get('count', 0))
for pid, p in sorted_p[:15]:
    print(f"  {pid[:12]:<14} {p.get('count',0):<6} {len(p.get('sessions',[])):<9} {p.get('status',''):<11} {p.get('canonical','')[:50]}")
PYEOF
  else
    error "No JSON tool available (jq or python3 required)"
  fi

  echo ""

  # pending 파일 수
  local pending_count
  pending_count=$(pending_count)

  if [ "${pending_count:-0}" -gt 0 ]; then
    echo -e "  ${YELLOW}${BOLD}$pending_count pattern(s) pending review!${NC} Run: ${CYAN}skill-fog review${NC}"
    echo ""
  fi
}

# ─────────────────────────────────────────────
# 서브커맨드: review
# ─────────────────────────────────────────────
cmd_review() {
  echo ""
  echo -e "${BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
  echo -e "${BOLD}  skill-fog — Review Pending Patterns${NC}"
  echo -e "${BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
  echo ""

  local pending_files pending_total
  pending_total=$(pending_count)
  pending_files=$(find "$PENDING_DIR" -maxdepth 1 -name "*.json" -type f 2>/dev/null | sort || true)

  if [ "${pending_total:-0}" -eq 0 ]; then
    success "No pending patterns to review."
    echo ""
    dim "  Patterns are promoted to pending when count >= 3 across >= 2 sessions."
    echo ""
    return
  fi

  local count=0
  for f in $pending_files; do
    if cleanup_stale_pending "$f"; then
      continue
    fi

    count=$((count + 1))
    local pattern_id
    pattern_id=$(basename "$f" .json)

    echo -e "  ${BOLD}[$count] Pattern: ${CYAN}$pattern_id${NC}"

    if [ "$JSON_TOOL" = "jq" ]; then
      local canonical freq session_count examples

      canonical=$(jq -r '.canonical' "$f" 2>/dev/null || echo "(unknown)")
      freq=$(jq -r '.count' "$f" 2>/dev/null || echo "?")
      session_count=$(jq -r '.sessions | length' "$f" 2>/dev/null || echo "?")

      echo -e "  ${DIM}Pattern: $canonical${NC}"
      echo -e "  Seen ${BOLD}$freq${NC} times across ${BOLD}$session_count${NC} sessions"
      echo ""
      echo -e "  ${DIM}Examples:${NC}"
      jq -r '.examples[]?' "$f" 2>/dev/null | head -3 | while IFS= read -r ex; do
        echo -e "    ${DIM}• $ex${NC}"
      done

    elif [ "$JSON_TOOL" = "python3" ]; then
      SF_FILE="$f" python3 - <<'PYEOF'
import json, os
try:
    with open(os.environ['SF_FILE']) as fp:
        p = json.load(fp)
    print(f"  Pattern: {p.get('canonical','?')[:80]}")
    print(f"  Seen {p.get('count','?')} times across {len(p.get('sessions',[]))} sessions")
    print()
    print("  Examples:")
    for ex in p.get('examples', [])[:3]:
        print(f"    • {ex[:100]}")
except Exception as e:
    print(f"  (Error reading pattern: {e})")
PYEOF
    fi

    echo ""
    echo -e "  Actions: ${GREEN}[s]${NC}kill  ${BLUE}[c]${NC}ommand  ${YELLOW}[a]${NC}gent  ${DIM}[k]${NC}eep (later)  ${RED}[r]${NC}eject"
    echo -n "  Choice: "

    local choice
    read -r choice

    case "$choice" in
      s|skill)
        mark_pending_review_choice "$f" "skill"
        success "Marked for skill creation. Open Claude Code and run /skill-fog to generate."
        ;;
      c|command)
        mark_pending_review_choice "$f" "command"
        success "Marked for command creation. Open Claude Code and run /skill-fog to generate."
        ;;
      a|agent)
        mark_pending_review_choice "$f" "agent"
        success "Marked for agent creation. Open Claude Code and run /skill-fog to generate."
        ;;
      k|keep|later|"")
        info "Keeping for later review."
        ;;
      r|reject)
        rm "$f"
        # status를 rejected로 업데이트
        if [ "$JSON_TOOL" = "jq" ] && [ -f "$PATTERNS_FILE" ]; then
          jq --arg id "$pattern_id" '.patterns[$id].status = "rejected"' \
            "$PATTERNS_FILE" > "${PATTERNS_FILE}.tmp" 2>/dev/null \
            && mv "${PATTERNS_FILE}.tmp" "$PATTERNS_FILE"
        elif [ "$JSON_TOOL" = "python3" ] && [ -f "$PATTERNS_FILE" ]; then
          SF_PATTERN_ID="$pattern_id" SF_PATTERNS_FILE="$PATTERNS_FILE" python3 - <<'PYEOF'
import json, os

pattern_id = os.environ['SF_PATTERN_ID']
patterns_file = os.environ['SF_PATTERNS_FILE']

try:
    with open(patterns_file) as f:
        data = json.load(f)
except Exception:
    raise SystemExit(0)

pattern = data.get('patterns', {}).get(pattern_id)
if pattern is not None:
    pattern['status'] = 'rejected'
    tmp_file = patterns_file + '.tmp'
    with open(tmp_file, 'w') as f:
        json.dump(data, f, indent=2, ensure_ascii=False)
        f.write('\n')
    os.replace(tmp_file, patterns_file)
PYEOF
        fi
        warn "Pattern rejected and removed."
        ;;
      *)
        info "Unknown choice, keeping for later."
        ;;
    esac

    echo ""
    echo -e "  ${DIM}─────────────────────────────────────────────────────────────${NC}"
    echo ""
  done

  echo -e "  Review complete. Run ${CYAN}/skill-fog${NC} in Claude Code to generate artifacts."
  echo ""
}

# ─────────────────────────────────────────────
# 서브커맨드: list
# ─────────────────────────────────────────────
cmd_list() {
  echo ""
  echo -e "${BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
  echo -e "${BOLD}  skill-fog — Generated Skills / Commands / Agents${NC}"
  echo -e "${BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
  echo ""

  # Skills
  echo -e "  ${BOLD}${GREEN}Skills${NC} (${CLAUDE_DIR}/skills/):"
  if [ -d "$CLAUDE_DIR/skills" ]; then
    local files
    files=$(find "$CLAUDE_DIR/skills" -name "*.md" -type f 2>/dev/null | sort)
    if [ -z "$files" ]; then
      dim "    (none)"
    else
      echo "$files" | while read -r f; do
        local rel="${f#"$CLAUDE_DIR/skills/"}"
        echo -e "    ${DIM}•${NC} $rel"
      done
    fi
  else
    dim "    (directory not found)"
  fi
  echo ""

  # Commands
  echo -e "  ${BOLD}${BLUE}Commands${NC} (${CLAUDE_DIR}/commands/):"
  if [ -d "$CLAUDE_DIR/commands" ]; then
    local cmd_files
    cmd_files=$(find "$CLAUDE_DIR/commands" -name "*.md" -type f 2>/dev/null | sort)
    if [ -z "$cmd_files" ]; then
      dim "    (none)"
    else
      echo "$cmd_files" | while read -r f; do
        local rel="${f#"$CLAUDE_DIR/commands/"}"
        echo -e "    ${DIM}•${NC} $rel"
      done
    fi
  else
    dim "    (directory not found)"
  fi
  echo ""

  # Agents
  echo -e "  ${BOLD}${YELLOW}Agents${NC} (${CLAUDE_DIR}/agents/):"
  if [ -d "$CLAUDE_DIR/agents" ]; then
    local agent_files
    agent_files=$(find "$CLAUDE_DIR/agents" -name "*.md" -type f 2>/dev/null | sort)
    if [ -z "$agent_files" ]; then
      dim "    (none)"
    else
      echo "$agent_files" | while read -r f; do
        local rel="${f#"$CLAUDE_DIR/agents/"}"
        echo -e "    ${DIM}•${NC} $rel"
      done
    fi
  else
    dim "    (directory not found)"
  fi

  echo ""
}

# ─────────────────────────────────────────────
# 서브커맨드: clean
# ─────────────────────────────────────────────
cmd_clean() {
  echo ""
  echo -e "${BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
  echo -e "${BOLD}  skill-fog — Clean Old Patterns${NC}"
  echo -e "${BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
  echo ""

  if [ ! -f "$PATTERNS_FILE" ]; then
    info "No patterns.json found."
    return
  fi

  local cleaned=0

  if [ "$JSON_TOOL" = "jq" ]; then
    # 30일 이상 된 패턴 제거 (last_seen 기준)
    local cutoff_date
    cutoff_date=$(date -u -d "30 days ago" +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || \
                  date -u -v-30d +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || \
                  echo "")

    if [ -n "$cutoff_date" ]; then
      local old_count
      old_count=$(jq --arg cutoff "$cutoff_date" \
        '[.patterns[] | select(.last_seen < $cutoff or .status == "rejected")] | length' \
        "$PATTERNS_FILE" 2>/dev/null || echo "0")

      if [ "${old_count:-0}" -gt 0 ]; then
        echo -e "  Found ${YELLOW}$old_count${NC} stale/rejected pattern(s) to remove."
        echo -n "  Proceed? [y/N] "
        local confirm
        read -r confirm
        case "$confirm" in
          [Yy]*)
            jq --arg cutoff "$cutoff_date" \
              'del(.patterns[] | select(.last_seen < $cutoff or .status == "rejected"))' \
              "$PATTERNS_FILE" > "${PATTERNS_FILE}.tmp" 2>/dev/null \
              && mv "${PATTERNS_FILE}.tmp" "$PATTERNS_FILE"
            success "Removed $old_count stale/rejected patterns."
            cleaned=$old_count
            ;;
          *)
            info "Cancelled."
            ;;
        esac
      else
        success "No stale patterns found. patterns.json is clean."
      fi
    else
      # 날짜 계산 안 되면 rejected만 제거
      local rejected_count
      rejected_count=$(jq '[.patterns[] | select(.status == "rejected")] | length' \
        "$PATTERNS_FILE" 2>/dev/null || echo "0")

      if [ "${rejected_count:-0}" -gt 0 ]; then
        jq 'del(.patterns[] | select(.status == "rejected"))' \
          "$PATTERNS_FILE" > "${PATTERNS_FILE}.tmp" 2>/dev/null \
          && mv "${PATTERNS_FILE}.tmp" "$PATTERNS_FILE"
        success "Removed $rejected_count rejected patterns."
        cleaned=$rejected_count
      else
        success "No rejected patterns to clean."
      fi
    fi

  elif [ "$JSON_TOOL" = "python3" ]; then
    SF_PATTERNS_FILE="$PATTERNS_FILE" python3 - <<'PYEOF'
import json, datetime, os, sys

patterns_file = os.environ['SF_PATTERNS_FILE']
cutoff = datetime.datetime.utcnow() - datetime.timedelta(days=30)

try:
    with open(patterns_file) as f:
        data = json.load(f)
except Exception:
    print("  Error reading patterns.json")
    sys.exit(0)

patterns = data.get('patterns', {})
to_remove = []

for pid, p in patterns.items():
    last_seen_str = p.get('last_seen', '')
    status = p.get('status', '')
    if status == 'rejected':
        to_remove.append(pid)
        continue
    try:
        last_seen = datetime.datetime.strptime(last_seen_str, '%Y-%m-%dT%H:%M:%SZ')
        if last_seen < cutoff:
            to_remove.append(pid)
    except Exception:
        pass

if to_remove:
    print(f"  Found {len(to_remove)} stale/rejected patterns to remove.")
    answer = input("  Proceed? [y/N] ").strip().lower()
    if answer == 'y':
        for pid in to_remove:
            del patterns[pid]
        tmp_file = patterns_file + '.tmp'
        with open(tmp_file, 'w') as f:
            json.dump(data, f, indent=2, ensure_ascii=False)
            f.write('\n')
        os.replace(tmp_file, patterns_file)
        print(f"  Removed {len(to_remove)} patterns.")
    else:
        print("  Cancelled.")
else:
    print("  No stale patterns found.")
PYEOF
  else
    error "No JSON tool available."
  fi

  # 오래된 로그 파일 정리 (30일 이상)
  find "$SKILL_FOG_DIR/logs" -name "*.log" -mtime +30 -delete 2>/dev/null || true
  info "Cleaned log files older than 30 days."

  echo ""
}

# ─────────────────────────────────────────────
# 서브커맨드: doctor
# ─────────────────────────────────────────────
cmd_doctor() {
  if [ "${1:-}" = "--self-test" ]; then
    cmd_doctor_self_test
    return
  fi

  echo ""
  echo -e "${BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
  echo -e "${BOLD}  skill-fog — Installation Diagnostics${NC}"
  echo -e "${BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
  echo ""

  local ok=0
  local warn_count=0
  local fail=0

  check_item() {
    local label="$1"
    local status="$2"  # ok, warn, fail
    local detail="${3:-}"
    case "$status" in
      ok)   echo -e "  ${GREEN}✓${NC}  $label${detail:+  ${DIM}($detail)${NC}}"; ok=$((ok+1)) ;;
      warn) echo -e "  ${YELLOW}⚠${NC}  $label${detail:+  ${DIM}($detail)${NC}}"; warn_count=$((warn_count+1)) ;;
      fail) echo -e "  ${RED}✗${NC}  $label${detail:+  ${DIM}($detail)${NC}}"; fail=$((fail+1)) ;;
    esac
  }

  # jq
  if command -v jq &>/dev/null; then
    check_item "jq installed" "ok" "$(jq --version)"
  else
    check_item "jq installed" "warn" "python3 fallback active"
  fi

  # python3
  if command -v python3 &>/dev/null; then
    check_item "python3 available" "ok" "$(python3 --version 2>&1)"
  else
    check_item "python3 available" "warn" "not found"
  fi

  # ~/.skill-fog/
  if [ -d "$SKILL_FOG_DIR" ]; then
    check_item "~/.skill-fog/ directory" "ok"
  else
    check_item "~/.skill-fog/ directory" "fail" "missing — run install.sh"
  fi

  # ~/.skill-fog/pending/
  if [ -d "$PENDING_DIR" ]; then
    check_item "~/.skill-fog/pending/ directory" "ok"
  else
    check_item "~/.skill-fog/pending/ directory" "fail" "missing"
  fi

  # patterns.json
  if [ -f "$PATTERNS_FILE" ]; then
    check_item "~/.skill-fog/patterns.json" "ok"
  else
    check_item "~/.skill-fog/patterns.json" "warn" "will be created on first session"
  fi

  # stop.sh hook
  if [ -f "$SKILL_FOG_DIR/hooks/stop.sh" ]; then
    if [ -x "$SKILL_FOG_DIR/hooks/stop.sh" ]; then
      check_item "stop.sh hook (executable)" "ok"
    else
      check_item "stop.sh hook (executable)" "fail" "not executable — run: chmod +x ~/.skill-fog/hooks/stop.sh"
    fi
  else
    check_item "stop.sh hook" "fail" "missing — run install.sh"
  fi

  # SKILL.md
  if [ -f "$CLAUDE_DIR/skills/skill-fog/SKILL.md" ]; then
    check_item "SKILL.md installed" "ok"

    skill_refs=$(grep -oE 'references/[^)[:space:]]+' "$CLAUDE_DIR/skills/skill-fog/SKILL.md" 2>/dev/null | sort -u || true)
    if [ -n "$skill_refs" ]; then
      local missing_refs=0
      local ref
      while IFS= read -r ref; do
        [ -n "$ref" ] || continue
        if [ ! -f "$CLAUDE_DIR/skills/skill-fog/$ref" ]; then
          missing_refs=$((missing_refs + 1))
        fi
      done <<< "$skill_refs"

      if [ "$missing_refs" -eq 0 ]; then
        check_item "SKILL.md runtime references installed" "ok"
      else
        check_item "SKILL.md runtime references installed" "fail" "$missing_refs missing — run install.sh"
      fi
    else
      check_item "SKILL.md runtime references installed" "warn" "no references/*.md links found"
    fi
  else
    check_item "SKILL.md installed" "fail" "missing — run install.sh"
  fi

  # settings.json hook registration
  local settings_file="$CLAUDE_DIR/settings.json"
  if [ -f "$settings_file" ]; then
    local hook_registered=""
    if command -v jq &>/dev/null; then
      hook_registered=$(jq -r \
        --arg cmd "$EXPECTED_HOOK_CMD" \
        '.hooks.Stop[]?.hooks[]? | select(.type == "command" and .command == $cmd) | .command' \
        "$settings_file" 2>/dev/null || echo "")
    elif command -v python3 &>/dev/null; then
      hook_registered=$(SF_SETTINGS_FILE="$settings_file" SF_EXPECTED_HOOK_CMD="$EXPECTED_HOOK_CMD" python3 -c "
import json, os
with open(os.environ['SF_SETTINGS_FILE']) as f:
    s = json.load(f)
expected = os.environ['SF_EXPECTED_HOOK_CMD']
for entry in s.get('hooks', {}).get('Stop', []):
    for h in entry.get('hooks', []):
        if h.get('type') == 'command' and h.get('command') == expected:
            print(h['command'])
" 2>/dev/null || echo "")
    fi

    if [ -n "$hook_registered" ]; then
      check_item "Stop hook registered in settings.json" "ok"
    else
      check_item "Stop hook registered in settings.json" "fail" "not found — run install.sh"
    fi
  else
    check_item "settings.json exists" "warn" "will be created by install.sh"
  fi

  # CLI in PATH
  if command -v skill-fog &>/dev/null; then
    check_item "skill-fog in PATH" "ok" "$(command -v skill-fog)"
  else
    check_item "skill-fog in PATH" "warn" "not in PATH — add ~/.local/bin to PATH"
  fi

  echo ""
  echo -e "  ${BOLD}Summary:${NC} ${GREEN}$ok ok${NC}  ${YELLOW}$warn_count warnings${NC}  ${RED}$fail failures${NC}"
  echo ""

  if [ "$fail" -gt 0 ]; then
    echo -e "  ${RED}Installation issues detected.${NC} Re-run install.sh to fix:"
    print_install_remediation
    echo ""
  fi
}

# ─────────────────────────────────────────────
# 서브커맨드: help
# ─────────────────────────────────────────────
cmd_help() {
  echo ""
  echo -e "${BOLD}skill-fog${NC} v$VERSION — Claude Code pattern detector"
  echo ""
  echo -e "${BOLD}USAGE${NC}"
  echo -e "  skill-fog <command>"
  echo ""
  echo -e "${BOLD}COMMANDS${NC}"
  echo -e "  ${CYAN}status${NC}    Show pattern statistics and pending review count"
  echo -e "  ${CYAN}review${NC}    Review pending patterns and choose action"
  echo -e "  ${CYAN}list${NC}      List generated skills, commands, and agents"
  echo -e "  ${CYAN}clean${NC}     Remove rejected/stale patterns and old logs"
  echo -e "  ${CYAN}doctor${NC}    Diagnose installation health"
  echo -e "  ${CYAN}doctor --self-test${NC}  Run an isolated temp-HOME install self-test"
  echo -e "  ${CYAN}help${NC}      Show this help message"
  echo ""
  echo -e "${BOLD}HOW IT WORKS${NC}"
  echo -e "  1. Claude Code session ends → stop.sh analyzes message patterns"
  echo -e "  2. Pattern seen ≥3 times across ≥2 sessions → moved to pending"
  echo -e "  3. Session start → skill-fog proposes skill/command/agent creation"
  echo -e "  4. You confirm → file generated in ~/.claude/skills|commands|agents"
  echo ""
  echo -e "${BOLD}FILES${NC}"
  echo -e "  ${DIM}~/.skill-fog/patterns.json${NC}   Pattern database"
  echo -e "  ${DIM}~/.skill-fog/pending/*.json${NC}  Patterns awaiting review"
  echo -e "  ${DIM}~/.skill-fog/logs/*.log${NC}      Session processing logs"
  echo -e "  ${DIM}~/.skill-fog/hooks/stop.sh${NC}   Claude Code Stop hook"
  echo -e "  ${DIM}~/.claude/skills/skill-fog/${NC}  SKILL.md for Claude"
  echo ""
  echo -e "${BOLD}UNINSTALL${NC}"
  echo -e "  bash \$(dirname \$(readlink -f \$(which skill-fog)))/../uninstall.sh"
  echo ""
}

# ─────────────────────────────────────────────
# MAIN
# ─────────────────────────────────────────────
main() {
  local cmd="${1:-help}"
  shift || true

  case "$cmd" in
    status)  cmd_status "$@" ;;
    review)  cmd_review "$@" ;;
    list)    cmd_list "$@" ;;
    clean)   cmd_clean "$@" ;;
    doctor)  cmd_doctor "$@" ;;
    help|-h|--help) cmd_help ;;
    version|--version|-v) echo "skill-fog v$VERSION" ;;
    *)
      error "Unknown command: $cmd"
      echo ""
      cmd_help
      exit 1
      ;;
  esac
}

main "$@"
