#!/usr/bin/env python3
"""Live check of the two held products after commit 21429f11 (Trust State Engine, CainDrift). Stdlib only.

Trust State Engine: the public demo (policy violation -> high-risk write DENIED -> explanation) must explain the
violation, not "Initial trust state" / "No events yet".  CainDrift: close-wording meaning flips must be REGRESSION
and same-meaning rephrases must carry no meaning flag (called via the gateway with the account key).
Usage: python3 24-hours/verify_held2.py [--json out.json]     Exit 0 only when every check passes.
"""
import json, sys, time, urllib.request, urllib.error

BASE = "https://cainstudio.online"
KEY = __import__("os").environ.get("CAIN_API_KEY") or json.load(open(__import__("os").path.expanduser("~/.cain/credentials.json")))["api_key"]
results = []


def post(path, body, key=False):
    h = {"Content-Type": "application/json", "User-Agent": "cain-verify-held2"}
    if key:
        h["X-API-Key"] = KEY
    r = urllib.request.Request(BASE + path, data=json.dumps(body).encode(), headers=h, method="POST")
    try:
        with urllib.request.urlopen(r, timeout=90) as f:
            return f.status, json.load(f)
    except urllib.error.HTTPError as e:
        return e.code, {"error": e.read()[:300].decode("utf8", "ignore")}


def check(product, case, ok, detail=""):
    results.append({"product": product, "case": case, "pass": bool(ok), "detail": str(detail)[:240]})
    print(f"  [{'PASS' if ok else 'FAIL'}] {product}: {case}" + (f"  ({detail})" if not ok else ""))


p = "Trust State Engine"
# The product's own API through the gateway (the public demo shares a per-IP hourly limit and can answer 429).
uid = f"agent-verify-{int(time.time())}"
post("/trust-state-engine/trust/event", {"tenant": "public-demo", "principal_id": uid, "event_type": "policy_violated",
     "event_data": {"detail": "tried to read another tenant's data"}, "risk_level": "high"}, key=True)
_, a = post("/trust-state-engine/authorize", {"tenant": "public-demo", "principal_id": uid, "action": "write",
            "resource": "customer-db", "risk_level": "high"}, key=True)
st = 0
try:
    r_ = urllib.request.Request(f"{BASE}/trust-state-engine/trust/public-demo/{uid}/explanation",
                                headers={"X-API-Key": KEY, "User-Agent": "cain-verify-held2"})
    with urllib.request.urlopen(r_, timeout=90) as f:
        st, e = f.status, json.load(f)
except urllib.error.HTTPError as ex:
    st, e = ex.code, {}
check(p, "high-risk write after a violation is DENIED", a.get("authorization_decision") == "deny", a)
check(p, "authorization counts the violation", (a.get("evidence_summary") or {}).get("violations") == 1, a.get("evidence_summary"))
check(p, "explanation names the policy violation", "policy violation" in str(e.get("why_trust_changed")), e.get("why_trust_changed"))
check(p, "explanation lists the policy_violated event", [x.get("event_type") for x in e.get("which_events_mattered") or []] == ["policy_violated"], e.get("which_events_mattered"))
check(p, "explanation shows the state before (unknown)", (e.get("what_trust_was_before") or {}).get("trust_state") == "unknown", e.get("what_trust_was_before"))
check(p, "no 'No events yet' after a recorded event", bool(e.get("why_trust_changed")) and "No events" not in json.dumps(e),
      e.get("what_evidence_changed_it") or f"explanation HTTP {st}")

p = "CainDrift"
flips = [("yes/no flip", "No, refunds only within 30 days.", "Yes, refunds after 30 days."),
         ("seat count", "Your plan includes 5 seats.", "Your plan includes 50 seats."),
         ("number words", "Shipping takes three days.", "Shipping takes seven days."),
         ("before -> after", "Payment is due before delivery.", "Payment is due after delivery."),
         ("negation dropped", "The API key is not shared with third parties.", "The API key is shared with third parties."),
         ("allowed -> prohibited", "Pets are allowed in the office.", "Pets are prohibited in the office.")]
same = [("identical", "Refunds are available within 30 days with a receipt.", "Refunds are available within 30 days with a receipt."),
        ("3 == three", "Shipping takes three days.", "Shipping takes 3 days."),
        ("1,000 == 1000", "The limit is 1,000 requests per hour.", "The limit is 1000 requests per hour."),
        ("yes rephrased", "Yes, you can cancel at any time.", "Yes, cancellation is possible whenever you like.")]
pairs = [{"prompt": n, "response_old": o_, "response_new": n_} for n, o_, n_ in flips + same]
st, r = post("/caindrift/regression/compare", {"pairs": pairs}, key=True)
res = r.get("results") or []
for i, (n, _, _) in enumerate(flips):
    x = res[i] if len(res) > i else {}
    check(p, f"flags meaning flip: {n}", x.get("flagged") and x.get("meaning_changes"), x.get("reasons"))
for j, (n, _, _) in enumerate(same):
    x = res[len(flips) + j] if len(res) > len(flips) + j else {}
    check(p, f"no meaning flag: {n}", x.get("meaning_changes") == [], x.get("reasons"))

n, ok = len(results), sum(r["pass"] for r in results)
print(f"\n{ok}/{n} live checks passed")
if "--json" in sys.argv:
    json.dump({"base": BASE, "checked_at": time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()), "passed": ok,
               "total": n, "results": results}, open(sys.argv[sys.argv.index("--json") + 1], "w"), indent=1)
sys.exit(0 if ok == n else 1)
