#!/usr/bin/env python3
"""Practice file filtering and HTTP replay using synthetic local-only requests.

Requires Python 3.9+ and an installed GoReplay binary. Verified with the official
macOS release archive 1.3.3 (whose embedded version reports 1.3.0). Other builds
may behave differently; a failed assertion is not a passing replay. Does not capture packets,
contact production, download software, or use customer data.
"""
import argparse
from collections import Counter
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
import json
from pathlib import Path
import shutil
import subprocess
import tempfile
import threading
import time
from urllib.parse import urlsplit, parse_qs


def main():
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument('--gor', default='gor', help='Path to an installed GoReplay binary')
    args = parser.parse_args()
    executable = shutil.which(args.gor)
    if not executable:
        parser.error('GoReplay was not found; install it first or pass --gor /path/to/gor')
    received = []

    class Target(BaseHTTPRequestHandler):
        def do_GET(self):
            currency = parse_qs(urlsplit(self.path).query).get('currency', ['USD'])[0]
            status = 200 if currency == 'USD' else 422
            body = json.dumps({'currency': currency, 'accepted': status == 200}).encode()
            received.append((self.command, self.path, status))
            self.send_response(status)
            self.send_header('Content-Type', 'application/json')
            self.send_header('Content-Length', str(len(body)))
            self.end_headers()
            self.wfile.write(body)

        def do_POST(self):
            received.append(('POST', self.path, 500))
            self.send_error(500, 'The GET filter should prevent this request')

        def log_message(self, *_args):
            pass

    server = ThreadingHTTPServer(('127.0.0.1', 0), Target)
    thread = threading.Thread(target=server.serve_forever, daemon=True)
    thread.start()
    try:
        with tempfile.TemporaryDirectory(prefix='goreplay-demo-') as workspace:
            root = Path(workspace)
            stamp = time.time_ns()
            records = []
            for i, (method, path) in enumerate([
                ('GET', '/price?currency=USD'),
                ('GET', '/price?currency=INVALID'),
                ('POST', '/do-not-replay'),
            ]):
                meta = f'1 {i + 1:024x} {stamp + i * 100_000_000} -1\n'
                request = f'{method} {path} HTTP/1.1\r\nHost: demo.invalid\r\nContent-Length: 0\r\n\r\n'
                records.append((meta + request + '\n🐵🙈🙉\n').encode())
            (root / 'synthetic.gor').write_bytes(b''.join(records))

            def run(*flags):
                result = subprocess.run([executable, *flags], cwd=root, capture_output=True, text=True, timeout=15)
                if result.returncode:
                    raise RuntimeError(f'GoReplay exited with {result.returncode}: {result.stderr[-1000:]}')
                return result

            filter_result = run('--input-file', 'synthetic.gor', '--http-allow-method', 'GET',
                '--output-file', 'requests.gor', '--output-file-append',
                '--output-file-flush-interval', '100ms', '--exit-after', '3s')
            saved = root / 'requests.gor'
            if not saved.is_file() or b'POST /do-not-replay' in saved.read_bytes():
                raise RuntimeError(f'Expected one saved file containing only GET requests; files: {[(p.name, p.stat().st_size) for p in root.iterdir()]}; output: {filter_result.stdout[-1000:]} {filter_result.stderr[-1000:]}')
            destination = f'http://127.0.0.1:{server.server_port}'
            run('--input-file', 'requests.gor', '--output-http', destination, '--exit-after', '3s')
            expected = Counter([('GET', '/price?currency=USD', 200), ('GET', '/price?currency=INVALID', 422)])
            if Counter(received) != expected:
                raise RuntimeError(f'Unexpected target observations: {received!r}')
            print(json.dumps({
                'result': 'passed', 'synthetic_requests': 3,
                'replayed_requests': len(received), 'filtered_POST_requests': 1,
                'target_observations': sorted(received),
                'scope': 'Synthetic file input, GET filtering, append filename and loopback HTTP replay. No packet capture, TLS, production workload or response-diff engine tested.',
            }, indent=2))
    finally:
        server.shutdown()
        server.server_close()
        thread.join(timeout=2)


if __name__ == '__main__':
    main()
