#!/bin/bash

set -euo pipefail

if [[ "$(uname -s)" != "Darwin" ]]; then
  echo "這支腳本只支援 macOS。" >&2
  exit 1
fi

if [[ "$#" -gt 1 ]]; then
  echo "用法：$0 [新的報告檔路徑]" >&2
  exit 1
fi

output_path="${1:-./mac-memory-safe-report-$(date +%Y%m%d-%H%M%S)-$$.md}"
output_dir="$(dirname "$output_path")"
output_name="$(basename "$output_path")"

if [[ ! -d "$output_dir" ]]; then
  echo "輸出資料夾不存在：${output_dir}" >&2
  exit 1
fi

if [[ -e "$output_path" || -L "$output_path" ]]; then
  echo "為避免覆寫既有資料，請改用新的報告檔名：${output_name}" >&2
  exit 1
fi

tmp_report="$(mktemp "${output_path}.tmp.XXXXXX")"
tmp_families="$(mktemp)"
trap '/bin/unlink "$tmp_report" 2>/dev/null || true; /bin/unlink "$tmp_families" 2>/dev/null || true' EXIT

physical_bytes="$(/usr/sbin/sysctl -n hw.memsize)"
page_bytes="$(/usr/sbin/sysctl -n hw.pagesize)"
physical_gib="$(awk -v n="$physical_bytes" 'BEGIN { printf "%.1f", n / 1073741824 }')"
physical_mib="$(awk -v n="$physical_bytes" 'BEGIN { printf "%.0f", n / 1048576 }')"

vm_snapshot="$(/usr/bin/vm_stat)"
compressed_pages="$(printf '%s\n' "$vm_snapshot" | awk '/Pages occupied by compressor/ { gsub(/\./, "", $NF); print $NF }')"
compressed_gib="$(awk -v p="${compressed_pages:-0}" -v s="$page_bytes" 'BEGIN { printf "%.2f", p * s / 1073741824 }')"

swap_snapshot="$(/usr/sbin/sysctl vm.swapusage)"
swap_total_mib="$(printf '%s\n' "$swap_snapshot" | awk '{ for (i=1; i<=NF; i++) if ($i=="total") { v=$(i+2); gsub(/M/, "", v); print v } }')"
swap_used_mib="$(printf '%s\n' "$swap_snapshot" | awk '{ for (i=1; i<=NF; i++) if ($i=="used") { v=$(i+2); gsub(/M/, "", v); print v } }')"
swap_free_mib="$(printf '%s\n' "$swap_snapshot" | awk '{ for (i=1; i<=NF; i++) if ($i=="free") { v=$(i+2); gsub(/M/, "", v); print v } }')"

swap_total_gib="$(awk -v n="${swap_total_mib:-0}" 'BEGIN { printf "%.2f", n / 1024 }')"
swap_used_gib="$(awk -v n="${swap_used_mib:-0}" 'BEGIN { printf "%.2f", n / 1024 }')"
swap_free_gib="$(awk -v n="${swap_free_mib:-0}" 'BEGIN { printf "%.2f", n / 1024 }')"

free_percent="$(/usr/bin/memory_pressure -Q 2>/dev/null | awk -F: '/System-wide memory free percentage/ { gsub(/[%[:space:]]/, "", $2); print $2 }')"
free_percent="${free_percent:-未知}"
free_percent_display="$free_percent"
if [[ "$free_percent" != "未知" ]]; then
  free_percent_display="${free_percent}%"
fi

disk_free_kib="$(/bin/df -Pk / | awk 'NR == 2 { print $4 }')"
disk_free_gib="$(awk -v n="$disk_free_kib" 'BEGIN { printf "%.1f", n / 1048576 }')"

/bin/ps -axo rss=,ucomm= | awk '
  {
    rss=$1
    $1=""
    name=tolower($0)
    family=""

    if (name ~ /claude/) {
      family="Claude"
    } else if (name ~ /codex/ || name ~ /chatgpt/) {
      family="Codex／ChatGPT"
    } else if (name ~ /node/) {
      family="Node.js"
    } else if (name ~ /chrome/ || name ~ /headless/ || name ~ /chromium/) {
      family="Chrome／headless"
    }

    if (family != "") {
      count[family] += 1
      kb[family] += rss
    }
  }

  function row(label) {
    printf "| %s | %d | %.2fGiB |\n", label, count[label] + 0, (kb[label] + 0) / 1048576
  }

  END {
    row("Claude")
    row("Codex／ChatGPT")
    row("Node.js")
    row("Chrome／headless")
  }
' > "$tmp_families"

pressure_level="穩定"
pressure_note="目前沒有命中這支腳本的保守警戒線。每加一個重任務，仍建議回到活動監視器確認。"

if [[ "$free_percent" != "未知" ]] && awk -v f="$free_percent" 'BEGIN { exit !(f <= 10) }'; then
  pressure_level="高壓"
  pressure_note="先儲存工作，停止新增任務，關閉不需要的重任務與瀏覽器自動操作。"
elif awk -v s="${swap_used_mib:-0}" 'BEGIN { exit !(s >= 30720) }'; then
  pressure_level="高壓"
  pressure_note="Swap 已達我的 30GB 緊急線；先儲存工作，停止新增任務，再看記憶體壓力與趨勢。"
elif awk -v d="$disk_free_gib" 'BEGIN { exit !(d <= 10) }'; then
  pressure_level="高壓"
  pressure_note="磁碟餘裕已低於個人保守紅線 10GB；先儲存工作，不要新增任務或進行大檔輸出。"
elif [[ "$free_percent" != "未知" ]] && awk -v f="$free_percent" 'BEGIN { exit !(f <= 20) }'; then
  pressure_level="偏高"
  pressure_note="停止新增大型程式、索引、生圖與瀏覽器操作，等現有任務結束後再看一次。"
elif awk -v d="$disk_free_gib" 'BEGIN { exit !(d <= 40) }'; then
  pressure_level="偏高"
  pressure_note="磁碟餘裕已低於個人保守黃線 40GB；安排清理可辨認的大檔與閒置工作。"
elif awk -v s="${swap_used_mib:-0}" -v p="$physical_mib" 'BEGIN { exit !(s >= p / 2) }'; then
  pressure_level="需追蹤"
  pressure_note="Swap 基準值偏高，但單次快照不能判斷正在惡化；隔 5 到 10 分鐘再跑一次比較。"
fi

cat > "$tmp_report" <<EOF
# Mac AI 多工記憶體安全報告

- 產生時間：$(date '+%Y-%m-%d %H:%M:%S %Z')
- 腳本判讀：**${pressure_level}**
- 判讀提醒：${pressure_note}

## 系統總覽

| 項目 | 目前數值 |
|---|---:|
| 實體記憶體 | ${physical_gib}GiB |
| 系統估算 free percentage | ${free_percent_display} |
| 壓縮記憶體 | ${compressed_gib}GiB |
| Swap 總量 | ${swap_total_gib}GiB |
| Swap 已用 | ${swap_used_gib}GiB |
| Swap 可用 | ${swap_free_gib}GiB |
| 磁碟剩餘 | ${disk_free_gib}GB |

## 固定程序家族

| 程序家族 | 程序數 | 近似 RSS |
|---|---:|---:|
$(cat "$tmp_families")

程序 RSS 可能包含共享頁面，不能直接相加成實體 RAM。這裡只保留四個固定分類，不列完整命令列、私人路徑、檔名、PID 或主機名稱。

## 下一步

1. 打開「活動監視器」→「記憶體」。
2. 記憶體壓力變黃時，停止新增重任務。
3. 變紅時，先儲存，再關閉重任務與瀏覽器自動操作。
4. 磁碟低於 40GB 時，從大型檔案、下載項目與閒置伺服器開始整理；不要直接刪「系統資料」。
5. 隔 5 到 10 分鐘再跑一次；如果 Swap 或某個程序家族快速增加，就不要再加開工作。

## 交給 AI 前的提醒

請先解釋這份報告中的記憶體壓力、壓縮、swap 與磁碟餘裕。不要提供刪檔、kill、pkill 或 sudo 指令。若資訊不足，請先告訴我應該回到活動監視器或儲存空間查看哪個欄位。

> 腳本的「穩定／偏高／高壓」和 40GB／10GB 磁碟線是個人保守操作建議，不是 Apple 官方分級；最終仍以活動監視器的記憶體壓力曲線與自己的工作負載為準。
EOF

/bin/mv -n "$tmp_report" "$output_path"
if [[ -e "$tmp_report" ]]; then
  echo "建立報告時發現同名檔案，已停止且沒有覆寫：${output_name}" >&2
  exit 1
fi
/bin/chmod 600 "$output_path"

echo "報告已建立：${output_name}"
echo "這份報告不含完整命令列、私人路徑、檔名、PID 或主機名稱。"
