#!/usr/bin/env python3
"""Fill the already-open (or re-opened) Change Password modal and submit it,
capturing the real GraphQL request URL + status. Secrets typed into page only;
only URLs/statuses/op-name printed. Reads creds from env file."""
import json, urllib.request, time
from websocket import create_connection

def read_key(k):
    for line in open("/home/claude/.config/amd-agent/credentials.env"):
        if line.startswith(k+"="): return line[len(k)+1:].rstrip("\n")
    return ""
OLD=read_key("CUROGRAM_AGENT_PASSWORD"); NEW=read_key("AMD_PASSWORD")

tabs=json.loads(urllib.request.urlopen("http://localhost:9223/json",timeout=8).read())
page=next(t for t in tabs if t.get("type")=="page" and "app.curogram.com" in (t.get("url") or "").lower())
ws=create_connection(page["webSocketDebuggerUrl"],timeout=30); _id=[0]
reqs={}; captured=[]
def send(method,params=None):
    _id[0]+=1; mid=_id[0]; ws.send(json.dumps({"id":mid,"method":method,"params":params or {}})); return mid
def pump(mid=None, secs=0):
    end=time.time()+(secs or 30); res=None
    while time.time()<end:
        ws.settimeout(max(0.2,end-time.time()))
        try: m=json.loads(ws.recv())
        except Exception: break
        meth=m.get("method")
        if meth=="Network.requestWillBeSent":
            p=m["params"]; u=p["request"]["url"]
            if "curogram.com" in u:
                pd=p["request"].get("postData","") or ""
                if "graphql" in u or "assword" in u or "assword" in pd or "authenticate" in u:
                    reqs[p["requestId"]]={"url":u,"pd":pd}
        elif meth=="Network.responseReceived":
            rid=m["params"]["requestId"]
            if rid in reqs: reqs[rid]["status"]=m["params"]["response"]["status"]
        elif meth=="Network.loadingFinished":
            rid=m["params"]["requestId"]
            if rid in reqs and not any(c["rid"]==rid for c in captured):
                pd=reqs[rid].get("pd","")
                isp = ("etPassword" in pd) or ("hangePassword" in pd)
                captured.append({"rid":rid,"url":reqs[rid]["url"],"status":reqs[rid].get("status"),"isPwOp":isp})
                if isp:
                    # try to fetch response body for the pw op
                    bmid=send("Network.getResponseBody",{"requestId":rid})
                    # body handled below by id match
        if mid and m.get("id")==mid and secs==0:
            if "error" in m: raise RuntimeError(m["error"])
            return m.get("result",{})
    return res
def ev(expr, awaitp=False):
    mid=send("Runtime.evaluate",{"expression":expr,"returnByValue":True,"awaitPromise":awaitp,"timeout":15000})
    r=pump(mid)
    return r.get("result",{}).get("value") if r else None

pump(send("Runtime.enable"))
pump(send("Network.enable"))

# Re-open modal if not present (idempotent), then fill + submit.
open_js = r"""
(()=>{
  let ins=[...document.querySelectorAll('input[type=password]')];
  if(ins.length<3){
    const els=[...document.querySelectorAll('span,div,a,button')]
      .filter(e=>/^\s*Change Password\s*$/i.test((e.innerText||e.textContent||'')));
    const t=els.find(e=>/menu-item|action/i.test(e.className))||els[0];
    if(t) t.click();
  }
  return 'ok';
})()
"""
ev(open_js); time.sleep(1.2)

fill_js = """
((oldp,newp)=>{
  const ins=[...document.querySelectorAll('input[type=password]')];
  if(ins.length<3) return 'ONLY_'+ins.length+'_INPUTS';
  const setVal=(el,v)=>{
    const proto=Object.getPrototypeOf(el);
    const desc=Object.getOwnPropertyDescriptor(proto,'value');
    desc.set.call(el,v);
    el.dispatchEvent(new Event('input',{bubbles:true}));
    el.dispatchEvent(new Event('change',{bubbles:true}));
    el.dispatchEvent(new Event('blur',{bubbles:true}));
  };
  setVal(ins[0],oldp); setVal(ins[1],newp); setVal(ins[2],newp);
  return 'FILLED';
})
"""
print("fill", ev(f"({fill_js})({json.dumps(OLD)},{json.dumps(NEW)})"))
time.sleep(0.6)

# find + click submit button inside the modal
submit_js = r"""
(()=>{
  const btns=[...document.querySelectorAll('button,[role=button],input[type=submit]')];
  // candidate by text
  let b=btns.find(x=>/save|change|update|confirm|submit/i.test((x.innerText||x.textContent||x.value||'')) && !x.disabled);
  if(!b){
    // fallback: the modal's primary button (last enabled non-cancel)
    b=btns.filter(x=>!/cancel|close/i.test((x.innerText||x.textContent||'')) && !x.disabled).pop();
  }
  if(!b) return 'NO_SUBMIT';
  b.click();
  return 'SUBMIT_CLICKED:'+((b.innerText||b.textContent||b.value||'').trim().slice(0,30));
})()
"""
print("submit", ev(submit_js))
# collect network for a few seconds
pump(secs=6)
print("CAPTURED", json.dumps(captured))
ws.close()
