#CODE SOLVER MODUL
#TEST Server : http://144.76.92.77:5000/ realy server : http://37.27.240.144:5000/
import json
import logging
import re
from functools import lru_cache
from pathlib import Path
from typing import TYPE_CHECKING, Any, Dict, Optional

import requests

from log_utils import log_debug_event

_CONFIG_FILE = Path(__file__).with_name("config.json")
_DEFAULT_SERVER = "http://37.27.240.144:5000"
_TEST_SERVER = "http://144.76.92.77:5000"
_FALLBACK_USER_AGENT = (
    "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
    "AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36"
)
_FATAL_ERROR_CODES = {
    "ERROR_API_KEY",
    "ERROR_MISSING_BLOB",
    "ERROR_MISSING_USER_AGENT",
    "ERROR_MISSING_SITE_KEY",
    "ERROR_MISSING_SITE_URL",
    "ERROR_SIZE_LIMIT",
    "POOL_DISABLED",
    "UNSUPPORTED_USER_AGENT",
    "INVALID_BLOB_ENCODING",
    "UNSUPPORTED_SITE_KEY",
    "SITEKEY_USER_AGENT_MISMATCH",
    "ERROR_PROXY_CONFIG",
    "ERROR_NO_BALANCE",
    "ERROR_RATE_LIMIT",
}

REQUEST_TIMEOUT = 30  # saniye

if TYPE_CHECKING:
    from requests import Response
else:
    Response = Any


class CaptchaServiceHTTPError(RuntimeError):
    def __init__(self, status_code: int, body: str) -> None:
        self.status_code = status_code
        self.body = body
        preview = body if len(body) <= 300 else body[:297] + "..."
        super().__init__(f"CaptchaService get_task HTTP {status_code}: {preview}")


@lru_cache(maxsize=1)
def _load_user_agent() -> str:
    try:
        with _CONFIG_FILE.open("r", encoding="utf-8") as file:
            config = json.load(file)
    except (OSError, json.JSONDecodeError):
        return _FALLBACK_USER_AGENT
    ua = str(config.get("user_agent") or "").strip()
    return ua or _FALLBACK_USER_AGENT


@lru_cache(maxsize=1)
def _load_api_key() -> str:
    with _CONFIG_FILE.open("r", encoding="utf-8") as file:
        config = json.load(file)
    api_key = str(config.get("code_solver_api_key") or "").strip()
    if not api_key:
        raise ValueError("config.json içinde 'code_solver_api_key' anahtarı bulunamadı.")
    return api_key


@lru_cache(maxsize=1)
def _load_castle_api_key() -> str:
    with _CONFIG_FILE.open("r", encoding="utf-8") as file:
        config = json.load(file)
    block = config.get("code_solver_castle") or {}
    api_key = ""
    if isinstance(block, dict):
        api_key = str(block.get("api_key") or "").strip()
    if not api_key:
        api_key = str(config.get("code_solver_api_key") or "").strip()
    if not api_key:
        raise ValueError(
            "config.json içinde 'code_solver_castle.api_key' veya 'code_solver_api_key' bulunamadı."
        )
    return api_key


@lru_cache(maxsize=1)
def _use_test_server() -> bool:
    try:
        with _CONFIG_FILE.open("r", encoding="utf-8") as file:
            config = json.load(file)
    except Exception:
        return False
    flag = config.get("code_solver_test_server")
    if isinstance(flag, bool):
        return flag
    if isinstance(flag, (int, float)):
        return bool(flag)
    if isinstance(flag, str):
        lowered = flag.strip().lower()
        return lowered in {"1", "true", "yes", "on"}
    return False


def _get_server_base() -> str:
    base = _TEST_SERVER if _use_test_server() else _DEFAULT_SERVER
    return base.rstrip("/")


# --- SIKI FORMAT PARSE: host:port:username:password ---
# host  : ':' ve whitespace içermez
# port  : yalnızca rakam
# user  : boş olamaz, ':' içermez
# pass  : boş olamaz, ':' içerebilir (grup4'te kalan her şey)
_PROXY_RE = re.compile(
    r"""^\s*
        (?P<host>[^:\s@]+)
        :
        (?P<port>\d+)
        :
        (?P<user>[^:\s]+)
        :
        (?P<pass>.+\S)     # en az bir non-space ile bitsin (boş parola olmasın)
        \s*$
    """,
    re.VERBOSE,
)


def _build_proxy_payload(proxy_spec: Optional[str]) -> Optional[Dict[str, str]]:
    """
    SADECE şu format desteklenir: host:port:username:password

    - port numerik olmak zorunda
    - password ':' içerebilir (regex 4. grupta hepsini alır)
    - '@' veya '://' görürse ya da regex eşleşmezse ValueError atar (sessizce None dönmez)
    """
    if proxy_spec is None:
        return None

    spec = proxy_spec.strip()
    if not spec:
        return None

    if "@" in spec or "://" in spec:
        raise ValueError("Proxy formatı yalnızca 'host:port:username:password' olmalıdır.")

    m = _PROXY_RE.match(spec)
    if not m:
        raise ValueError("Geçersiz proxy formatı. Beklenen: host:port:username:password")

    host = m.group("host")
    port = m.group("port")
    username = m.group("user")
    password = m.group("pass")

    # Ek guard (regex zaten filtreliyor ama açık dursun)
    if not host:
        raise ValueError("Proxy host boş olamaz.")
    if not port.isdigit():
        raise ValueError("Proxy port numerik olmalıdır.")
    if not username:
        raise ValueError("Proxy username boş olamaz.")
    if not password:
        raise ValueError("Proxy password boş olamaz.")

    log_debug_event("proxy_parsed", {"host": host, "port": port, "user": username})
    return {
        "Address": f"{host}:{port}",
        "Username": username,
        "Password": password,
    }


def create_task(
    api_key: str, proxy_spec: Optional[str] = None, user_agent: Optional[str] = None
) -> Any:
    url = f"{_get_server_base()}/api/token/createTask"

    resolved_user_agent = (user_agent or "").strip() or _load_user_agent()
    payload: Dict[str, Any] = {
        "ApiKey": api_key,
        "Blob": "TWITTER_REGISTER",
        "UserAgent": resolved_user_agent,
        "SiteKey": "2CB16598-CB82-4CF7-B332-5990DB66F3AB",
        "SiteUrl": "https://x.com/i/flow/signup",
    }

    if proxy_spec is not None:
        proxy_payload = _build_proxy_payload(proxy_spec)
        if proxy_payload:
            payload["Proxy"] = proxy_payload

    response = requests.post(
        url,
        json=payload,
        headers={"accept": "*/*"},
        timeout=REQUEST_TIMEOUT,
    )

    try:
        result = response.json()
    except ValueError:
        log_debug_event("CaptchaService create_task JSON Hatası", response.text)
        raise

    result = _monitor_errors("create_task", response, result)

    if isinstance(result, dict) and result.get("success") is True:
        log_debug_event(
            "CaptchaService create_task yanıtı",
            {"status_code": response.status_code, "body": result},
        )
    return result


def get_task(session_id: str, api_key: str) -> Any:
    url = f"{_get_server_base()}/api/Token/getTask/{session_id}?apiKey={api_key}"
    response = requests.get(url, timeout=REQUEST_TIMEOUT)

    if response.status_code == 400:
        body_text = response.text
        log_debug_event(
            "CaptchaService get_task 400 yanıtı",
            {"status_code": response.status_code, "body": body_text},
        )

        try:
            result: Any = response.json()
        except ValueError:
            result = {
                "success": False,
                "taskType": "HTTP_400",
                "message": body_text,
            }
        else:
            if not isinstance(result, dict):
                result = {
                    "success": False,
                    "taskType": "HTTP_400",
                    "message": str(result),
                }

        annotated = _monitor_errors("get_task", response, result)
        if isinstance(annotated, dict):
            annotated.setdefault(
                "message",
                "Captcha servisi get_task isteği 400 hatası: geçersiz görev verisi.",
            )
            annotated.setdefault("status_code", response.status_code)

            task_type = str(annotated.get("taskType") or "").strip()
            if task_type:
                annotated.setdefault("fatal_error", task_type.upper())
            else:
                annotated.setdefault("fatal_error", "HTTP_400")

        return annotated

    try:
        result = response.json()
    except ValueError:
        log_debug_event("CaptchaService get_task JSON Hatası", response.text)
        raise

    result = _monitor_errors("get_task", response, result)

    if isinstance(result, dict) and result.get("success") is True:
        log_debug_event(
            "CaptchaService get_task yanıtı",
            {"status_code": response.status_code, "body": result},
        )
    return result


def get_balance(api_key: Optional[str] = None) -> Dict[str, Any]:
    if api_key is None:
        api_key = _load_api_key()

    url = f"{_get_server_base()}/api/Token/getBalance?apiKey={api_key}"

    response = requests.get(url, timeout=REQUEST_TIMEOUT)
    response.raise_for_status()

    result: Dict[str, Any] = response.json()
    log_debug_event(
        "CaptchaService get_balance yanıtı",
        {"status_code": response.status_code, "body": result},
    )
    return result


def create_castle_task(
    api_key: Optional[str] = None,
    proxy_spec: Optional[str] = None,
    user_agent: Optional[str] = None,
) -> Any:
    if api_key is None:
        api_key = _load_castle_api_key()

    url = f"{_get_server_base()}/api/token/createTask"

    resolved_user_agent = (user_agent or "").strip() or _load_user_agent()
    payload: Dict[str, Any] = {
        "ApiKey": api_key,
        "Blob": "CASTLE",
        "UserAgent": resolved_user_agent,
        "SiteKey": "CASTLE",
        "SiteUrl": "https://x.com/i/flow/signup",
    }

    if proxy_spec is not None:
        proxy_payload = _build_proxy_payload(proxy_spec)
        if proxy_payload:
            payload["Proxy"] = proxy_payload

    response = requests.post(
        url,
        json=payload,
        headers={"accept": "*/*"},
        timeout=REQUEST_TIMEOUT,
    )

    try:
        result = response.json()
    except ValueError:
        log_debug_event("CaptchaService castle create_task JSON Hatası", response.text)
        raise

    return _monitor_errors("create_task", response, result)


def get_castle_task(session_id: str, api_key: Optional[str] = None) -> Any:
    if api_key is None:
        api_key = _load_castle_api_key()

    url = f"{_get_server_base()}/api/Token/getTask/{session_id}?apiKey={api_key}"
    response = requests.get(url, timeout=REQUEST_TIMEOUT)

    try:
        result = response.json()
    except ValueError:
        log_debug_event("CaptchaService castle get_task JSON Hatası", response.text)
        raise

    return _monitor_errors("get_task", response, result)


def _monitor_errors(endpoint: str, response: Response, result: Any) -> Any:
    if not isinstance(result, dict):
        return result

    task_type = str(result.get("taskType") or "").strip().upper()
    error_code_raw = result.get("error")
    error_code = str(error_code_raw or "").strip().upper()

    if not error_code:
        return result

    fatal = False
    if error_code in _FATAL_ERROR_CODES:
        if not task_type or task_type == "FAILED":
            fatal = True

    if not fatal:
        return result

    annotated = dict(result)
    annotated["fatal_error"] = error_code
    annotated.setdefault("message", str(result.get("message") or ""))
    annotated.setdefault("status_code", response.status_code)

    log_debug_event(
        f"CaptchaService {endpoint} fatal error",
        {"status_code": response.status_code, "body": annotated},
    )
    return annotated
