import re,ssl,time,json,urllib.request,concurrent.futures as cf
UA="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36"
ctx=ssl.create_default_context();ctx.check_hostname=False;ctx.verify_mode=ssl.CERT_NONE
def get(u,t=25):
    r=urllib.request.Request(u,headers={"User-Agent":UA,"Accept":"text/html,*/*;q=0.8","Accept-Language":"es-CL,es;q=0.9","Accept-Encoding":"identity"})
    t0=time.time()
    with urllib.request.urlopen(r,timeout=t,context=ctx) as x:
        ttfb=time.time()-t0; b=x.read(6_000_000); return x.geturl(),round(ttfb,2),round(time.time()-t0,2),b
def probe(d):
    o={"domain":d}
    try: url,ttfb,total,b=get("https://"+d)
    except Exception as e:
        try: url,ttfb,total,b=get("http://"+d)
        except Exception as e2: return {"domain":d,"error":str(e2)[:60]}
    h=b.decode("utf-8","ignore"); l=h.lower()
    o.update(final=url,https=url.startswith("https"),ttfb=ttfb,load=total,kb=round(len(b)/1024))
    blob=h
    for s in re.findall(r'<script[^>]+src=["\']([^"\']+)',h)[:14]:
        if s.startswith("//"): s="https:"+s
        elif s.startswith("/"): s=re.match(r'https?://[^/]+',url).group(0)+s
        elif not s.startswith("http"): s=url.rstrip("/")+"/"+s
        try: blob+=get(s,10)[3].decode("utf-8","ignore")[:400000]
        except Exception: pass
    sig={"three.js":r'THREE\.|three(?:\.module|\.min)?\.js|@react-three|WebGLRenderer\(',"webgl":r'getContext\(["\'](?:webgl2?|experimental-webgl)',"shaders":r'gl_FragColor|gl_Position|precision (?:high|medium)p float|uniform (?:float|vec[234]|sampler2D)',"gsap":r'gsap\.(?:to|timeline|registerPlugin)|gsap(?:\.min)?\.js',"lenis":r'\bLenis\b|locomotive-scroll',"lottie":r'lottie|bodymovin',"spline":r'@splinetool|spline\.design',"video":r'<video[^>]+autoplay'}
    o["fx"]=[k for k,rx in sig.items() if re.search(rx,blob)]
    o["realtime"]=any(k in o["fx"] for k in ("three.js","webgl","shaders","spline"))
    stack="a medida"
    for k,lab in [("elementor","WordPress + Elementor"),("wp-content","WordPress"),("webflow","Webflow"),("framer","Framer"),("hs-scripts","HubSpot"),("hubspot","HubSpot"),("wixstatic","Wix"),("squarespace","Squarespace"),("shopify","Shopify"),("_next/","Next.js (a medida)"),("data-astro","Astro (a medida)"),("_nuxt","Nuxt (a medida)")]:
        if k in l: stack=lab; break
    o["stack"]=stack
    t=re.search(r"<title[^>]*>(.*?)</title>",h,re.S|re.I); title=re.sub(r"\s+"," ",t.group(1)).strip() if t else ""
    o["title"]=title[:90]; o["title_ok"]=25<=len(title)<=70
    o["description"]='name="description"' in l or "name='description'" in l
    o["h1"]=len(re.findall(r"<h1",l)); o["og"]="og:image" in l; o["viewport"]="viewport" in l
    o["schema"]=len(re.findall(r'application/ld\+json',l)); o["hreflang"]="hreflang" in l
    gen=re.search(r'name=["\']generator["\'][^>]*content=["\']([^"\']+)',h,re.I); o["generator"]=gen.group(1)[:40] if gen else ""
    host=re.match(r'https?://[^/]+',url).group(0)
    for p,k in [("/sitemap.xml","sitemap"),("/sitemap_index.xml","sitemap2"),("/sitemap-index.xml","sitemap3"),("/llms.txt","llms")]:
        try:
            u2,_,_,b2=get(host+p,10); s2=b2[:400].decode("utf-8","ignore").lower(); o[k]=("<urlset" in s2 or "<sitemapindex" in s2) if k!="llms" else ("<html" not in s2 and len(b2)>40)
        except Exception: o[k]=False
    o["sitemap"]=o["sitemap"] or o.get("sitemap2") or o.get("sitemap3"); o.pop("sitemap2",None); o.pop("sitemap3",None)
    return o
D="""stratus.cl bigbuda.cl loup.cl want.cl nexbu.com cebra.com nexodigital.cl jelly.cl rompecabeza.cl seonetdigital.cl solitario.studio sa.design d85.cl inbrax.cl dive.cl bluxstudio.com solerdev.com altai.cl milasantiago.com visualchile.cl crea7ive.cl innovaweb.cl studiochile.cl tuwebchile.cl raven.cl devartisan.cl gitz.cl infograf.cl bloom.cl nnodes.com magnet.cl bluecompany.cl 4alabs.cl arkano.cl acidlabs.com koncept.cl lisit.cl urbanmarketing.cl mullerperez.cl postedin.com seoaustral.cl jorgeurzuaseo.cl agenciaweb.cl webchile.cl paginasweb.cl disenoweb.cl ideativa.cl vuelodigital.cl cuatrosur.cl bunker.cl""".split()
with cf.ThreadPoolExecutor(10) as ex: R=list(ex.map(probe,D))
json.dump(R,open("ranking_raw.json","w",encoding="utf-8"),ensure_ascii=False,indent=1)
ok=[r for r in R if "error" not in r]; print("ok:",len(ok),"errores:",[r["domain"] for r in R if "error" in r])
for r in ok: print(f'{r["domain"]:22s} {r["kb"]:6d}KB ttfb={r["ttfb"]:5.2f} {r["stack"][:22]:22s} rt={int(r["realtime"])} fx={",".join(r["fx"])[:30]:30s} schema={r["schema"]} desc={int(r["description"])} h1={r["h1"]} og={int(r["og"])} sm={int(bool(r["sitemap"]))} llms={int(r["llms"])} | {r["title"][:50]}')
