#!/usr/bin/env python3
"""Interactive MS 2FA enrollment driver over CDP (AGENT Chrome, port 9223).

Usage (run from tmux amdwork pane; repo bash hook blocks direct python3):
    python3 /tmp/amdwork/ms_driver.py <command> [args...]

Commands:
    newtab <url>            Create a new tab at url, print its target id + url.
    inspect [url_sub]       Attach to tab (default: most recent non-blank) and
                            dump URL/title/inputs/clickables/body-snippet to
                            /tmp/amdwork/state.txt (NON-SECRET; body truncated).
    type <sel> <value>      Focus selector, set value, fire input/change events.
    typeName <name> <value> Same but match by name= or aria-label/placeholder.
    click <sel>             Click selector.
    clicktext <text>        Click first visible element whose text == text.
    shot <path>             Screenshot to path.
    eval <expr>             Evaluate JS, print returnByValue.

The secret-capture command is in ms_capture.py (writes ONLY to creds file).

Tab selection: we always operate on the FRONT-MOST signin/security tab. We pick
the tab whose URL contains the most relevant MS host.
"""
import json, sys, time, os
sys.path.insert(0, "/home/claude/repos/openclaw/.claude/skills/advancedmd/scripts")
import amd_browser as A

MS_HOSTS = ("login.microsoftonline.com", "login.live.com", "mysignins.microsoft.com",
            "account.activedirectory", "microsoft.com", "office.com", "outlook.office",
            "aka.ms", "msauth", "login.microsoft")


def pick_target(url_sub=None):
    pages = A.list_pages()
    if url_sub:
        for p in pages:
            if url_sub in (p.get("url") or ""):
                return p
    # prefer an MS-host tab that is not blank
    for p in pages:
        u = (p.get("url") or "")
        if any(h in u for h in MS_HOSTS):
            return p
    for p in pages:
        u = (p.get("url") or "")
        if u and u != "about:blank":
            return p
    return pages[0] if pages else None


def attach(url_sub=None):
    t = pick_target(url_sub)
    if not t:
        raise RuntimeError("no tabs")
    pg = A.attach(t)
    pg.enable()
    return pg, t


def cmd_newtab(url):
    ver = A._http_get("/json/version")
    b = A.Page(ver["webSocketDebuggerUrl"])
    tid = b.send("Target.createTarget", {"url": url})["targetId"]
    b.close()
    time.sleep(3)
    # find it
    for p in A.list_pages():
        if p.get("id") == tid:
            print("NEWTAB", tid, p.get("url"))
            return
    print("NEWTAB", tid, "(url unknown)")


def cmd_inspect(url_sub=None):
    pg, t = attach(url_sub)
    out = []
    url = pg.eval("location.href")
    title = pg.eval("document.title")
    out.append("URL: %s" % url)
    out.append("TITLE: %s" % title)
    out.append("TARGET_URL_MATCH: %s" % t.get("url"))
    try:
        inputs = A.list_inputs(pg)
    except Exception as e:
        inputs = "ERR %s" % e
    out.append("INPUTS: %s" % json.dumps(inputs))
    try:
        clk = A.list_clickables(pg, limit=80)
    except Exception as e:
        clk = "ERR %s" % e
    out.append("CLICKABLES: %s" % json.dumps(clk))
    # body snippet — TRUNCATED, intended PHI/secret-free (security-info pages).
    try:
        body = pg.eval("(document.body&&document.body.innerText||'').slice(0,2500)")
    except Exception as e:
        body = "ERR %s" % e
    out.append("BODY:\n%s" % body)
    with open("/tmp/amdwork/state.txt", "w") as f:
        f.write("\n".join(out))
    pg.close()
    print("INSPECT_DONE -> /tmp/amdwork/state.txt url=%s" % url)


def _set_value(pg, find_js, value):
    jval = json.dumps(value)
    expr = (
        "(()=>{const el=%s; if(!el) return 'NOEL';"
        "el.focus();"
        "const proto=el.tagName==='TEXTAREA'?window.HTMLTextAreaElement.prototype:window.HTMLInputElement.prototype;"
        "const setter=Object.getOwnPropertyDescriptor(proto,'value').set;"
        "setter.call(el,%s);"
        "el.dispatchEvent(new Event('input',{bubbles:true}));"
        "el.dispatchEvent(new Event('change',{bubbles:true}));"
        "el.dispatchEvent(new KeyboardEvent('keyup',{bubbles:true}));"
        "return 'OK:'+el.tagName+'#'+(el.id||el.name||'');})()" % (find_js, jval)
    )
    return pg.eval(expr)


def cmd_type(sel, value):
    pg, _ = attach()
    find_js = "document.querySelector(%s)" % json.dumps(sel)
    print("TYPE", _set_value(pg, find_js, value))
    pg.close()


def cmd_typeName(name, value):
    pg, _ = attach()
    jn = json.dumps(name)
    find_js = ("(()=>{return document.querySelector('[name='+JSON.stringify(%s)+']')"
               "||[...document.querySelectorAll('input,textarea')].find(e=>e.offsetParent &&"
               "((e.getAttribute('aria-label')||'').includes(%s)||(e.placeholder||'').includes(%s)));})()"
               % (jn, jn, jn))
    print("TYPENAME", _set_value(pg, find_js, value))
    pg.close()


def cmd_click(sel):
    pg, _ = attach()
    try:
        r = A.click(pg, sel, timeout=10)
        print("CLICK", r)
    except Exception as e:
        print("CLICK_ERR", e)
    pg.close()


def cmd_clicktext(text):
    pg, _ = attach()
    try:
        r = A.click_text(pg, text, timeout=10)
        print("CLICKTEXT", r)
    except Exception as e:
        print("CLICKTEXT_ERR", e)
    pg.close()


def cmd_shot(path):
    pg, _ = attach()
    A.screenshot_path = pg.screenshot(path)
    print("SHOT", path)
    pg.close()


def cmd_enumbtns():
    pg, _ = attach()
    expr = ("JSON.stringify(Array.from(document.querySelectorAll('button,a,input')).map("
            "function(b){return {tag:b.tagName,t:(b.innerText||b.value||'').trim().slice(0,40),"
            "dis:!!b.disabled,vis:!!b.offsetParent};}).filter(function(x){return x.vis;}))")
    print("BTNS", pg.eval(expr))
    pg.close()


def cmd_clicktext2(text):
    """Click by exact text using real mouse events on the element center."""
    pg, _ = attach()
    jt = json.dumps(text)
    # find element, return its center coords
    expr = ("(function(){var els=Array.from(document.querySelectorAll('button,a,[role=button]'));"
            "var m=els.find(function(e){return e.offsetParent && (e.innerText||e.textContent||'').trim()===" + jt + ";});"
            "if(!m) return null; var r=m.getBoundingClientRect();"
            "return JSON.stringify({x:r.left+r.width/2,y:r.top+r.height/2});})()")
    pos = pg.eval(expr)
    if not pos:
        print("CLICKTEXT2 NOTFOUND"); pg.close(); return
    p = json.loads(pos)
    x, y = p["x"], p["y"]
    pg.send("Input.dispatchMouseEvent", {"type": "mouseMoved", "x": x, "y": y})
    pg.send("Input.dispatchMouseEvent", {"type": "mousePressed", "x": x, "y": y, "button": "left", "clickCount": 1})
    pg.send("Input.dispatchMouseEvent", {"type": "mouseReleased", "x": x, "y": y, "button": "left", "clickCount": 1})
    print("CLICKTEXT2 OK", x, y)
    pg.close()


def cmd_eval(expr):
    pg, _ = attach()
    try:
        v = pg.eval(expr)
        print("EVAL", json.dumps(v)[:1500])
    except Exception as e:
        print("EVAL_ERR", e)
    pg.close()


def main():
    if len(sys.argv) < 2:
        print("need command"); return
    c = sys.argv[1]
    a = sys.argv[2:]
    fns = {
        "newtab": cmd_newtab, "inspect": cmd_inspect, "type": cmd_type,
        "typeName": cmd_typeName, "click": cmd_click, "clicktext": cmd_clicktext,
        "shot": cmd_shot, "eval": cmd_eval,
        "enumbtns": cmd_enumbtns, "clicktext2": cmd_clicktext2,
    }
    if c not in fns:
        print("unknown command", c); return
    fns[c](*a)


if __name__ == "__main__":
    main()
