from dataclasses import dataclass
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.tron")

SUN = Decimal("1000000")


@dataclass
class TronTransfer:
    tx_hash: str
    amount: Decimal
    from_address: str
    to_address: str
    timestamp: int
    confirmed: bool


class TronService:
    """Reads incoming TRX transfers of the shop wallet from TronGrid."""

    def __init__(self, base_url: str = "", api_key: str = "", timeout: float = 20.0) -> None:
        self.base_url = (base_url or settings.tron_api_url).rstrip("/")
        self.api_key = api_key or settings.tron_api_key
        self.timeout = timeout

    def _headers(self) -> dict[str, str]:
        return {"TRON-PRO-API-KEY": self.api_key} if self.api_key else {}

    async def incoming_transfers(
        self, address: str, min_timestamp: int = 0, limit: int = 100
    ) -> list[TronTransfer]:
        if not address:
            raise ExternalServiceError("tron_wallet_not_configured")

        url = f"{self.base_url}/v1/accounts/{address}/transactions"
        params: dict[str, Any] = {
            "only_to": "true",
            "only_confirmed": "true",
            "limit": min(limit, 200),
            "order_by": "block_timestamp,desc",
            "visible": "true",
        }
        if min_timestamp:
            params["min_timestamp"] = min_timestamp

        try:
            async with httpx.AsyncClient(timeout=self.timeout) as client:
                response = await client.get(url, params=params, headers=self._headers())
        except httpx.HTTPError as exc:
            logger.error("tron request failed: %s", exc)
            raise ExternalServiceError("tron_unavailable") from exc

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

        payload = response.json()
        return [
            transfer
            for item in payload.get("data", [])
            if (transfer := self._parse(item, address)) is not None
        ]

    def _parse(self, item: dict[str, Any], address: str) -> TronTransfer | None:
        try:
            contract = item["raw_data"]["contract"][0]
            if contract.get("type") != "TransferContract":
                return None
            value = contract["parameter"]["value"]
            to_address = value.get("to_address")
            amount = Decimal(str(value.get("amount", 0))) / SUN
            success = True
            results = item.get("ret") or []
            if results:
                success = results[0].get("contractRet") == "SUCCESS"
            return TronTransfer(
                tx_hash=item.get("txID", ""),
                amount=amount,
                from_address=str(value.get("owner_address", "")),
                to_address=str(to_address or address),
                timestamp=int(item.get("block_timestamp", 0)),
                confirmed=success,
            )
        except (KeyError, IndexError, TypeError, ValueError, ArithmeticError):
            logger.warning("unparsable tron transaction: %s", str(item)[:200])
            return None
