from __future__ import annotations

from dataclasses import dataclass, field
from typing import Any, Dict, Iterator, List, Mapping, Optional


@dataclass(slots=True)
class Bot:
    bot_id: str
    name: str = ""
    description: str = ""
    avatar: Optional[str] = None
    avatar_url: Optional[str] = None
    categories: List[str] = field(default_factory=list)
    features: List[str] = field(default_factory=list)
    upvotes: int = 0
    reviews: int = 0
    rating: float = 0.0
    score: int = 0
    servers: int = 0
    users: int = 0
    shards: int = 0
    slug: Optional[str] = None
    review_score_events: int = 0
    submitted_at: Optional[str] = None
    approved_at: Optional[str] = None
    raw: Dict[str, Any] = field(default_factory=dict, repr=False)

    @classmethod
    def from_payload(cls, payload: Mapping[str, Any]) -> "Bot":
        if payload is None:
            return cls(bot_id="")

        data: Mapping[str, Any] = payload

        if isinstance(payload.get("bot"), Mapping):
            data = payload["bot"]
        elif isinstance(payload.get("data"), Mapping) and isinstance(payload["data"].get("bot"), Mapping):
            data = payload["data"]["bot"]

        return cls(
            bot_id=str(data.get("bot_id") or ""),
            name=str(data.get("name") or ""),
            description=str(data.get("description") or ""),
            avatar=_to_optional_str(data.get("avatar")),
            avatar_url=_to_optional_str(data.get("avatar_url")),
            categories=_to_string_list(data.get("categories")),
            features=_to_string_list(data.get("features")),
            upvotes=_to_int(data.get("upvotes"), default=0),
            reviews=_to_int(data.get("reviews"), default=0),
            rating=_to_float(data.get("rating"), default=0.0),
            score=_to_int(data.get("score"), default=0),
            servers=_to_int(data.get("servers"), default=0),
            users=_to_int(data.get("users"), default=0),
            shards=_to_int(data.get("shards"), default=0),
            slug=_to_optional_str(data.get("slug")),
            review_score_events=_to_int(data.get("review_score_events"), default=0),
            submitted_at=_to_optional_str(data.get("submitted_at")),
            approved_at=_to_optional_str(data.get("approved_at")),
            raw=dict(payload),
        )


@dataclass(slots=True)
class BotList:
    bots: List[Bot]
    total: Optional[int] = None
    page: Optional[int] = None
    per_page: Optional[int] = None
    raw: Dict[str, Any] = field(default_factory=dict, repr=False)

    def __len__(self) -> int:
        return len(self.bots)

    def __iter__(self) -> Iterator[Bot]:
        return iter(self.bots)

    def __getitem__(self, index: int) -> Bot:
        return self.bots[index]


def _to_string_list(value: Any) -> List[str]:
    if value is None:
        return []
    if isinstance(value, list):
        return [str(item) for item in value if item is not None]
    if isinstance(value, tuple):
        return [str(item) for item in value if item is not None]
    return [str(value)]


def _to_int(value: Any, *, default: int) -> int:
    if value is None:
        return default
    try:
        return int(value)
    except (TypeError, ValueError):
        return default


def _to_float(value: Any, *, default: float) -> float:
    if value is None:
        return default
    try:
        return float(value)
    except (TypeError, ValueError):
        return default


def _to_optional_str(value: Any) -> Optional[str]:
    if value is None:
        return None
    text = str(value).strip()
    return text or None
