import time,re,json,requests,random,math,io
import numpy as np
from PIL import Image, ImageFilter
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 calc_offset(bg_bytes, sl_bytes, top):
    bg=Image.open(io.BytesIO(bg_bytes)).convert('RGB'); bg_a=np.array(bg).astype(np.float32)
    sl=Image.open(io.BytesIO(sl_bytes)).convert('RGBA'); sa=np.array(sl).astype(np.float32)
    mask=sa[:,:,3]>50; h,w=mask.shape; H,W,_=bg_a.shape
    gray=bg_a.mean(axis=2); best=(1e9,0)
    for x in range(0,W-w+1):
        y=max(0,min(H-h,int(round(top))))
        reg=gray[y:y+h,x:x+w]
        m=reg[mask].mean()
        if m<best[0]: best=(m,x)
    return best[1], bg.size, sl.size

def make_driver():
    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/')
    return webdriver.Firefox(options=o)

for attempt in range(1,4):
    d=make_driver(); 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 && document.querySelectorAll('.geetest_btn').length>0"): break
        time.sleep(1.5)
        info=d.execute_script("""
          function r(sel){var e=document.querySelector(sel); if(!e)return null; var b=e.getBoundingClientRect(); var s=getComputedStyle(e); return {t:b.top,l:b.left,w:b.width,h:b.height,bg:s.backgroundImage};}
          return {bg:r('.geetest_bg'),slice:r('.geetest_slice'),slicebg:r('.geetest_slice_bg'),btn:r('.geetest_btn'),track:r('.geetest_track')};
        """)
        if not info or not info.get('bg'): print('attempt',attempt,'no captcha'); d.quit(); continue
        def dl(u):
            m=re.search(r'url\("?([^")]+)"?\)',u or ''); 
            if not m: return None
            url=m.group(1).replace('http://','https://')
            return requests.get(url,timeout=20,verify=False).content
        bg=dl(info['bg']['bg']); sl=dl(info['slicebg']['bg'])
        if not bg or not sl: print('attempt',attempt,'img fail'); d.quit(); continue
        top_css=info['slice']['t']-info['bg']['t']
        x_nat,bgsz,slsz=calc_offset(bg,sl,top_css*(Image.open(io.BytesIO(bg)).size[1]/info['bg']['h']))
        scale=info['bg']['w']/bgsz[0]
        drag=x_nat*scale
        print(f'attempt {attempt}: nat_x={x_nat} bg={bgsz} css={info["bg"]["w"]:.2f} scale={scale:.4f} drag={drag:.2f} top_css={top_css:.2f}')
        btn=d.find_element(By.CSS_SELECTOR,'.geetest_btn')
        ActionChains(d).move_to_element(btn).perform(); time.sleep(random.uniform(0.2,0.5))
        ActionChains(d).click_and_hold(btn).perform(); time.sleep(random.uniform(0.1,0.25))
        steps=random.randint(28,40); moved=0.0
        for i in range(1,steps+1):
            t=i/steps; p=1-(1-t)**3
            target=drag*p + (2.5 if i==steps else 0)
            dx=target-moved; moved=target
            ActionChains(d).move_by_offset(dx,random.uniform(-0.4,0.4)).perform()
            time.sleep(random.uniform(0.012,0.035))
        time.sleep(random.uniform(0.15,0.3))
        # correction back
        ActionChains(d).move_by_offset(-2.0,0).perform(); time.sleep(0.12)
        ActionChains(d).release().perform()
        res=None
        for _ in range(24):
            time.sleep(0.5)
            res=d.execute_script('return window.validateResult')
            if res: break
            err=d.execute_script('return window.gtError')
            if err: print(' gtError',err)
        print('RESULT',json.dumps(res,ensure_ascii=False) if res else None)
        if res:
            open('gt_result.json','w').write(json.dumps(res,ensure_ascii=False))
            d.quit(); break
        d.quit()
    except Exception as e:
        print('attempt',attempt,'ERR',str(e)[:200])
        try: d.quit()
        except: pass
