"""Read-only VPS storage audit; exclusive bytes account for shared hard links."""
import datetime
import json
import os
from pathlib import Path
import re
import shutil

root = Path('/var/www/html/ro-th-online')

def files_under(path):
    if path.is_file():
        return [path]
    return [p for p in path.rglob('*') if p.is_file() and not p.is_symlink()]

def sizes(files):
    nodes = {}
    for p in files:
        s = p.stat()
        key = (s.st_dev, s.st_ino)
        if key not in nodes:
            nodes[key] = [s, 0]
        nodes[key][1] += 1
    return dict(files=len(files), logicalBytes=sum(p.stat().st_size for p in files),
                allocatedBytes=sum(s.st_blocks * 512 for s, n in nodes.values()),
                exclusiveBytes=sum(s.st_blocks * 512 for s, n in nodes.values() if n == s.st_nlink))

def item(p):
    s = p.stat()
    return dict(path=str(p.relative_to(root)), modifiedUtc=datetime.datetime.fromtimestamp(s.st_mtime, datetime.timezone.utc).isoformat(), **sizes(files_under(p)))

result = dict(staging=[], deployLarge=[], buildReleases=[], buildOther=[], references=[], openStagingFiles=[])
result['disk'] = dict(zip(['total', 'used', 'free'], shutil.disk_usage(root)))
result['staging'] = [item(p) for p in sorted((root / 'staging').iterdir())]
for p in (root / 'deploy').rglob('*'):
    if p.is_file() and p.stat().st_size > 1048576:
        result['deployLarge'].append(item(p))
build = root / 'public/Build'
hashes = set()
for p in build.iterdir():
    match = re.search(r'(?:mobile-|WebPreview\.data\.)([a-f0-9]{8})(?:\.|$)', p.name)
    if match:
        hashes.add(match[1])
    else:
        result['buildOther'].append(item(p))
for value in sorted(hashes):
    members = [p for p in build.iterdir() if p.name == 'mobile-' + value or p.name.startswith('WebPreview.data.' + value + '.')]
    files = [f for p in members for f in files_under(p)]
    record = dict(hash=value, **sizes(files))
    record['rawData'] = [dict(path=str(p.relative_to(root)), bytes=p.stat().st_size) for p in files if p.name == 'WebPreview.data']
    record['brotliBytes'] = sum(p.stat().st_size for p in files if p.suffix == '.br')
    result['buildReleases'].append(record)
pages = [root / 'mobile/index.html', root / 'public/index.html', *list((root / 'deploy').rglob('*index*.html'))]
for p in pages:
    lines = [line.strip() for line in p.read_text(errors='replace').splitlines() if any(k in line for k in ['buildUrl =', 'dataUrl:', 'codeUrl:'])]
    result['references'].append(dict(path=str(p.relative_to(root)), lines=lines))
for proc in Path('/proc').iterdir():
    if not proc.name.isdigit():
        continue
    try:
        for fd in (proc / 'fd').iterdir():
            try:
                target = os.readlink(fd)
            except OSError:
                continue
            if target.startswith(str(root / 'staging') + '/'):
                result['openStagingFiles'].append(dict(pid=int(proc.name), process=(proc / 'comm').read_text().strip(), path=target))
    except (OSError, PermissionError):
        pass
print(json.dumps(result))
