import asyncio
import re
from typing import Any

import httpx
from config import cfg

_clients: dict[int, httpx.AsyncClient] = {}
_success_keys = ("success", "likes_sent", "LikesGivenByAPI", "count", "sent", "total", "like", "likes")
_jwt_meta_keys = {
    "server",
    "purpose",
    "file_index",
    "file_total",
    "file_id",
    "file_name",
    "file_requests",
    "total_requests",
    "success_requests",
    "failed_requests",
    "reset_day",
    "reset_time",
}

class UpstreamAPIError(Exception):
    def __init__(self, message: str, jwt_meta: dict | None = None):
        super().__init__(message)
        self.jwt_meta = jwt_meta or {}

def calc_cut_100(success: int) -> int:
    return 1 if success >= 70 else 0

def calc_cut_200(success: int) -> int:
    if success >= 120:
        return 2
    if success >= 90:
        return 1
    return 0

def calc_cut_free(success: int) -> int:
    return 0

def _client_for_loop() -> httpx.AsyncClient:
    loop_id = id(asyncio.get_running_loop())
    client = _clients.get(loop_id)
    if client is None or client.is_closed:
        timeout = httpx.Timeout(cfg.LIKE_API_TIMEOUT, connect=5.0)
        limits = httpx.Limits(max_connections=100, max_keepalive_connections=20)
        client = httpx.AsyncClient(
            timeout=timeout,
            limits=limits,
            headers={"User-Agent": "AMS-FF-Like/3.0"},
            follow_redirects=True,
            trust_env=False,
        )
        _clients[loop_id] = client
    return client

def _to_int(value: Any) -> int:
    if value is None:
        return 0
    if isinstance(value, bool):
        return int(value)
    if isinstance(value, (int, float)):
        return max(int(value), 0)
    if isinstance(value, str):
        match = re.search(r"\d+", value)
        return int(match.group(0)) if match else 0
    return 0

def extract_success(data: Any) -> int:
    if isinstance(data, dict):
        for key in _success_keys:
            value = data.get(key)
            if value not in (None, ""):
                return _to_int(value)
    return 0

def check_upstream_error(data: Any, jwt_meta: dict | None = None):
    if not isinstance(data, dict):
        return
    message = str(data.get("message") or data.get("error") or "").strip()
    lowered = message.lower()
    if "invalid key" in lowered or "invalid api key" in lowered:
        raise UpstreamAPIError(message or "Invalid upstream API key", jwt_meta)
    status = str(data.get("status") or "").strip().lower()
    if status in {"error", "failed", "fail"}:
        raise UpstreamAPIError(message or "Upstream like API returned error", jwt_meta)

def extract_jwt_meta(data: Any) -> dict:
    if not isinstance(data, dict):
        return {}
    meta = data.get("jwt_meta")
    if not isinstance(meta, dict):
        return {}
    safe = {}
    for key in _jwt_meta_keys:
        value = meta.get(key)
        if value not in (None, ""):
            safe[key] = str(value)
    return safe

async def call_like_api(url: str, uid: str, amount: int | None = None, server_name: str | None = None) -> dict:
    final_url = url.replace("{UID}", str(uid)).replace("{uid}", str(uid))
    server = str(server_name or cfg.SERVER_NAME).strip() or cfg.SERVER_NAME
    final_url = final_url.replace("{SERVER_NAME}", server).replace("{server_name}", server)
    if amount is not None:
        final_url = final_url.replace("{AMOUNT}", str(amount)).replace("{amount}", str(amount))
    client = _client_for_loop()
    resp = await client.get(final_url)
    resp.raise_for_status()
    try:
        data = resp.json()
    except ValueError:
        data = {"raw_text": resp.text}
    jwt_meta = extract_jwt_meta(data)
    check_upstream_error(data, jwt_meta)
    return {"success": extract_success(data), "jwt_meta": jwt_meta, "raw": data}

async def close_http_clients():
    clients = list(_clients.values())
    _clients.clear()
    for client in clients:
        await client.aclose()
