"""Monte Carlo model of a five-layer hook chain for one governed agent action.

Each layer is a separate service. Every call costs one network round trip plus
the service's own processing, both lognormal. The calls are sequential, because
each layer needs the previous layer's output. A final correlation step joins
the five answers, which share no common run or grant identifier.
"""
import json
import sys

import numpy as np

rng = np.random.default_rng(31)
N = 1_000_000
LAYERS = 5
Z99 = 2.3263


def lognormal(median_ms, p99_ratio, size):
    """Lognormal with the given median and p99/median ratio."""
    sigma = np.log(p99_ratio) / Z99
    return median_ms * np.exp(sigma * rng.standard_normal(size))


SCENARIOS = {
    # Vendors deployed in the customer's cloud region, persistent connections.
    # RTT median from AWS's published cross-AZ p50 (1.5 ms).
    "in_region": dict(rtt=(1.5, 3.0), proc=(15.0, 8.0), corr=(5.0, 6.0)),
    # Vendors reached as public SaaS over the internet, persistent connections.
    # Processing median sits inside Lakera's published SaaS p95 band for
    # 1k-10k character inputs (<20 ms to 100-200 ms).
    "public_saas": dict(rtt=(30.0, 3.0), proc=(40.0, 8.0), corr=(10.0, 6.0)),
}


def run(cfg):
    total = np.zeros(N)
    for _ in range(LAYERS):
        total += lognormal(*cfg["rtt"], N) + lognormal(*cfg["proc"], N)
    total += lognormal(*cfg["corr"], N)
    q = np.percentile(total, [50, 90, 95, 99, 99.9])
    return {k: round(float(v), 1) for k, v in zip(["p50", "p90", "p95", "p99", "p999"], q)}, total


out = {}
hist = {}
for name, cfg in SCENARIOS.items():
    stats, total = run(cfg)
    out[name] = stats
    hist[name] = np.percentile(total, np.linspace(0.5, 99.9, 200)).round(2).tolist()

# Chance that a five-call chain contains at least one call at or beyond its own p99.
out["p_chain_hits_a_p99"] = round(1 - 0.99**LAYERS, 4)
out["assumptions"] = {
    name: {k: {"median_ms": v[0], "p99_over_median": v[1]} for k, v in cfg.items()}
    for name, cfg in SCENARIOS.items()
}
out["N"] = N
out["seed"] = 31

json.dump(out, sys.stdout, indent=2)
with open(sys.argv[1], "w") as f:
    json.dump({"stats": out, "quantiles": hist}, f)
