#!/usr/bin/env python3
"""
Clawdi.ai Registration via VPS + Residential Proxy
All Clawdi/Clerk requests go through proxy to use residential IP
"""
import httpx
import json
import time
import re
import random
import string
import uuid
import sys
import datetime
import os

YYDS_API_KEY = "AC-29e9b09c5ae93e1347debb66"
YYDS_BASE = "https://maliapi.215.im/v1"
SOLVER_URL = "http://localhost:5000"
PROXY_URL = os.environ.get("PROXY_URL", "http://127.0.0.1:2080")

CLERK_API = "https://clerk.clawdi.ai"
CLAWDI_API = "https://api.clawdi.ai"
SITEKEY = "0x4AAAAAAAWXJGBD7bONzLBd"
SITEURL = "https://accounts.clawdi.ai/sign-up"
CLERK_JS_VERSION = "6.12.1"
API_VERSION = "2025-04-10"
PASSWORD = "Cl@wdi2026xY7!"
REF_CODE = "AV476KVN"

PREFERRED_DOMAINS = [
    "007.hzeg.eu.org", "0m0.abrdns.com", "0m0.app",
    "3jsfuye.tech", "15768.xyz", "20220108.xyz", "100811.xyz",
]

BROWSER_HEADERS = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36",
    "Accept": "application/json",
    "Content-Type": "application/x-www-form-urlencoded",
    "Origin": "https://accounts.clawdi.ai",
    "Referer": "https://accounts.clawdi.ai/sign-up",
}

OUTPUT_FILE = "clawdi_ref_accounts.json"


def pclient(**kwargs):
    kwargs.setdefault("proxy", PROXY_URL)
    return httpx.Client(**kwargs)


def rand_name(length=10):
    return ''.join(random.choices(string.ascii_lowercase + string.digits, k=length))


def capture_referral(ref_code, device_token):
    print(f"\n[0] Capturing referral: {ref_code}")
    with pclient(timeout=15) as c:
        r = c.post(f"{CLAWDI_API}/referrals/capture",
            headers={"Content-Type": "application/json"},
            json={"code": ref_code, "device_token": device_token, "capture_surface": "shortlink"})
        if r.status_code in (200, 201):
            data = r.json()
            print(f"  ✅ referrer: {data.get('referrer_display_name')} ({data.get('referrer_user_id')})")
            return data
        print(f"  [!] Capture failed: {r.status_code} {r.text[:200]}")
        return None


def identify_referral(session_token, device_token, anonymous_token=None, ref_code=None):
    print(f"\n[7] Identifying referral...")
    with pclient(timeout=15) as c:
        body = {"device_token": device_token, "capture_surface": "onboarding"}
        if anonymous_token:
            body["anonymous_token"] = anonymous_token
        if ref_code:
            body["referral_code"] = ref_code
        r = c.post(f"{CLAWDI_API}/referrals/identify",
            headers={"Content-Type": "application/json", "Authorization": f"Bearer {session_token}"},
            json=body)
        print(f"  Status: {r.status_code} → {r.text[:200]}")
        return r.json() if r.status_code in (200, 201) else None


def yyds_create_email(domain=None):
    if domain is None:
        domain = random.choice(PREFERRED_DOMAINS)
    local_part = rand_name(10)
    with httpx.Client(timeout=15) as c:
        r = c.post(f"{YYDS_BASE}/accounts",
            headers={"X-API-Key": YYDS_API_KEY, "Content-Type": "application/json"},
            json={"localPart": local_part, "domain": domain})
        if r.status_code not in (200, 201):
            print(f"  [!] YYDS error: {r.status_code}")
            return None, None, None
        info = r.json()["data"]
        print(f"  📧 {info['address']}")
        return info["address"], info["token"], info["id"]


def yyds_get_code(email, token, max_wait=90):
    print(f"  Waiting for code...")
    start = time.time()
    with httpx.Client(timeout=15) as c:
        while time.time() - start < max_wait:
            time.sleep(5)
            try:
                r = c.get(f"{YYDS_BASE}/messages",
                    headers={"Authorization": f"Bearer {token}"},
                    params={"address": email, "limit": "5"})
                if r.status_code != 200:
                    continue
                msgs = r.json().get("data", {}).get("messages", [])
                for msg in msgs:
                    subj = msg.get("subject", "")
                    codes = re.findall(r'\b(\d{6})\b', subj)
                    if codes and ("verif" in subj.lower() or "code" in subj.lower()):
                        print(f"  ✅ Code: {codes[0]}")
                        return codes[0]
                    mid = msg.get("id")
                    r2 = c.get(f"{YYDS_BASE}/messages/{mid}",
                        headers={"Authorization": f"Bearer {token}"},
                        params={"address": email})
                    if r2.status_code == 200:
                        full = r2.json().get("data", {})
                        all_text = (full.get("text", "") or "") + " " + " ".join(full.get("html", []) or []) + " " + subj
                        codes = re.findall(r'\b(\d{6})\b', all_text)
                        if codes:
                            print(f"  ✅ Code: {codes[0]}")
                            return codes[0]
            except Exception:
                pass
    return None


def solve_turnstile(url=None, sitekey=None, max_wait=120):
    url = url or SITEURL
    sitekey = sitekey or SITEKEY
    print(f"  [Solver] Solving Turnstile (sitekey: {sitekey[:12]}...)")
    with httpx.Client(timeout=20) as c:
        r = c.post(f"{SOLVER_URL}/turnstile", json={"url": url, "sitekey": sitekey})
        if r.status_code != 202:
            print(f"  [!] Solver submit failed: {r.status_code} {r.text[:200]}")
            return None
        task_id = r.json().get("task_id")
        if not task_id:
            print(f"  [!] No task_id: {r.json()}")
            return None
        print(f"  [Solver] Task submitted: {task_id[:8]}...")
        start = time.time()
        while time.time() - start < max_wait:
            time.sleep(3)
            try:
                rr = c.get(f"{SOLVER_URL}/result", params={"id": task_id}, timeout=10)
                data = rr.json()
                status = data.get("status")
                if status == "success":
                    token = data.get("data", {}).get("token", "")
                    elapsed = data.get("data", {}).get("elapsed_time", 0)
                    if token:
                        print(f"  ✅ Turnstile solved ({len(token)} chars, {elapsed:.1f}s)")
                        return token
                    print(f"  [!] Success but no token")
                    return None
                elif status == "error":
                    error = data.get("error", "unknown")
                    print(f"  [!] Solver error: {error}")
                    return None
                elif status == "pending":
                    elapsed = time.time() - start
                    if int(elapsed) % 15 == 0:
                        print(f"  [Solver] Processing... {elapsed:.0f}s")
                else:
                    print(f"  [!] Unknown status: {status}")
                    return None
            except Exception as e:
                print(f"  [!] Poll error: {e}")
        print(f"  [!] Solver timeout after {max_wait}s")
        return None


def clerk_full_flow(email, password, captcha_token, yyds_token, yyds_address):
    with pclient(base_url=CLERK_API, follow_redirects=True, timeout=30,
                      headers=BROWSER_HEADERS) as client:
        params = {"__clerk_api_version": API_VERSION, "_clerk_js_version": CLERK_JS_VERSION}
        client.get("/v1/client", params=params)
        r = client.post("/v1/client/sign_ups", params=params, data={
            "email_address": email,
            "password": password,
            "captcha_token": captcha_token,
        })
        data = r.json()
        if data.get("errors"):
            print(f"  [!] Signup: {data['errors'][0].get('code')} - {data['errors'][0].get('message', '')[:80]}")
            return None
        resp = data["response"]
        sign_up_id = resp["id"]
        print(f"  signUp: {sign_up_id} ({resp.get('status')})")
        if "email_address" in (resp.get("unverified_fields") or []):
            client.post(f"/v1/client/sign_ups/{sign_up_id}/prepare_verification",
                params=params, data={"strategy": "email_code"})
            print(f"  Verification email sent")
        print(f"  Waiting for verification code...")
        code = yyds_get_code(yyds_address, yyds_token)
        if not code:
            print(f"  [!] No code received")
            return None
        print(f"  Submitting code: {code}")
        r = client.post(f"/v1/client/sign_ups/{sign_up_id}/attempt_verification",
            params=params, data={"strategy": "email_code", "code": code})
        vdata = r.json()
        if vdata.get("errors"):
            print(f"  [!] Verify: {vdata['errors'][0].get('message', '')[:80]}")
            return None
        vresp = vdata["response"]
        uid = vresp.get("created_user_id")
        print(f"  ✅ Verified! uid: {uid}")
        session_token = None
        sessions = vdata.get("client", {}).get("sessions", [])
        if sessions:
            session_token = sessions[0].get("last_active_token", {}).get("jwt", "")
        return {"uid": uid, "session_token": session_token, "sign_up_id": sign_up_id}


def clawdi_get_plans():
    with pclient(timeout=15) as c:
        r = c.get(f"{CLAWDI_API}/subscription/plans")
        if r.status_code == 200:
            return r.json()
        return None


def clawdi_get_subscription(session_token):
    with pclient(timeout=15) as c:
        r = c.get(f"{CLAWDI_API}/subscription/current",
            headers={"Authorization": f"Bearer {session_token}"})
        return r.status_code, r.json() if r.status_code == 200 else r.text[:300]


def clawdi_activation_fee(session_token):
    with pclient(timeout=15) as c:
        r = c.get(f"{CLAWDI_API}/subscription/activation-fee",
            headers={"Authorization": f"Bearer {session_token}"})
        return r.status_code, r.json() if r.status_code == 200 else r.text[:300]


def clawdi_addon_credits(session_token):
    with pclient(timeout=15) as c:
        r = c.get(f"{CLAWDI_API}/subscription/addon-credits",
            headers={"Authorization": f"Bearer {session_token}"})
        return r.status_code, r.json() if r.status_code == 200 else r.text[:300]


def register_one(num=1):
    print(f"\n{'='*60}")
    print(f"  Account #{num} (ref: {REF_CODE})")
    print(f"{'='*60}")
    device_token = str(uuid.uuid4())
    capture = capture_referral(REF_CODE, device_token)
    anonymous_token = capture.get("anonymous_token") if capture else None

    print(f"\n[1] Creating temp email...")
    email, yyds_token, yyds_id = yyds_create_email()
    if not email:
        return None

    print(f"\n[2] Solving Turnstile (CloakBrowser solver via proxy)...")
    token = solve_turnstile()
    if not token:
        return None

    print(f"\n[3] Registering + Verifying on Clerk...")
    result = clerk_full_flow(email, PASSWORD, token, yyds_token, email)
    if not result:
        return None
    uid = result["uid"]
    session_token = result["session_token"]

    if session_token:
        ref_result = identify_referral(session_token, device_token, anonymous_token, REF_CODE)
    else:
        ref_result = None

    if session_token:
        print(f"\n[8] Subscription info:")
        sub_status, sub_data = clawdi_get_subscription(session_token)
        print(f"  Current sub: {sub_status} → {json.dumps(sub_data)[:100] if isinstance(sub_data, dict) else sub_data[:100]}")
        fee_status, fee_data = clawdi_activation_fee(session_token)
        print(f"  Activation fee: {fee_status} → {json.dumps(fee_data) if isinstance(fee_data, dict) else fee_data[:100]}")
        credits_status, credits_data = clawdi_addon_credits(session_token)
        print(f"  Credits: {credits_status} → {json.dumps(credits_data) if isinstance(credits_data, dict) else credits_data[:100]}")

    result_data = {
        "email": email, "password": PASSWORD, "user_id": uid,
        "session_token": session_token, "ref_code": REF_CODE,
        "device_token": device_token, "anonymous_token": anonymous_token,
        "referral_captured": capture is not None,
        "referral_identified": ref_result is not None,
        "status": "complete",
        "registered_at": datetime.datetime.now().isoformat(),
    }
    print(f"\n{'='*60}")
    print(f"  ✅ Account #{num} done!")
    print(f"  Email:  {email}")
    print(f"  UserID: {uid}")
    print(f"  Ref:    {REF_CODE} (capture={'✅' if capture else '❌'} identify={'✅' if ref_result else '❌'})")
    print(f"{'='*60}")
    return result_data


def main():
    print("""
╔══════════════════════════════════════════════════════╗
║  Clawdi.ai Registration (VPS + Residential Proxy)      ║
╚══════════════════════════════════════════════════════╝
""")
    print(f"[*] Proxy: {PROXY_URL}")
    try:
        with pclient(timeout=10) as c:
            r = c.get("https://api.ipify.org")
            print(f"[*] Outbound IP: {r.text}")
    except Exception as e:
        print(f"[!] Proxy check failed: {e}")
        return

    print("[*] Checking Turnstile solver...")
    try:
        r = httpx.get(f"{SOLVER_URL}/", timeout=5)
        print(f"  ✅ Solver running at {SOLVER_URL}")
    except Exception as e:
        print(f"  ❌ Solver not reachable: {e}")
        return

    num = int(sys.argv[1]) if len(sys.argv) > 1 else 1

    print("\n[*] Available plans:")
    plans = clawdi_get_plans()
    if plans:
        for p in plans:
            print(f"  {p['slug']:10} {p['name']:10} ${p['price_cents']/100:.2f}/mo  {p['monthly_budget_credits']} credits")

    print(f"\n[*] Referral code: {REF_CODE}")
    try:
        with pclient(timeout=15) as c:
            r = c.post(f"{CLAWDI_API}/referrals/validate",
                headers={"Content-Type": "application/json"},
                json={"code": REF_CODE})
            if r.status_code == 200:
                d = r.json()
                print(f"  ✅ Valid! Referrer: {d.get('referrer_display_name')} ({d.get('referrer_user_id')})")
    except Exception as e:
        print(f"  [!] Skipped: {e}")
    print()

    results = []
    failed = 0
    for i in range(1, num + 1):
        result = register_one(i)
        if result:
            results.append(result)
        else:
            failed += 1
        if i < num:
            delay = random.uniform(3, 8)
            print(f"\n[*] Waiting {delay:.1f}s...")
            time.sleep(delay)

    print(f"\n{'='*60}")
    print(f"  Results: {len(results)} success, {failed} failed")
    print(f"{'='*60}")
    with open(OUTPUT_FILE, "w") as f:
        json.dump(results, f, indent=2, ensure_ascii=False)
    print(f"[*] Saved to {OUTPUT_FILE}")
    for r in results:
        print(f"  ✅ {r['email']} / {r['password']} / uid={r['user_id']} / ref={'✅' if r['referral_identified'] else '❌'}")


if __name__ == "__main__":
    main()