from __future__ import annotations

import time
from dataclasses import dataclass
from typing import Any, Dict, List, Optional


try:
    import aiohttp
except Exception:  # pragma: no cover
    aiohttp = None  # type: ignore


TGSTAT_BASE_URL = "https://api.tgstat.ru"


class TGStatError(RuntimeError):
    """Ошибка при работе с TGStat API."""


@dataclass(frozen=True)
class TGStatSearchParams:
    """Параметры поиска TGStat.

    Минимальный обязательный набор для /channels/search:
    - token
    - country
    - хотя бы один из: q или category
    """

    token: str
    country: str
    q: Optional[str] = None
    category: Optional[str] = None
    language: str = "russian"
    peer_type: str = "channel"  # channel/chat/all
    search_by_description: int = 0
    limit: int = 20


def _clamp_int(v: int, lo: int, hi: int) -> int:
    try:
        iv = int(v)
    except Exception:
        return lo
    if iv < lo:
        return lo
    if iv > hi:
        return hi
    return iv


def _normalize_username(username: Optional[str]) -> Optional[str]:
    if not username:
        return None
    u = str(username).strip()
    if not u:
        return None
    return u.lstrip("@").strip() or None


def _normalize_link(link: Optional[str], username: Optional[str]) -> Optional[str]:
    """Пытается привести link к https://t.me/...

    TGStat часто отдаёт link в виде "t.me/xxx".
    """
    if link:
        s = str(link).strip()
        if s.startswith("//"):
            return "https:" + s
        if s.startswith("http://") or s.startswith("https://"):
            return s
        if s.startswith("t.me/"):
            return "https://" + s
    # fallback по username
    un = _normalize_username(username)
    if un:
        return f"https://t.me/{un}"
    return None


async def tgstat_channels_search(params: TGStatSearchParams, *, timeout_sec: float = 25.0) -> List[Dict[str, Any]]:
    """Поиск каналов/чатов через TGStat API (/channels/search).

    Возвращает список items (объекты Channel), как их отдаёт API.
    """
    if aiohttp is None:
        raise TGStatError("aiohttp не установлен (нужен для TGStat).")

    token = (params.token or "").strip()
    if not token:
        raise TGStatError("TGStat token пустой")

    country = (params.country or "").strip()
    if not country:
        raise TGStatError("TGStat country не задан")

    q = (params.q or "").strip() or None
    category = (params.category or "").strip() or None
    if not q and not category:
        raise TGStatError("Нужно передать хотя бы один параметр: q или category")

    limit = _clamp_int(params.limit, 1, 100)
    sbd = 1 if str(params.search_by_description).strip() in ("1", "true", "True") else 0
    peer_type = (params.peer_type or "channel").strip().lower()
    if peer_type not in ("channel", "chat", "all"):
        peer_type = "channel"

    language = (params.language or "russian").strip().lower() or "russian"

    url = f"{TGSTAT_BASE_URL}/channels/search"
    query_params: Dict[str, Any] = {
        "token": token,
        "country": country,
        "peer_type": peer_type,
        "language": language,
        "search_by_description": int(sbd),
        "limit": int(limit),
    }
    if q:
        query_params["q"] = q
    if category:
        query_params["category"] = category

    timeout = aiohttp.ClientTimeout(total=float(timeout_sec))
    async with aiohttp.ClientSession(timeout=timeout) as session:
        async with session.get(url, params=query_params) as resp:
            # TGStat отдаёт application/json
            try:
                data = await resp.json(content_type=None)
            except Exception as e:
                text = await resp.text()
                raise TGStatError(f"TGStat: не смог прочитать JSON (HTTP {resp.status}): {text[:200]}") from e

    if not isinstance(data, dict):
        raise TGStatError("TGStat: неожиданный формат ответа")

    status = str(data.get("status") or "").lower()
    if status != "ok":
        # часто: {"status":"error", "error":{...}}
        err = data.get("error") or data.get("message") or data
        raise TGStatError(f"TGStat: error response: {err}")

    resp_obj = data.get("response") or {}
    items = resp_obj.get("items") or []
    if not isinstance(items, list):
        return []
    return [x for x in items if isinstance(x, dict)]


async def tgstat_usage_stat(token: str, *, timeout_sec: float = 25.0) -> List[Dict[str, Any]]:
    """Получить статистику использования TGStat API (/usage/stat).

    Удобно для диагностики: показывает активированные услуги (API Stat / API Search / API Callback),
    квоты и дату окончания.

    Возвращает список объектов из поля response.
    """

    if aiohttp is None:
        raise TGStatError("aiohttp не установлен (нужен для TGStat).")

    tok = (token or "").strip()
    if not tok:
        raise TGStatError("TGStat token пустой")

    url = f"{TGSTAT_BASE_URL}/usage/stat"
    query_params: Dict[str, Any] = {"token": tok}

    timeout = aiohttp.ClientTimeout(total=float(timeout_sec))
    async with aiohttp.ClientSession(timeout=timeout) as session:
        async with session.get(url, params=query_params) as resp:
            try:
                data = await resp.json(content_type=None)
            except Exception as e:
                text = await resp.text()
                raise TGStatError(f"TGStat: не смог прочитать JSON (HTTP {resp.status}): {text[:200]}") from e

    if not isinstance(data, dict):
        raise TGStatError("TGStat: неожиданный формат ответа")

    status = str(data.get("status") or "").lower()
    if status != "ok":
        err = data.get("error") or data.get("message") or data
        raise TGStatError(f"TGStat: error response: {err}")

    resp_obj = data.get("response")
    if not isinstance(resp_obj, list):
        return []
    return [x for x in resp_obj if isinstance(x, dict)]


def tgstat_item_to_row(item: Dict[str, Any], *, found_query: str) -> Dict[str, Any]:
    """Маппинг Channel (TGStat) -> row для sqlite (results).

    Важно: чтобы не конфликтовать с telegram-id, используем отрицательный id.
    """
    tgstat_id = 0
    try:
        tgstat_id = int(item.get("id") or 0)
    except Exception:
        tgstat_id = 0

    peer_type = str(item.get("peer_type") or "channel").strip().lower()
    if peer_type == "chat":
        entity_type = "supergroup"
    else:
        entity_type = "channel"

    username_raw = item.get("username")
    username = _normalize_username(str(username_raw) if username_raw is not None else None)

    link = _normalize_link(item.get("link"), username)

    participants_count = item.get("participants_count")
    try:
        if participants_count is not None:
            participants_count = int(participants_count)
    except Exception:
        participants_count = None

    return {
        "id": int(-abs(tgstat_id)) if tgstat_id else int(-abs(hash((username or link or found_query))) % 2_000_000_000),
        "access_hash": None,
        "entity_type": entity_type,
        "title": item.get("title"),
        "username": username,
        "link": link,
        "verified": 0,
        "scam": 0,
        "fake": 0,
        "participants_count": participants_count,
        "about": item.get("about"),
        "found_query": found_query,
        "found_at": int(time.time()),
    }
