from __future__ import annotations

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


class TonCenterError(RuntimeError):
    pass


@dataclass
class IncomingTransfer:
    tx_hash: str
    lt: str
    value_nano: int
    from_address: str
    comment: str


def get_transactions(
    base_url: str,
    address: str,
    api_key: str = "",
    limit: int = 20,
) -> List[Dict[str, Any]]:
    params = {
        "address": address,
        "limit": str(limit),
    }
    if api_key:
        params["api_key"] = api_key

    url = base_url.rstrip("/") + "/getTransactions"
    try:
        resp = requests.get(url, params=params, timeout=20)
    except Exception as e:
        raise TonCenterError(f"TonCenter request failed: {e}") from e

    try:
        data = resp.json()
    except Exception:
        raise TonCenterError(f"TonCenter returned non-JSON: {resp.status_code} {resp.text[:200]}")

    if not data.get("ok"):
        raise TonCenterError(f"TonCenter error: {data}")

    return data.get("result") or []


def _extract_comment(in_msg: Dict[str, Any]) -> str:
    # TonCenter puts human-readable comment in `message` for text comments.
    msg = in_msg.get("message")
    if isinstance(msg, str):
        return msg

    # some responses keep it under msg_data.text
    msg_data = in_msg.get("msg_data")
    if isinstance(msg_data, dict):
        text = msg_data.get("text")
        if isinstance(text, str):
            return text

    return ""


def find_incoming_by_comment(
    base_url: str,
    receiver_address: str,
    comment: str,
    min_value_nano: int,
    api_key: str = "",
    limit: int = 30,
) -> Optional[IncomingTransfer]:
    txs = get_transactions(base_url=base_url, address=receiver_address, api_key=api_key, limit=limit)

    for tx in txs:
        in_msg = tx.get("in_msg") or {}
        if not isinstance(in_msg, dict):
            continue

        # must be inbound to our address
        dst = in_msg.get("destination")
        if dst and dst != receiver_address:
            continue

        c = _extract_comment(in_msg)
        if comment not in c:
            continue

        # value is in nanotons as string
        try:
            value_nano = int(in_msg.get("value") or 0)
        except Exception:
            value_nano = 0

        if value_nano < min_value_nano:
            continue

        tx_hash = tx.get("transaction_id", {}).get("hash") or ""
        lt = tx.get("transaction_id", {}).get("lt") or ""
        from_address = in_msg.get("source") or ""

        return IncomingTransfer(
            tx_hash=str(tx_hash),
            lt=str(lt),
            value_nano=value_nano,
            from_address=str(from_address),
            comment=str(c),
        )

    return None
