#!/usr/bin/env python3
"""Input-Event Entropy — 2-test validation."""
import asyncio, json, subprocess, time, os, glob
from playwright.async_api import async_playwright

PORT = 9350
CHROME = "/usr/bin/google-chrome"
if not os.path.exists(CHROME):
    cands = glob.glob("/home/huxley/.cache/ms-playwright/chromium_headless_shell-*/chrome-headless-shell-linux64/chrome-headless-shell")
    if cands: CHROME = cands[0]
print(f"Using CHROME: {CHROME}")


async def run_one(p, use_cdp):
    if use_cdp:
        proc = subprocess.Popen([CHROME, "--headless=new", "--no-sandbox", "--disable-gpu",
                                  f"--remote-debugging-port={PORT}", "--user-data-dir=/tmp/ie_test_cdp",
                                  "about:blank"],
                                 stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
        time.sleep(3)
        try:
            browser = await p.chromium.connect_over_cdp(f"http://localhost:{PORT}")
            ctx = browser.contexts[0] if browser.contexts else await browser.new_context()
            page = ctx.pages[0] if ctx.pages else await ctx.new_page()
        except Exception as e:
            proc.terminate()
            return {"error": f"CDP connect failed: {e}"}
    else:
        browser = await p.chromium.launch(headless=True, args=["--no-sandbox", "--disable-setuid-sandbox", "--disable-dev-shm-usage"])
        page = await (await browser.new_context()).new_page()

    try:
        await page.goto("http://127.0.0.1:3457/spikes/input-entropy/demo.html", wait_until="networkidle", timeout=15000)

        # Use Playwright's page.mouse.move (real input events) instead of dispatchEvent.
        # 60 moves in a 400x400 box with realistic variance. Move during the 8s collection window.
        await page.click("#collect-start")
        # 60 events over 7 seconds (8s window, leave 1s buffer)
        n = 60
        for i in range(n):
            t = 7000 / n  # ms per event
            # Synthetic clustered pattern (mimics bot — center-bias, low variance)
            x = 200 + (i * 7) % 200
            y = 200 + (i * 5) % 150
            await page.mouse.move(x, y)
            await page.wait_for_timeout(t)

        # Wait for auto-analyze (fires at 8s mark)
        await page.wait_for_timeout(1500)
        text = await page.inner_text("#results")
        cd = await page.inner_text("#countdown")
        await browser.close()
    finally:
        if use_cdp:
            proc.terminate()
            time.sleep(1)
            if proc.poll() is None: proc.kill()
    return {"countdown": cd, "results": text}


def parse(text):
    out = {}
    # Format: "60 events | Vel Entropy: 0.844 | ApEn: 0.110 | Dir Entropy: 0.589"
    for line in text.split("\n"):
        line = line.strip()
        if "Vel Entropy:" in line:
            for part in line.split("|"):
                if "Vel Entropy" in part:
                    try: out["velEntropy"] = float(part.split(":")[-1].strip())
                    except: pass
                if "Dir Entropy" in part:
                    try: out["dirEntropy"] = float(part.split(":")[-1].strip())
                    except: pass
                if "ApEn" in part:
                    try: out["apen"] = float(part.split(":")[-1].strip())
                    except: pass
                if "IEI" in part and "CV=" in part:
                    try: out["ieiCV"] = float(part.split("CV=")[-1].rstrip(")").strip())
                    except: pass
        if "events |" in line and "Vel Entropy" not in line:
            try: out["nMouse"] = int(line.split("events")[0].strip().split()[-1])
            except: pass
    # Check the raw session data dump at the bottom (JSON block)
    if '"velEntropy":' in text:
        try:
            import re
            m = re.search(r'"velEntropy":\s*([\d.]+)', text)
            if m and "velEntropy" not in out: out["velEntropy"] = float(m.group(1))
            m = re.search(r'"dirEntropy":\s*([\d.]+)', text)
            if m and "dirEntropy" not in out: out["dirEntropy"] = float(m.group(1))
            m = re.search(r'"apen":\s*([\d.]+)', text)
            if m and "apen" not in out: out["apen"] = float(m.group(1))
            m = re.search(r'"ieiCV":\s*([\d.]+)', text)
            if m and "ieiCV" not in out: out["ieiCV"] = float(m.group(1))
        except: pass
    return out


async def main():
    async with async_playwright() as p:
        v = await run_one(p, use_cdp=False)
        c = await run_one(p, use_cdp=True)

    print("=" * 60)
    print("TEST 1: Vanilla headless (no CDP)")
    print("=" * 60)
    print(f"Countdown: {v.get('countdown', '?')[:200]}")
    print(f"Results: {v.get('results', '?')[:1500]}")
    pv = parse(v.get("results", ""))

    print("=" * 60)
    print("TEST 2: Chrome --remote-debugging-port (CDP attached)")
    print("=" * 60)
    print(f"Countdown: {c.get('countdown', '?')[:200]}")
    print(f"Results: {c.get('results', '?')[:1500]}")
    pc = parse(c.get("results", ""))

    print("=" * 60)
    print("VERDICT")
    print("=" * 60)
    print(f"  Vanilla: {pv}")
    print(f"  CDP:     {pc}")
    if pv and pc:
        dv = abs(pv.get("velEntropy", 0) - pc.get("velEntropy", 0))
        dd = abs(pv.get("dirEntropy", 0) - pc.get("dirEntropy", 0))
        print(f"  Δvel={dv:.3f}  Δdir={dd:.3f}")
        if dv > 0.5 or dd > 0.5:
            print("  ⚠️  CDP-discriminator (DIVERGENCE).")
        else:
            print("  ✅ Convergence. Headless-vs-headful signal (synthetic input vs real human).")
    else:
        print("  ⚠️  Incomplete results — nMouse likely 0.")


asyncio.run(main())
