from decimal import Decimal
from typing import Any

import httpx

from app.core.config import settings
from app.core.exceptions import ExternalServiceError
from app.core.logging import get_logger

logger = get_logger("app.fragment")


class FragmentService:
    """The only component that talks to the Fragment/fulfilment provider.

    When no provider is configured the call reports `manual`, which keeps the
    order in `processing` for the admin to complete from the panel.
    """

    def __init__(self, base_url: str = "", api_key: str = "", timeout: float = 30.0) -> None:
        self.base_url = (base_url or settings.fragment_api_url).rstrip("/")
        self.api_key = api_key or settings.fragment_api_key
        self.timeout = timeout

    @property
    def configured(self) -> bool:
        return bool(self.base_url and self.api_key)

    async def _post(self, path: str, payload: dict[str, Any]) -> dict[str, Any]:
        if not self.configured:
            logger.warning("fragment not configured, order needs manual processing: %s", path)
            return {"manual": True, "reason": "fragment_not_configured"}

        url = f"{self.base_url}/{path.lstrip('/')}"
        headers = {"Authorization": f"Bearer {self.api_key}"}
        try:
            async with httpx.AsyncClient(timeout=self.timeout) as client:
                response = await client.post(url, json=payload, headers=headers)
        except httpx.HTTPError as exc:
            logger.error("fragment request failed: %s", exc)
            raise ExternalServiceError("fragment_unavailable") from exc

        if response.status_code >= 400:
            logger.error("fragment error %s: %s", response.status_code, response.text[:500])
            raise ExternalServiceError("fragment_error")

        data = response.json()
        if isinstance(data, dict) and data.get("success") is False:
            raise ExternalServiceError(str(data.get("message") or "fragment_error"))
        return data if isinstance(data, dict) else {"result": data}

    async def buy_premium(self, username: str, months: int) -> dict[str, Any]:
        return await self._post("premium", {"username": username, "months": months})

    async def buy_stars(self, username: str, count: int) -> dict[str, Any]:
        return await self._post("stars", {"username": username, "quantity": count})

    async def send_gift(
        self,
        username: str,
        stars: int,
        hide: bool = True,
        comment: str | None = None,
        comment_type: str = "normal",
    ) -> dict[str, Any]:
        return await self._post(
            "gift",
            {
                "username": username,
                "stars": stars,
                "hide": hide,
                "comment": comment,
                "comment_type": comment_type,
            },
        )

    async def boost_channel(self, channel: str, count: int, config: dict[str, Any]) -> dict[str, Any]:
        return await self._post(
            "boost", {"channel": channel, "count": count, "plan": config.get("plan", "normal")}
        )

    async def create_giveaway(
        self, channel: str, config: dict[str, Any], quantity: int = 1
    ) -> dict[str, Any]:
        return await self._post(
            "giveaway",
            {"channel": channel, "stars": config.get("stars"), "winners": config.get("winners"),
             "quantity": quantity},
        )

    async def send_reactions(
        self, channel: str, config: dict[str, Any], quantity: int = 1
    ) -> dict[str, Any]:
        count = int(config.get("count", 0)) * max(quantity, 1)
        return await self._post("reaction", {"channel": channel, "count": count})

    async def balance(self) -> Decimal:
        data = await self._post("balance", {})
        try:
            return Decimal(str(data.get("balance", "0")))
        except (ValueError, ArithmeticError):
            return Decimal("0")
