
import sys, json
from playwright.sync_api import sync_playwright
APP = "file:///Users/rig128gb/Developer/rig-offerings/recall-engine/app/index.html"
fails=[]
with sync_playwright() as p:
    b = p.chromium.launch()
    pg = b.new_page(viewport={"width":1680,"height":1000})
    errs=[]; pg.on("console", lambda m: errs.append(m.text) if m.type=="error" else None)
    pg.on("pageerror", lambda e: errs.append("PAGEERROR: "+str(e)))
    pg.goto(APP); pg.wait_for_timeout(2500)

    # 1 fleet health dot
    cls = pg.get_attribute("#aidot","class"); print("1. fleet dot class:", cls)
    if "ok" not in cls: fails.append("fleet dot not green in browser")

    # 2 table populated
    n = pg.locator("#tbl tbody tr").count(); print("2. table rows:", n)
    if n < 100: fails.append("table underpopulated")

    # 3 click a row -> drawer
    pg.locator("#tbl tbody tr").nth(3).click(); pg.wait_for_timeout(300)
    nm = pg.locator("#drawer h3").inner_text(); print("3. drawer patient:", nm)

    # 4 LIVE AI DRAFT
    pg.locator('#drawer [data-gen="sms"]').click()
    pg.wait_for_function("document.querySelector('#mmeta') && document.querySelector('#mmeta').textContent.length>0", timeout=90000)
    msg = pg.locator("#msgout").inner_text(); meta = pg.locator("#mmeta").inner_text()
    print("4. AI meta:", meta)
    print("   AI text:", msg[:220].replace("\n"," "))
    if "fleet" not in meta or "template" in meta: fails.append("AI fell back to template: "+meta)

    # 5 email draft
    pg.locator('#drawer [data-gen="email"]').click()
    pg.wait_for_function("document.querySelector('#mmeta').textContent.includes('ms')", timeout=90000)
    pg.wait_for_timeout(500)
    print("5. email draft:", pg.locator("#msgout").inner_text()[:180].replace("\n"," | "))

    # 6 capacity slider changes KPIs
    before = pg.locator("#kpis .kpi").nth(3).inner_text()
    pg.eval_on_selector("#cap","e=>{e.value=160;e.dispatchEvent(new Event('input'))}"); pg.wait_for_timeout(300)
    after = pg.locator("#kpis .kpi").nth(3).inner_text()
    print("6. cap 80->160 recovery:", before.split(chr(10))[1], "->", after.split(chr(10))[1])
    if before==after: fails.append("slider did not change KPIs")

    # 7 filters
    pg.eval_on_selector("#fben","e=>{e.checked=true;e.dispatchEvent(new Event('change'))}"); pg.wait_for_timeout(300)
    print("7. after 'unused benefits' filter, rows:", pg.locator("#tbl tbody tr").count())
    pg.eval_on_selector("#fben","e=>{e.checked=false;e.dispatchEvent(new Event('change'))}")
    pg.eval_on_selector("#cap","e=>{e.value=80;e.dispatchEvent(new Event('input'))}"); pg.wait_for_timeout(200)

    # 8 sort
    pg.locator('#tbl thead th[data-s="months"]').click(); pg.wait_for_timeout(200)
    print("8. sort by lapsed, top row:", pg.locator("#tbl tbody tr").first.inner_text().split("\n")[0:3])

    # 9 campaign
    pg.locator("#run").click(); pg.wait_for_timeout(900)
    cards = pg.locator("#cwrap .card")
    print("9. campaign cards:", [cards.nth(i).inner_text().split("\n")[1] for i in range(4)])
    cw = pg.evaluate("()=>{const c=document.querySelector('#cv');return c?c.width+'x'+c.height:'NO CANVAS'}")
    print("   canvas:", cw)
    if "NO" in cw: fails.append("chart canvas missing")

    # 10 CSV export
    with pg.expect_download() as dl:
        pg.locator("#run").click(); pg.wait_for_timeout(200)
        pg.locator('.tabs button[data-tab="list"]').click(); pg.wait_for_timeout(200)
        pg.locator("#csv").click()
    d = dl.value; path="/tmp/worklist.csv"; d.save_as(path)
    import os; print("10. CSV:", d.suggested_filename, os.path.getsize(path), "bytes")

    print("\nconsole errors:", errs[:5] if errs else "none")
    if errs: fails.append("console errors: %s" % errs[:2])
    b.close()
print("\n=== FAILURES:", fails if fails else "NONE - all 10 checks passed")
