#!/usr/bin/env python3
"""Controlled latency regression: a delayed API beside a game socket, one worker.

All traffic is loopback. This measures worker interference, not network capacity.
The exact backend and socket helpers are shared with test/test_proxy.py.
"""
import argparse
import hashlib
import http.client
import json
import pathlib
import platform
import statistics
import sys
import time
sys.path.insert(0, str(pathlib.Path(__file__).resolve().parents[1] / 'test'))
import test_proxy as fixture


def run(binary, rounds):
    fixture.BINARY = pathlib.Path(binary).resolve()
    fixture.ProxyTests.setUpClass()
    case = fixture.ProxyTests()
    case.setUp()
    measurements = []
    try:
        port = case.start()
        ws = case.websocket(port)
        for _ in range(rounds):
            case.backend.seen.clear()
            slow = case.connect(port)
            slow.sendall(b'GET /api/delay HTTP/1.1\r\nHost: localhost\r\n\r\n')
            if not case.backend.seen.wait(3): raise RuntimeError('Backend did not receive request')
            def echo():
                start = time.perf_counter()
                ws.sendall(fixture.frame(b'game tick'))
                if fixture.recv_frame(ws) != (130, b'game tick'): raise RuntimeError('Echo mismatch')
                return (time.perf_counter() - start) * 1000
            # Measure both probes concurrently, so the first does not absorb the delay.
            with fixture.concurrent.futures.ThreadPoolExecutor(2) as pool:
                echo_result = pool.submit(echo)
                asset_start = time.perf_counter()
                if case.get(port) != (200, b'assets stay responsive'): raise RuntimeError('Asset mismatch')
                asset_ms = (time.perf_counter() - asset_start) * 1000
                echo_ms = echo_result.result()
            response = http.client.HTTPResponse(slow); response.begin()
            if response.status != 200: raise RuntimeError('API response failed')
            response.read(); slow.close()
            measurements.append({'websocket_ms': echo_ms, 'asset_ms': asset_ms, 'errors': 0})
    finally:
        case.tearDown()
        fixture.ProxyTests.tearDownClass()
    return {'binary_sha256': hashlib.sha256(fixture.BINARY.read_bytes()).hexdigest(),
            'samples': measurements, 'median_websocket_ms': statistics.median(x['websocket_ms'] for x in measurements),
            'median_asset_ms': statistics.median(x['asset_ms'] for x in measurements)}


if __name__ == '__main__':
    parser = argparse.ArgumentParser()
    parser.add_argument('--before', required=True); parser.add_argument('--after', required=True)
    parser.add_argument('--rounds', type=int, default=5); parser.add_argument('--output', required=True)
    args = parser.parse_args()
    result = {'environment': platform.platform(), 'machine': platform.machine(), 'workers': 1,
              'backend_delay_ms': 800, 'rounds': args.rounds,
              'scope': 'Loopback worker-interference regression; not throughput, capacity, or a cross-server comparison.'}
    for label in ('before', 'after'):
        result[label] = run(getattr(args, label), args.rounds)
        pathlib.Path(args.output).write_text(json.dumps(result, indent=2) + '\n')
        print(label, json.dumps(result[label]), flush=True)
