"""Recompute the stress test tables from the CSV files PreflightStressEA writes.

Usage:  python preflight_crosscheck.py [folder]
The folder defaults to the current one. It must hold PreflightStress_<tag>.csv
files, as written to Terminal\\Common\\Files with InpRunTag set. Only the
Python 3 standard library is used.

Columns: time, step, gate, shadow, sent, retcode
  gate    -1 gate off, 0 passed, 1 skipped as no change, >1 blocked (ENUM_PREFLIGHT_CODE)
  shadow  -1 no shadow gate, 0 the shadow would pass the request, >0 it would block it
  sent    1 when the request reached OrderSend()
  retcode server retcode for sent requests
"""
import csv
import glob
import os
import sys
from collections import Counter

ACCEPTED = {10008, 10009, 10010}          # placed, done, done partially
NO_CHANGE = 1


def load(path):
    with open(path, newline="") as f:
        return [{k: (v if k == "time" else int(v)) for k, v in row.items()}
                for row in csv.DictReader(f)]


def summary(rows):
    sent = [r for r in rows if r["sent"] == 1]
    rejected = [r for r in sent if r["retcode"] not in ACCEPTED]
    skipped = sum(1 for r in rows if r["sent"] == 0 and r["gate"] == NO_CHANGE)
    blocked = sum(1 for r in rows if r["sent"] == 0 and r["gate"] > NO_CHANGE)
    return len(rows), blocked, skipped, len(sent), rejected


def main():
    folder = sys.argv[1] if len(sys.argv) > 1 else "."
    files = sorted(glob.glob(os.path.join(folder, "PreflightStress_*.csv")))
    if not files:
        sys.exit("no PreflightStress_*.csv files in " + os.path.abspath(folder))
    print("run        requests  blocked  skipped   sent  rejected  retcodes")
    shadow_runs = []
    for path in files:
        tag = os.path.basename(path)[len("PreflightStress_"):-4]
        rows = load(path)
        n, blocked, skipped, sent, rejected = summary(rows)
        codes = Counter(r["retcode"] for r in rejected)
        text = " ".join("%dx%d" % (c, k) for c, k in sorted(codes.items()))
        print("%-10s %8d %8d %8d %6d %9d  %s" % (tag, n, blocked, skipped, sent, len(rejected), text))
        by_step = Counter(r["step"] for r in rejected)
        print("           rejected by step: %s" %
              ", ".join("%d:%d" % (s, k) for s, k in sorted(by_step.items())))
        if any(r["shadow"] >= 0 for r in rows):
            shadow_runs.append((tag, rows))

    # the same sent requests, shadow verdict against the server result
    for tag, rows in shadow_runs:
        cells = Counter()
        for r in rows:
            if r["sent"] == 1 and r["shadow"] >= 0:
                blocked = r["shadow"] != 0
                accepted = r["retcode"] in ACCEPTED
                cells[(blocked, accepted)] += 1
        br, ba = cells[(True, False)], cells[(True, True)]
        pr, pa = cells[(False, False)], cells[(False, True)]
        print("\nshadow %s: blocked+rejected %d, blocked+accepted %d, passed+rejected %d, passed+accepted %d"
              % (tag, br, ba, pr, pa))
        caught = br / (br + pr) if br + pr else float("nan")
        print("   share of server rejections the shadow would have stopped: %.1f%%" % (100 * caught))
        print("   accepted requests it would have blocked per rejection stopped: %.2f" % (ba / br if br else float("nan")))


if __name__ == "__main__":
    main()
