#!/usr/bin/env python3
"""Score the planted-bug proof runs (the Tallyloop preview build) into the numbers /benchmark shows.

    python3 scripts/proof-planted-bugs.py runs.jsonl > src/features/marketing/proof-planted-bugs.json

runs.jsonl has one line per agent: {run, persona, result: {journey_steps, problems, action, ...}}, written by
the in-container runner (no database, no credits). For each planted problem it counts the agents who reached
the place it lives, the agents whose browser records caught it (observed), and the agents whose own report
named it (their words, matched by the phrases below). A third-party widget fails on purpose on the welcome
page; any browser record of it would be a false record, and is counted.
"""
import json, re, sys
from collections import Counter

rows = [json.loads(l) for l in open(sys.argv[1]) if l.strip()]

def path(u):
    m = re.search(r"mimiqai\.com(/[^?#]*)", u or "")
    return m.group(1).rstrip("/") if m else ""

def sentences(r):
    """The agent's own report, one sentence (or one listed problem) at a time."""
    parts = []
    for p in r.get("problems") or []:
        parts.append(p.get("description", "") if isinstance(p, dict) else str(p))
    for k in ("monologue", "biggest_pain_point"):
        if isinstance(r.get(k), str): parts.append(r[k])
    for k in ("confusion_points", "objections"):
        for v in r.get(k) or []: parts.append(str(v))
    out = []
    for part in parts:
        out += [x.strip().lower() for x in re.split(r"(?<=[.!?])\s+", part) if x.strip()]
    return out

def says(r, pattern, not_=None):
    """True when one sentence matches every regex in the pattern (a tuple: the thing, and what went wrong)."""
    pats = pattern if isinstance(pattern, tuple) else (pattern,)
    return any(all(re.search(q, sent) for q in pats) and not (not_ and re.search(not_, sent)) for sent in sentences(r))

PLANTED = [
    {"key": "signup_500", "label": "Sign-up request answers 500 on the first try", "where": "/demo/tallyloop/preview",
     "tech": lambda t: t.get("kind") == "http_error" and t.get("status") == 500 and t.get("path") == "/api/demo-signup",
     "said": (r"sign|creat|account|submi", r"500|server error|something went wrong|generic error|fail|error")},
    {"key": "signup_slow", "label": "Sign-up request takes 3.6 seconds", "where": "/demo/tallyloop/preview",
     "tech": lambda t: t.get("kind") == "slow" and t.get("path") == "/api/demo-signup",
     "said": (r"sign ?up\b|signup|create account|creating (your )?account|account creation|demo-signup", r"\bslow|took|seconds|delay|long wait|after a [^.]*wait|waited")},
    {"key": "website_field", "label": "Company website field blocks the form with only \"Invalid URL\"", "where": "/demo/tallyloop/preview/business",
     "tech": None,
     "said": (r"website|url", r"invalid|reject|error|https|format|not accept|wouldn.t accept"),
     # The narrower finding: the agent named the https rule itself, not only the bare error.
     "detail": ("named the https:// rule", (r"website|url", r"https?://|https\b|http prefix|protocol|scheme"))},
    {"key": "invoices_slow", "label": "Invoice list takes 5.2 seconds to load", "where": "/demo/tallyloop/preview/welcome",
     "tech": lambda t: t.get("kind") == "slow" and t.get("path") == "/api/demo-invoices",
     "said": (r"invoice list|invoices? (api|call|request|load)|demo-invoices|loading (your )?invoices", r"\bslow|took|seconds|delay|wait"),
     # Sentences about the dead "Add an invoice" button are not about the slow list.
     "not": r"add an invoice"},
    {"key": "dead_button", "label": "\"Add an invoice\" throws a JavaScript error and does nothing", "where": "/demo/tallyloop/preview/welcome",
     "tech": lambda t: t.get("kind") == "js_error" and "plan" in (t.get("message") or ""),
     "said": (r"add an invoice", r"nothing|no response|did not|didn.t|not work|dead|no visible|unresponsive|broken")},
    {"key": "broken_link", "label": "\"Reminders\" link goes to a missing page (404)", "where": "/demo/tallyloop/preview/welcome",
     "tech": lambda t: t.get("kind") == "http_error" and t.get("status") == 404 and "reminders" in (t.get("path") or ""),
     "said": (r"reminders", r"404|not found|missing|broken|does not exist|doesn.t exist")},
]

def reached(steps, where):
    return any(path(s.get("url")) == where for s in steps) or (where == "/demo/tallyloop/preview" and steps)

out, false_records, finished = [], Counter(), 0
for p in PLANTED:
    exposed = caught = said = detail = 0
    for row in rows:
        r = row["result"]; steps = r.get("journey_steps") or []
        if not reached(steps, p["where"]):
            continue
        exposed += 1
        if p["tech"] and any(p["tech"](t) for s in steps for t in (s.get("tech") or [])):
            caught += 1
        if says(r, p["said"], p.get("not")):
            said += 1
        if p.get("detail") and says(r, p["detail"][1]):
            detail += 1
    out.append({"key": p["key"], "label": p["label"], "exposed": exposed, "browser": caught if p["tech"] else None, "said": said,
                **({"detail": {"label": p["detail"][0], "n": detail}} if p.get("detail") else {})})

# Problems the agents reported that nobody planted, counted the same way.
UNPLANTED = [
    {"key": "invoices_nav", "label": "The \"Invoices\" item in the sidebar looks clickable but does nothing",
     "said": (r"invoices (sidebar|nav|navigation|item|link|menu|tab)|sidebar[^.]*invoices", r"nothing|no (visible|page) change|did not|didn.t|no response|not clickable")},
]
unplanted = []
for u in UNPLANTED:
    unplanted.append({"key": u["key"], "label": u["label"], "said": sum(1 for row in rows if says(row["result"], u["said"]))})

known = [p["tech"] for p in PLANTED if p["tech"]]
for row in rows:
    r = row["result"]
    if r.get("action") in ("converted", "done"):
        finished += 1
    for s in r.get("journey_steps") or []:
        for t in s.get("tech") or []:
            if not any(k(t) for k in known):
                false_records[f"{t.get('kind')} {t.get('method', '')} {t.get('path', '')} {t.get('status') or t.get('message') or ''}".strip()] += 1

print(json.dumps({
    "checked": "2026-10-22",
    "build": "https://www.mimiqai.com/demo/tallyloop/preview",
    "task": "Start the free trial, set up your business, then add your first invoice.",
    "runs": len({row["run"] for row in rows}),
    "agents": len(rows),
    "finished": finished,
    "planted": out,
    "unplanted": unplanted,
    "other_records": [{"what": k, "times": v} for k, v in false_records.most_common()],
    "control_recorded": sum(v for k, v in false_records.items() if "jsdelivr" in k or "widget" in k),
}, indent=2))
