"""Aggregate a project-scoped Claude session archive without exporting messages."""
import argparse
import hashlib
import json
from collections import Counter
from pathlib import Path

FIELDS = ('input_tokens', 'cache_creation_input_tokens', 'cache_read_input_tokens', 'output_tokens')


def summarize(directory):
    requests = {}
    errors = set()
    duplicate_records = 0
    synthetic_records = 0
    inventory = hashlib.sha256()
    files = sorted(directory.rglob('*.jsonl'))
    for path in files:
        raw = path.read_bytes()
        inventory.update(str(path.relative_to(directory)).encode() + b'\0' + hashlib.sha256(raw).digest())
        for line in raw.splitlines():
            record = json.loads(line)
            if record.get('type') != 'assistant':
                continue
            message = record['message']
            if record.get('isApiErrorMessage') and record.get('error') == 'rate_limit':
                errors.add(record['uuid'])
            if message.get('model') == '<synthetic>':
                synthetic_records += 1
                continue
            if not message.get('usage'):
                continue
            key = message['id']
            if not key or not record.get('requestId'):
                raise ValueError('Usage record lacks stable message/request identity')
            usage = {field: message['usage'][field] for field in FIELDS}
            if any(not isinstance(n, int) or n < 0 for n in usage.values()):
                raise ValueError('Invalid token counter')
            item = {'request_id': record['requestId'], 'model': message['model'], 'tokens': usage}
            if key in requests:
                if requests[key]['identity'] != item:
                    raise ValueError('Conflicting usage snapshots; reconcile before publishing')
                duplicate_records += 1
                requests[key]['timestamp'] = min(requests[key]['timestamp'], record['timestamp'])
            else:
                requests[key] = {'identity': item, 'timestamp': record['timestamp']}
    models = {}
    for request in requests.values():
        item = request['identity']
        model = item['model']
        if model not in models:
            models[model] = {'tokens': Counter(), 'requests': 0, 'first_timestamp': request['timestamp'], 'last_timestamp': request['timestamp']}
        row = models[model]
        row['tokens'].update(item['tokens'])
        row['requests'] += 1
        row['first_timestamp'] = min(row['first_timestamp'], request['timestamp'])
        row['last_timestamp'] = max(row['last_timestamp'], request['timestamp'])
    for row in models.values():
        row['total_tokens'] = sum(row['tokens'].values())
    return {'schema': 1, 'captured_at': '2026-10-03',
        'scope': 'Local Claude session archive for the historical cli-tool/translate-bible project directory only. Not a complete project history or a provider quota statement.',
        'archive_files': len(files), 'archive_inventory_sha256': inventory.hexdigest(),
        'unique_requests': len(requests), 'duplicate_usage_records_removed': duplicate_records,
        'synthetic_records_excluded': synthetic_records, 'distinct_rate_limit_error_records': len(errors),
        'models': models, 'total_tokens': sum(row['total_tokens'] for row in models.values()),
        'counting': 'Deduplicate by assistant message ID; require identical request ID, model and top-level counters across repeated records. Sum input + cache creation + cache read + output once. Do not add nested iterations, thinking details, or modelUsage.',
        'limits': ['Only the recovered local archive is covered; other dates, machines, deleted logs and unrecorded calls are absent.',
                   'Haiku records include CLI session-labeling work; this is not evidence that every Haiku call translated or reviewed scripture.',
                   'Rate-limit error entries are not distinct quota-exhaustion episodes and carry no measured consumed-token estimate.',
                   'Cache token counts and nominal token totals are not a measurement of subscription quota consumed.',
                   'The separate 38-call release audit is not included in this total.'],
        'whole_project_total_tokens': None}


if __name__ == '__main__':
    parser = argparse.ArgumentParser()
    parser.add_argument('--sessions', type=Path, required=True)
    print(json.dumps(summarize(parser.parse_args().sessions), ensure_ascii=False, indent=2))
