#!/usr/bin/env bash
#
# clean-unused-assets.sh — 站台未使用圖片/檔案清理工具（跨站台重用）
#
# 用途：盤點某站台 assets/images 與 assets/upload 內，
#       未被「資料庫 SQL + assets/views（images 另比對 CSS/JS）」引用的孤兒檔案。
#
# 判定法（保守）：以「檔名(basename)」literal 比對，檔名只要在語料出現一次就算「有用到」。
#                 並複驗 URL 百分比編碼變體（%20 / +）避免含空格檔名誤判。
#
# 用法：
#   ./clean-unused-assets.sh <station_dir> [--delete]
#     <station_dir>  站台根目錄，例如 hosts/stations/jyg
#     不加參數       只產生報告 (dry-run)，不刪任何檔
#     --delete       先打包備份，再刪除（B 類 CSS/JS 有引用者一律保留）
#
# 輸出：
#   <station>/docs/ai-agents/project-init/unused-assets-report.txt
#   <station>/docs/ai-agents/project-init/unused-assets-backup-<date>.tar.gz  (--delete 時)
#
set -euo pipefail

STATION="${1:?請提供站台目錄，例如 hosts/stations/jyg}"
MODE="${2:-dryrun}"
STATION="${STATION%/}"

[ -d "$STATION" ] || { echo "找不到站台目錄: $STATION"; exit 1; }

ASSETS="$STATION/apps/resources/assets"
IMAGES="$ASSETS/images"
UPLOAD="$ASSETS/upload"
VIEWS="$ASSETS/views"
SQL=$(find "$STATION" -maxdepth 1 -name '*.sql' | head -1)
OUTDIR="$STATION/docs/ai-agents/project-init"
mkdir -p "$OUTDIR"

[ -n "$SQL" ] || { echo "找不到站台 SQL 檔(*.sql)"; exit 1; }
echo "站台: $STATION"
echo "SQL : $SQL"

TMP=$(mktemp -d)
trap 'rm -rf "$TMP"' EXIT

# 1) 主語料 = SQL + 所有 views 內容
cat "$SQL" > "$TMP/corpus.txt"
[ -d "$VIEWS" ] && find "$VIEWS" -type f -print0 | xargs -0 cat >> "$TMP/corpus.txt" 2>/dev/null || true
# 2) CSS/JS 語料（images 專用，避免 CSS 背景圖被誤判）
: > "$TMP/cssjs.txt"
for d in "$ASSETS/css" "$ASSETS/js" "$ASSETS/vendor"; do
  [ -d "$d" ] && find "$d" -type f \( -name '*.css' -o -name '*.js' -o -name '*.scss' \) -print0 \
    | xargs -0 cat >> "$TMP/cssjs.txt" 2>/dev/null || true
done

python3 - "$IMAGES" "$UPLOAD" "$TMP/corpus.txt" "$TMP/cssjs.txt" "$OUTDIR" "$MODE" <<'PY'
import os, sys, urllib.parse, subprocess, datetime
images, upload, corpus_f, cssjs_f, outdir, mode = sys.argv[1:7]
corpus = open(corpus_f, encoding='utf-8', errors='ignore').read()
cssjs  = open(cssjs_f,  encoding='utf-8', errors='ignore').read()

def walk(d):
    return [os.path.join(r, fn) for r, _, fs in os.walk(d) for fn in fs] if os.path.isdir(d) else []

def used(name, hay):
    variants = {name, urllib.parse.quote(name), urllib.parse.quote(name, safe=''),
                name.replace(' ', '%20'), name.replace(' ', '+')}
    return any(v in hay for v in variants)

# images：未被 SQL/views 用到 → 再看 CSS/JS；CSS/JS 有用到歸 B(保留)
img_del, img_keep = [], []
for f in walk(images):
    b = os.path.basename(f)
    if used(b, corpus): continue
    (img_keep if used(b, cssjs) else img_del).append(f)

# upload：未被 SQL/views 用到 → 拆 系統快取(D) 與 內容檔(C)
up_content, up_cache = [], []
for f in walk(upload):
    b = os.path.basename(f)
    if used(b, corpus): continue
    (up_cache if f.endswith('.cache') or f.endswith('.htaccess') else up_content).append(f)

report = os.path.join(outdir, 'unused-assets-report.txt')
with open(report, 'w', encoding='utf-8') as w:
    w.write("未使用資產分析報告（來源：SQL + assets/views；images 另比對 CSS/JS）\n" + "="*70 + "\n\n")
    w.write(f"【A. images 可刪】{len(img_del)}\n"      + "\n".join(img_del)    + "\n\n")
    w.write(f"【B. images 被CSS/JS引用→勿刪】{len(img_keep)}\n" + "\n".join(img_keep) + "\n\n")
    w.write(f"【C. upload 內容檔 未使用】{len(up_content)}\n"  + "\n".join(up_content) + "\n\n")
    w.write(f"【D. upload CKFinder系統快取】{len(up_cache)}\n"  + "\n".join(up_cache)  + "\n")

print(f"A. images 可刪:         {len(img_del)}")
print(f"B. images 勿刪(CSS/JS): {len(img_keep)}")
print(f"C. upload 內容未用:     {len(up_content)}")
print(f"D. upload 系統快取:     {len(up_cache)}")
print(f"報告: {report}")

if mode == '--delete':
    todel = img_del + up_content + up_cache
    if not todel:
        print("沒有可刪檔案。"); sys.exit(0)
    date = datetime.date.today().strftime('%Y%m%d')
    backup = os.path.join(outdir, f'unused-assets-backup-{date}.tar.gz')
    listf = os.path.join(outdir, '.to_delete.tmp')
    open(listf, 'w', encoding='utf-8').write('\0'.join(todel))
    subprocess.run(['tar', '--null', '-czf', backup, '--files-from=' + listf], check=True)
    os.remove(listf)
    n = 0
    for f in todel:
        try: os.remove(f); n += 1
        except FileNotFoundError: pass
    print(f"已備份: {backup}")
    print(f"已刪除: {n} 個（B類 {len(img_keep)} 個保留）")
else:
    print("（dry-run，未刪任何檔；加 --delete 才會備份並刪除）")
PY
