import time,re,json,requests,io,random,math
import numpy as np
from PIL import Image
from scipy import ndimage
from selenium import webdriver
from selenium.webdriver.firefox.options import Options
from selenium.webdriver.common.by import By
from selenium.webdriver.common.action_chains import ActionChains
urllib3=__import__('urllib3'); urllib3.disable_warnings()

def mkdrv():
    o=Options(); o.set_preference('dom.webdriver.enabled',False)
    o.set_preference('network.http.referer.spoofSource',True)
    o.set_preference('network.http.referer.defaultReferrer','https://ein38eu95792b.4656hd15.cc/')
    o.set_preference('general.useragent.override','Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:136.0) Gecko/20100101 Firefox/136.0')
    return webdriver.Firefox(options=o)

def gap_x(bg, sl, top):
    g=np.array(bg.convert('L')).astype(np.float32); sa=np.array(sl); mask=sa[:,:,3]>128
    ys,xs=np.where(mask); ax0,ay0=xs.min(),ys.min(); aw,ah=xs.max()-ax0+1,ys.max()-ay0+1
    H,W=g.shape; cands=[]
    for th in [45,50,55,60,65,70,75,80,85,90,95,100]:
        dark=g<th; lab,n=ndimage.label(dark)
        for i in range(1,n+1):
            yy,xx=np.where(lab==i); a=len(xx)
            if a<300: continue
            w=xx.max()-xx.min()+1; h=yy.max()-yy.min()+1
            if abs(w-aw)<=20 and abs(h-ah)<=20:
                cands.append((int(xx.min())-int(ax0),abs(w-aw)+abs(h-ah)))
    if cands:
        center=min(cands,key=lambda c:c[1])[0]; lo=max(0,center-12); hi=min(W-aw-ax0,center+12)
    else:
        lo,hi=0,W-aw-ax0
    best=(1e9,lo)
    for x in range(lo,hi+1):
        reg=g[top:top+ah,x+ax0:x+ax0+aw]; m2=mask[ay0:ay0+ah,ax0:ax0+aw]
        v=reg[m2].mean()
        if v<best[0]: best=(v,x)
    return best[1],cands

for attempt in range(1,4):
    d=mkdrv(); d.set_page_load_timeout(40)
    try:
        d.get('http://127.0.0.1:9999/geetest_page.html')
        for _ in range(30):
            time.sleep(0.4)
            if d.execute_script("return document.querySelectorAll('.geetest_bg').length>0"): break
        time.sleep(random.uniform(1.0,2.0))
        info=d.execute_script("""function r(s){var e=document.querySelector(s);if(!e)return null;var b=e.getBoundingClientRect();return {t:b.top,l:b.left,w:b.width,h:b.height,bg:getComputedStyle(e).backgroundImage};}return {bg:r('.geetest_bg'),slice:r('.geetest_slice'),slicebg:r('.geetest_slice_bg'),btn:r('.geetest_btn')};""")
        def dl(u):
            m=re.search(r'url\("?([^")]+)"?\)',u or '');return requests.get(m.group(1).replace('http://','https://'),timeout=20,verify=False).content
        bg=Image.open(io.BytesIO(dl(info['bg']['bg']))).convert('RGB'); sl=Image.open(io.BytesIO(dl(info['slicebg']['bg']))).convert('RGBA')
        top=int(round((info['slice']['t']-info['bg']['t'])*bg.size[1]/info['bg']['h'])); top=max(0,min(bg.size[1]-sl.size[1],top))
        x,cands=gap_x(bg,sl,top); scale=info['bg']['w']/bg.size[0]; D=x*scale
        print(f'attempt {attempt} x={x} D={D:.1f} cands={cands[:4]} top={top}')
        btn=d.find_element(By.CSS_SELECTOR,'.geetest_btn')
        # random start point inside button
        ActionChains(d).move_to_element_with_offset(btn,random.randint(-8,8),random.randint(-3,3)).perform(); time.sleep(random.uniform(0.2,0.5))
        ActionChains(d).click_and_hold().perform(); time.sleep(random.uniform(0.12,0.28))
        # slight backward jitter
        for _ in range(random.randint(1,3)):
            ActionChains(d).move_by_offset(-random.uniform(0.5,1.5),random.uniform(-0.5,0.5)).perform(); time.sleep(random.uniform(0.02,0.05))
        over=random.uniform(2,4.5); total=D+over
        n=random.randint(38,58); moved=0.0; last_t=0
        pause_at=set(random.sample(range(5,n-5),random.randint(1,3)))
        exp=random.uniform(2.0,3.6)
        for i in range(1,n+1):
            t=i/n; p=1-(1-t)**exp
            target=total*p + random.uniform(-0.5,0.5)
            dx=target-moved; moved=target
            dy=random.uniform(-0.7,0.7) if random.random()<0.25 else 0
            ActionChains(d).move_by_offset(dx,dy).perform()
            if i in pause_at: time.sleep(random.uniform(0.05,0.14))
            else: time.sleep(random.uniform(0.012,0.038))
        time.sleep(random.uniform(0.08,0.18))
        # correct back
        for i in range(1,random.randint(4,7)+1):
            ActionChains(d).move_by_offset(-over/ (random.randint(4,7)), random.uniform(-0.3,0.3)).perform(); time.sleep(random.uniform(0.02,0.05))
        time.sleep(random.uniform(0.1,0.2))
        ActionChains(d).release().perform()
        val=None
        for _ in range(20):
            time.sleep(0.5)
            val=d.execute_script('return window.validateResult')
            if val: break
        print('  validate',json.dumps(val,ensure_ascii=False) if val else None)
        print('  result',d.execute_script("var e=document.querySelector('.geetest_result');return e?e.innerText:null"))
        if val:
            open('gt_result.json','w').write(json.dumps(val,ensure_ascii=False)); d.quit(); break
        d.quit()
    except Exception as e:
        print('attempt',attempt,'ERR',str(e)[:160])
        try:d.quit()
        except:pass
