#!/usr/bin/env python3
"""Recompute published body-transfer findings offline; never makes network requests."""
import argparse
from collections import Counter
import hashlib
import json
from pathlib import Path
import zipfile


def load_recordings(path):
    with zipfile.ZipFile(path) as archive:
        result = {}
        for run in ('main', 'sequence', 'headers'):
            def read(name):
                entry = archive.getinfo(f'{run}/{name}')
                if entry.file_size > 3 * 1024 * 1024:
                    raise ValueError('recording exceeds offline size limit')
                return archive.read(entry)
            manifest = json.loads(read('manifest.json'))
            completion = json.loads(read('completion.json'))
            records = [json.loads(line) for line in read('requests.jsonl').splitlines()]
            attempts = [r for r in records if r['attempted']]
            if len(attempts) != completion['attempted_requests']:
                raise ValueError('attempt count differs from completion')
            if any(type(r.get('size_download')) is not int or r['size_download'] < 0 for r in attempts):
                raise ValueError('unknown/invalid measured bytes; this comparison needs complete measurements')
            if any(r['exitcode'] != 0 or r['inspection']['state'] not in ('pass','reused_baseline') for r in attempts):
                raise ValueError('this recorded comparison contains a failed/unverified observation')
            if sum(r['size_download'] for r in attempts) != completion['budget_body_bytes']:
                raise ValueError('byte sum differs from completion')
            result[run] = (manifest, records)
        return result


def totals(rows):
    attempts = [r for r in rows if r['attempted']]
    return {'attempted': len(attempts), 'skipped':len(rows)-len(attempts),
            'status_counts':dict(sorted(Counter(str(r['status']) for r in attempts).items())),
            'response_body_bytes':sum(r['size_download'] for r in attempts)}


def analyze(path):
    runs = load_recordings(path)
    main, sequence, headers = [runs[name][1] for name in ('main','sequence','headers')]
    cold=[]
    for route in ('benchmark-html','benchmark-md','errors-html','errors-md'):
        rows=[r for r in main if r['attempted'] and r['route']==route and r['phase']=='baseline']
        cold.append({'route':route,'observations':len(rows),'minimum_body_bytes':min(r['size_download'] for r in rows),
                     'maximum_body_bytes':max(r['size_download'] for r in rows),
                     'decoded_body_bytes':sorted({r['inspection']['decoded_body_bytes'] for r in rows}),
                     'decoded_hashes':sorted({r['inspection']['decoded_sha256'] for r in rows})})
    by_representation={}
    for representation in ('html','md'):
        rows=[r for r in sequence if r['route'].endswith('-'+representation)]
        by_representation[representation]=totals(rows)
    html_bytes=by_representation['html']['response_body_bytes']; md_bytes=by_representation['md']['response_body_bytes']
    policies={policy:totals([r for r in headers if r['phase']=='conditional' and r['header_policy']==policy])
              for policy in ('no_override','no_cache')}
    seed=totals([r for r in headers if r['phase']=='seed'])
    unchanged=[r for r in headers if r['phase']=='conditional' and r.get('eligible_unchanged_seed')]
    body_hashes={route:len({r['inspection']['decoded_sha256'] for r in main if r['attempted'] and r['status']==200 and r['route']==route})
                 for route in sorted({r['route'] for r in main})}
    return {'scope':'Recorded response-body bytes, including gzip where returned; excludes headers, TCP/TLS/proxy overhead and billing.',
            'archive_sha256':hashlib.sha256(path.read_bytes()).hexdigest(),
            'experiments':{name:totals(rows) for name,(_,rows) in runs.items()},
            'all_experiment_requests':sum(totals(rows)['attempted'] for _,rows in runs.values()),
            'all_experiment_body_bytes':sum(totals(rows)['response_body_bytes'] for _,rows in runs.values()),
            'cold_observations':cold,
            'sequence_with_no_cache':{'representation_totals':by_representation,'html_minus_markdown_body_bytes':html_bytes-md_bytes,
                'markdown_body_reduction_percent':round((html_bytes-md_bytes)*100/html_bytes,6)},
            'controlled_header_comparison':{'conditional_policies':policies,'seed_overhead':seed,
                'conditionals_with_unchanged_seed_content':len(unchanged),
                'extra_no_cache_body_bytes':policies['no_cache']['response_body_bytes']-policies['no_override']['response_body_bytes']},
            'main_unique_decoded_hashes_per_endpoint':body_hashes,
            'limits':['These are dated observations from one deployed site, not independent providers or a latency benchmark.',
                'The no-cache and no-override policies do not guarantee equal freshness or origin contact.',
                'Markdown max-age=300 permits freshness-based reuse; these runners intentionally make network checks.',
                'Raw credentials, exit IPs, unfiltered headers and body captures remain private; the dataset retains selected measurements and hashes.',
                'This verifies arithmetic and record consistency, not independent authenticity of historical server responses.']}


def main():
    parser=argparse.ArgumentParser(description=__doc__)
    parser.add_argument('archive',type=Path,nargs='?',default=Path(__file__).with_name('recordings.zip'))
    args=parser.parse_args()
    try:
        print(json.dumps(analyze(args.archive),indent=2))
    except (OSError,ValueError,KeyError,TypeError,zipfile.BadZipFile) as exc:
        parser.exit(2,'Cannot reproduce from these recordings: '+type(exc).__name__+'\n')

if __name__=='__main__':main()
