from __future__ import annotations

import asyncio
import logging
from typing import Any

from ..base import (
    CaptchaSolver,
    CaptchaTaskResult,
    CaptchaTaskSpec,
    captcha_task_handler,
)
from ..router import CaptchaRouter

logger = logging.getLogger("anticaptcha_provider")


SUPPORTED_API_DOMAINS = {"www.google.com", "www.recaptcha.net"}


@CaptchaRouter.provider("anticaptcha", "anti-captcha", api_key_env="ANTICAPTCHA_KEY")
class AntiCaptchaProvider(CaptchaSolver):
    """AntiCaptcha implementation of CaptchaSolver."""

    BASE_URL = "https://api.anti-captcha.com"
    RETRYABLE_ERROR_CODES = [1, 2, 10, 16]

    async def _post(self, endpoint: str, json_data: dict[str, Any]) -> dict[str, Any]:
        url = f"{self.BASE_URL}/{endpoint}"
        session = await self._get_session()

        for attempt in range(self.max_retries + 1):
            try:
                async with session.post(url, json=json_data) as response:
                    data = await response.json()
                    error_id = data.get("errorId", 0)

                    if error_id == 0:
                        return data

                    if error_id in self.RETRYABLE_ERROR_CODES and attempt < self.max_retries:
                        logger.warning(f"AntiCaptcha error {error_id}, retrying...")
                        await asyncio.sleep(self.retry_delay * (2 ** attempt))
                        continue

                    logger.error(f"AntiCaptcha API Error: {data.get('errorDescription')}")
                    return data

            except Exception as e:
                logger.error(f"Request failed: {e}")
                if attempt < self.max_retries:
                    await asyncio.sleep(self.retry_delay)
                else:
                    raise
        return {}

    @captcha_task_handler(
        "recaptcha",
        "recaptcha_v2",
        "recaptcha_v2_enterprise",
        "recaptcha_v3",
        "recaptcha_v3_enterprise",
        "nocaptchataskproxyless",
        "recaptchav2taskproxyless",
        "recaptchav2enterprisetaskproxyless",
        "recaptchav3taskproxyless",
        "recaptchav3enterprise",
        "recaptchav3enterprisetaskproxyless",
    )
    async def _create_recaptcha_task(self, task: CaptchaTaskSpec, **kwargs) -> str | None:
        kind = task.kind.strip().lower()
        params = dict(task.params or {})

        website_url = params.get("website_url")
        site_key = params.get("site_key")
        if not website_url or not site_key:
            logger.error("Task params must include website_url and site_key")
            return None

        # Normalize logical type from task kind aliases.
        v2_aliases = {"recaptcha", "recaptcha_v2", "nocaptchataskproxyless", "recaptchav2taskproxyless"}
        v2_enterprise_aliases = {"recaptcha_v2_enterprise", "recaptchav2enterprisetaskproxyless"}
        v3_aliases = {"recaptcha_v3", "recaptchav3taskproxyless"}
        v3_enterprise_aliases = {"recaptcha_v3_enterprise", "recaptchav3enterprise", "recaptchav3enterprisetaskproxyless"}

        if kind in v2_enterprise_aliases:
            version = "v2"
            is_enterprise = True
        elif kind in v3_enterprise_aliases:
            version = "v3"
            is_enterprise = True
        elif kind in v3_aliases:
            version = "v3"
            is_enterprise = bool(params.get("is_enterprise", False))
        elif kind in v2_aliases:
            version = str(params.get("version", "v2")).lower()
            is_enterprise = bool(params.get("is_enterprise", False))
        else:
            logger.error("Unsupported captcha kind for AntiCaptcha: %s", kind)
            return None

        api_domain = params.get("api_domain", params.get("apiDomain"))
        if api_domain is not None and api_domain not in SUPPORTED_API_DOMAINS:
            logger.error("Unsupported apiDomain for AntiCaptcha: %s", api_domain)
            return None

        if version == "v3":
            # Anti-Captcha documents enterprise v3 as RecaptchaV3TaskProxyless + isEnterprise=true.
            task_type = "RecaptchaV3TaskProxyless"
            min_score = float(params.get("min_score", params.get("minScore", 0.7)))
            if min_score not in (0.3, 0.7, 0.9):
                logger.error("Invalid minScore for AntiCaptcha v3: %s", min_score)
                return None
            task_payload = {
                "type": task_type,
                "websiteURL": website_url,
                "websiteKey": site_key,
                "minScore": min_score,
            }
            page_action = params.get("page_action", params.get("pageAction", params.get("action", "verify")))
            task_payload["pageAction"] = page_action
            if is_enterprise:
                task_payload["isEnterprise"] = True
            if api_domain is not None:
                task_payload["apiDomain"] = api_domain
        else:
            task_type = (
                "RecaptchaV2EnterpriseTaskProxyless"
                if is_enterprise
                else "RecaptchaV2TaskProxyless"
            )
            task_payload = {
                "type": task_type,
                "websiteURL": website_url,
                "websiteKey": site_key,
            }
            if "is_invisible" in params or "isInvisible" in params:
                task_payload["isInvisible"] = bool(params.get("is_invisible", params.get("isInvisible")))

            recaptcha_data_s = params.get("recaptcha_data_s_value", params.get("recaptchaDataSValue", params.get("data_s")))
            if recaptcha_data_s is not None:
                task_payload["recaptchaDataSValue"] = recaptcha_data_s

            if is_enterprise:
                enterprise_payload = params.get("enterprise_payload", params.get("enterprisePayload"))
                if enterprise_payload is not None:
                    task_payload["enterprisePayload"] = enterprise_payload
                if api_domain is not None:
                    task_payload["apiDomain"] = api_domain

        task_data = {
            "clientKey": self.api_key,
            "task": task_payload,
        }

        create_resp = await self._post("createTask", task_data)
        task_id = create_resp.get("taskId")

        if not task_id:
            logger.error(f"Failed to create task: {create_resp}")
            return None

        logger.info(f"Created AntiCaptcha task {task_id}")
        return str(task_id)

    async def get_task_result(self, task_id: str, **kwargs) -> CaptchaTaskResult:
        result_resp = await self._post("getTaskResult", {
            "clientKey": self.api_key,
            "taskId": int(task_id) if task_id.isdigit() else task_id,
        })
        status = result_resp.get("status")

        if status == "ready":
            solution = result_resp.get("solution", {}).get("gRecaptchaResponse")
            if solution:
                return CaptchaTaskResult(status="ready", solution=solution, raw=result_resp)
            return CaptchaTaskResult(
                status="failed",
                error="ready_without_token",
                raw=result_resp,
            )

        if status == "processing":
            return CaptchaTaskResult(status="processing", raw=result_resp)

        return CaptchaTaskResult(
            status="failed",
            error=result_resp.get("errorDescription") or f"unexpected_status:{status}",
            raw=result_resp,
        )

    async def get_balance(self) -> float:
        resp = await self._post("getBalance", {"clientKey": self.api_key})
        return float(resp.get("balance", 0.0))
