"""Fold usage events into usage/by_asset.json (asset -> users) and usage/by_app.json (app/game -> assets).

Event (one JSON object per line, any usage/**/*.jsonl):
  {"ts": "2026-10-18T12:00:00Z", "asset_id": "prop.street.lamp/x", "version": 1, "release": "sha256:...",
   "user": "<app or game slug>", "platform": "omg|omu|hand", "source": "build_pack|db-brief|glb-hash|text-ref|..."}
Fold key = (asset_id, version, platform, user): duplicates collapse (first_seen = earliest ts, events = how many, sources = set).

usage: python3 tools/usage_index.py [--root <repo>]
"""
import glob, json, os, sys

ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
PLATFORMS = ('omg', 'omu', 'hand')


def read_events(usage_dir):
    """Every valid event line under usage_dir (skips by_*.json, bad lines, bad platforms)."""
    out = []
    for p in sorted(glob.glob(os.path.join(usage_dir, '**', '*.jsonl'), recursive=True)):
        for line in open(p, encoding='utf-8', errors='replace'):
            line = line.strip()
            if not line:
                continue
            try:
                e = json.loads(line)
            except ValueError:
                continue
            if isinstance(e, dict) and e.get('asset_id') and e.get('user') and e.get('platform') in PLATFORMS:
                out.append(e)
    return out


def fold(events, bulk=()):
    """-> (by_asset, by_app). Versions seen are kept per pair; release comes from the latest event."""
    pairs = {}
    for e in events:
        k = (e['asset_id'], e['platform'], e['user'])
        p = pairs.setdefault(k, {'first_seen': e.get('ts') or '', 'last_seen': e.get('ts') or '', 'events': 0,
                                 'versions': set(), 'releases': set(), 'sources': set()})
        ts = e.get('ts') or ''
        if ts and (not p['first_seen'] or ts < p['first_seen']):
            p['first_seen'] = ts
        if ts > p['last_seen']:
            p['last_seen'] = ts
        p['events'] += 1
        if e.get('version') is not None:
            p['versions'].add(e['version'])
        if e.get('release'):
            p['releases'].add(e['release'])
        if e.get('source'):
            p['sources'].add(e['source'])
    by_asset, by_app = {}, {}
    for (aid, plat, user), p in sorted(pairs.items()):
        row = {'user': user, 'platform': plat, **({'bulk': True} if f'{plat}:{user}' in bulk else {}), 'versions': sorted(p['versions']), 'first_seen': p['first_seen'],
               'last_seen': p['last_seen'], 'events': p['events'], 'sources': sorted(p['sources'])}
        by_asset.setdefault(aid, []).append(row)
        by_app.setdefault(f'{plat}:{user}', []).append(
            {'asset_id': aid, 'versions': row['versions'], 'releases': sorted(p['releases']), 'first_seen': p['first_seen'],
             'last_seen': p['last_seen'], 'events': p['events'], 'sources': row['sources']})
    return ({a: {'count': len([x for x in r if not x.get('bulk')]), 'users': sorted(r, key=lambda x: (x['platform'], x['user']))} for a, r in by_asset.items()},
            {u: {'count': len(r), **({'bulk': True} if u in bulk else {}), 'assets': sorted(r, key=lambda x: x['asset_id'])} for u, r in by_app.items()})


def build(root=ROOT):
    ud = os.path.join(root, 'usage')
    ev = read_events(ud)
    bp = os.path.join(ud, 'bulk_users.json')  # engine/kit trees that vendor the whole library: listed, not counted as a use
    by_asset, by_app = fold(ev, set(json.load(open(bp))) if os.path.exists(bp) else ())
    for name, data in (('by_asset.json', by_asset), ('by_app.json', by_app)):
        with open(os.path.join(ud, name), 'w') as f:
            json.dump(data, f, indent=1, sort_keys=True)
    return len(ev), by_asset, by_app


if __name__ == '__main__':
    root = sys.argv[sys.argv.index('--root') + 1] if '--root' in sys.argv else ROOT
    n, ba, bp = build(root)
    print(f'usage: {n} events -> {len(ba)} assets used, {len(bp)} apps/games')
