#!/usr/bin/env python3
"""Remove orphaned files under web/app/assets/ not referenced by current Vite build.

Vite/Rolldown minified bundles reference chunks by bare filename (e.g. ServersPage-xxx.js)
without import() paths — scan file contents for known asset basenames.
"""
from __future__ import annotations

import re
import sys
from pathlib import Path

# Leading `_` required for Vite chunks like `_plugin-vue_export-helper-*.js`
ASSET_NAME_RE = re.compile(
    r"[_A-Za-z0-9][A-Za-z0-9_.-]*\.(?:js|css|woff2?|ttf|eot)"
)
URL_RE = re.compile(r"""url\(["']?([^"')]+)["']?\)""")


def _referenced_basenames(text: str, known: set[str]) -> set[str]:
    found: set[str] = set()
    for m in ASSET_NAME_RE.finditer(text):
        name = m.group(0)
        if name in known:
            found.add(name)
    for raw in URL_RE.findall(text):
        name = Path(raw).name
        if name in known:
            found.add(name)
    return found


def collect(app_dir: Path) -> set[Path]:
    assets = app_dir / "assets"
    index = app_dir / "index.html"
    if not index.is_file():
        raise SystemExit(f"missing {index}")

    all_names = {p.name for p in assets.iterdir() if p.is_file()}
    keep_names: set[str] = set()

    for m in re.finditer(r"/app/assets/([^\s\"']+)", index.read_text(encoding="utf-8")):
        name = m.group(1).split("/")[-1]
        if name in all_names:
            keep_names.add(name)

    if not keep_names:
        raise SystemExit("no entry assets found in index.html")

    changed = True
    while changed:
        changed = False
        for name in list(keep_names):
            path = assets / name
            if not path.is_file():
                continue
            text = path.read_text(encoding="utf-8", errors="ignore")
            for ref in _referenced_basenames(text, all_names):
                if ref not in keep_names:
                    keep_names.add(ref)
                    changed = True

    return {assets / n for n in keep_names}


def main() -> int:
    dry_run = "--dry-run" in sys.argv
    args = [a for a in sys.argv[1:] if a != "--dry-run"]
    app_dir = Path(args[0] if args else "/www/wwwroot/api.synaglobal.vip/web/app")
    assets = app_dir / "assets"
    if not assets.is_dir():
        raise SystemExit(f"not a directory: {assets}")

    keep = collect(app_dir)
    all_files = {p for p in assets.iterdir() if p.is_file()}
    orphan = sorted(all_files - keep, key=lambda p: p.name)
    if dry_run:
        print(f"dry-run kept={len(keep)} would_remove={len(orphan)} total={len(all_files)}")
        return 0
    for p in orphan:
        p.unlink()
    print(f"kept={len(keep)} removed={len(orphan)} total_before={len(all_files)}")
    return 0


if __name__ == "__main__":
    raise SystemExit(main())
