import asyncio

from curl_cffi.requests import AsyncSession
from curl_cffi.requests.exceptions import RequestException


async def _solve_capmonster(cap_key, proxy, html, target_url, ua, timeout, _log):
    async with AsyncSession(timeout=20) as s:
        try:
            r = await s.post("https://api.capmonster.cloud/createTask", json={
                "clientKey": cap_key,
                "task": {
                    "type": "TurnstileTask",
                    "websiteURL": target_url,
                    "websiteKey": "xxxxxxxxxx",
                    "cloudflareTaskType": "cf_clearance",
                    "htmlPageBase64": html,
                    "userAgent": ua,
                    "proxyType": "http",
                    "proxyAddress": proxy.host,
                    "proxyPort": int(proxy.port),
                    "proxyLogin": proxy.username,
                    "proxyPassword": proxy.password,
                },
            })
            data = r.json()
        except (RequestException, ValueError) as e:
            await _log(f"[CM] createTask error: {e}")
            return ""

        if data.get("errorId"):
            await _log(f"[CM] error: {data.get('errorDescription', data)}")
            return ""
        if not (tid := data.get("taskId")):
            await _log(f"[CM] no taskId: {data}")
            return ""

        await _log(f"[CM] taskId={tid}, polling")
        deadline = asyncio.get_running_loop().time() + timeout
        while asyncio.get_running_loop().time() < deadline:
            await asyncio.sleep(3)
            try:
                r = await s.post("https://api.capmonster.cloud/getTaskResult",
                                 json={"clientKey": cap_key, "taskId": tid})
                poll = r.json()
            except (RequestException, ValueError):
                continue

            if poll.get("status") == "ready":
                sol = poll.get("solution") or {}
                if cf := sol.get("cf_clearance") or sol.get("token", ""):
                    await _log(f"[CM] solved: {cf[:40]}")
                    return cf
                await _log(f"[CM] ready but no cf_clearance: {poll}")
                return ""
            if poll.get("status") != "processing":
                await _log(f"[CM] failed: {poll}")
                return ""

        await _log("[CM] timeout")
        return ""


async def _solve_capsolver(cap_key, proxy, html, target_url, ua, timeout, _log):
    async with AsyncSession(timeout=20) as s:
        try:
            r = await s.post("https://api.capsolver.com/createTask", json={
                "clientKey": cap_key,
                "task": {
                    "type": "AntiCloudflareTask",
                    "websiteURL": target_url,
                    "userAgent": ua,
                    "html": html,
                    "proxy": proxy.raw,
                },
            })
            data = r.json()
        except (RequestException, ValueError) as e:
            await _log(f"[CS] createTask error: {e}")
            return ""

        if data.get("errorId"):
            await _log(f"[CS] error: {data.get('errorDescription', data)}")
            return ""
        if not (tid := data.get("taskId")):
            await _log(f"[CS] no taskId: {data}")
            return ""

        await _log(f"[CS] taskId={tid}, polling")
        deadline = asyncio.get_running_loop().time() + timeout
        while asyncio.get_running_loop().time() < deadline:
            await asyncio.sleep(2)
            try:
                r = await s.post("https://api.capsolver.com/getTaskResult",
                                 json={"clientKey": cap_key, "taskId": tid})
                poll = r.json()
            except (RequestException, ValueError):
                continue

            if poll.get("status") == "ready":
                sol = poll.get("solution") or {}
                cf = sol.get("cookies", {}).get("cf_clearance") or sol.get("token", "")
                if cf:
                    await _log(f"[CS] solved: {cf[:40]}")
                    return cf
                await _log(f"[CS] ready but no cf_clearance: {poll}")
                return ""
            if poll.get("status") == "failed" or poll.get("errorId"):
                await _log(f"[CS] failed: {poll}")
                return ""

        await _log("[CS] timeout")
        return ""


async def solve_cf(solver, cap_key, proxy, html, target_url, ua, timeout, log_fn=None):
    async def _log(msg):
        if log_fn:
            await log_fn(msg)

    if solver == "capsolver":
        return await _solve_capsolver(cap_key, proxy, html, target_url, ua, timeout, _log)
    
    return await _solve_capmonster(cap_key, proxy, html, target_url, ua, timeout, _log)
