#!/usr/bin/env python3
"""Receiver-side child process for the snapshot_link smoke test.

Reads CLI args, brings up a SnapshotPeer with a registered recv buffer,
writes endpoint metadata to a control file, then loops: wait for size
signal, verify recv buffer, write ack.

Status events are printed as single-line JSON to stdout for parent to
parse.
"""
from __future__ import annotations

import argparse
import ctypes
import hashlib
import json
import sys
import time
from pathlib import Path

sys.path.insert(0, str(Path(__file__).resolve().parent.parent / "src"))


def _pattern_byte(i: int, seed: int) -> int:
    return (i * 2654435761 + seed) & 0xFF


def _fill_pattern(buf, length: int, seed: int) -> None:
    tile_size = 4096
    tile = bytes(_pattern_byte(i, seed) for i in range(tile_size))
    tile_arr = (ctypes.c_ubyte * tile_size).from_buffer_copy(tile)
    n_full = length // tile_size
    rem = length - n_full * tile_size
    base = ctypes.addressof(buf)
    src_addr = ctypes.addressof(tile_arr)
    for k in range(n_full):
        ctypes.memmove(base + k * tile_size, src_addr, tile_size)
    if rem:
        ctypes.memmove(base + n_full * tile_size, src_addr, rem)


def _emit(d: dict) -> None:
    print(json.dumps(d), flush=True)


def main():
    ap = argparse.ArgumentParser()
    ap.add_argument("--host", required=True)
    ap.add_argument("--port", type=int, required=True)
    ap.add_argument("--ib", required=True)
    ap.add_argument("--max-bytes", type=int, required=True)
    ap.add_argument("--control-path", required=True)
    ap.add_argument("--sizes", required=True, help="comma-separated bytes")
    args = ap.parse_args()

    sizes = [int(s) for s in args.sizes.split(",")]

    from agentic_pd_hybrid.snapshot_link import SnapshotPeer

    try:
        peer = SnapshotPeer(
            host=args.host,
            port=args.port,
            ib_device=args.ib,
            receive_capacity_bytes=args.max_bytes,
        )
    except Exception as e:
        import traceback
        _emit({"event": "init-failed", "error": repr(e), "tb": traceback.format_exc()})
        sys.exit(2)

    endpoint = peer.endpoint
    Path(args.control_path).write_text(json.dumps({
        "session_id": endpoint.session_id,
        "base_ptr": endpoint.base_ptr,
        "capacity_bytes": endpoint.capacity_bytes,
        "ready": True,
    }))
    _emit({"event": "endpoint-ready", "session_id": endpoint.session_id,
           "base_ptr": endpoint.base_ptr, "capacity": endpoint.capacity_bytes})

    cp = Path(args.control_path)
    for size in sizes:
        if size > args.max_bytes:
            continue
        signal_path = cp.with_suffix(f".do{size}")
        ack_path = cp.with_suffix(f".ack{size}")
        deadline = time.time() + 120.0
        while time.time() < deadline:
            if signal_path.exists():
                break
            time.sleep(0.05)
        else:
            _emit({"event": "no-signal-timeout", "size": size})
            continue
        try:
            seed = int(signal_path.read_text().strip())
        except Exception as e:
            _emit({"event": "signal-parse-error", "size": size, "err": repr(e)})
            continue
        expected_arr = (ctypes.c_ubyte * size)()
        _fill_pattern(expected_arr, size, seed)
        expected_hash = hashlib.sha256(bytes(expected_arr)).hexdigest()
        recv_bytes = peer.read_bytes(0, size)
        recv_hash = hashlib.sha256(recv_bytes).hexdigest()
        ok = recv_hash == expected_hash
        _emit({
            "event": "verify",
            "size": size,
            "ok": ok,
            "expected_sha": expected_hash[:16],
            "got_sha": recv_hash[:16],
            "first8_recv": recv_bytes[:8].hex(),
            "last8_recv": recv_bytes[-8:].hex(),
        })
        ack_path.write_text("done")

    peer.close()
    _emit({"event": "receiver-done"})


if __name__ == "__main__":
    main()
