import requests
import datetime
import json
from typing import List, Dict, Any, Optional
from tonsdk.utils import Address
from ..dollers import Dollers


class Transactions:
    """
    Fetch and filter TON wallet transactions using TONAPI events endpoint.
    Supports: Sent TON, Received TON, NFT Transfer, Transfer Token, Gas Relay
    """

    TON_DECIMALS = 1_000_000_000

    def __init__(self, wallet: str):
        self.wallet = wallet
        self.base_url = "https://tonapi.io/v2"
        self._dollar_rate = Dollers()._TON_USDT()


    @staticmethod
    def raw_to_UQ(raw: str) -> str:
        try:
            addr = Address(raw)
            return addr.to_string(
                is_user_friendly=True,
                is_url_safe=True,
                is_bounceable=False,
                is_test_only=False
            )
        except Exception:
            return raw

    @staticmethod
    def _name_or_addr(info: Dict) -> str:
        """
        Return name (username / platform label) if TONAPI provided one,
        otherwise convert address to UQ format.
        """
        if not info:
            return ""
        name = info.get("name", "")
        addr = info.get("address", "")
        if name:
            return name
        if addr:
            try:
                a = Address(addr)
                return a.to_string(
                    is_user_friendly=True,
                    is_url_safe=True,
                    is_bounceable=False,
                    is_test_only=False
                )
            except Exception:
                return addr
        return ""


    @staticmethod
    def format_time(timestamp: int) -> str:
        return datetime.datetime.fromtimestamp(timestamp).strftime(
            "%d %b %I:%M:%S %p"
        )

    @staticmethod
    def clean_comment(comment: str) -> str:
        if not comment:
            return ""
        if "Ref#" in comment:
            comment = comment.split("Ref#")[0]
        return comment.strip()


    def _parse_event(self, event: Dict) -> Optional[Dict[str, Any]]:
        """
        Convert a TONAPI event dict into a unified transaction record.
        Returns None if the event type is not supported or event is in-progress.

        Action detection priority (most specific first):
          1. NftItemTransfer  → NFT Transfer
          2. JettonTransfer   → Transfer Token
          3. TonTransfer      → Sent TON / Received TON
          4. AuctionBid /
             SmartContractExec / etc. → Gas Relay
        """
        actions     = event.get("actions", [])
        timestamp   = event.get("timestamp", 0)
        event_id    = event.get("event_id", "")
        in_progress = event.get("in_progress", False)

        if in_progress:
            return None

        tx_time       = self.format_time(timestamp)
        types_present = {a.get("type") for a in actions}

        try:
            _w = Address(self.wallet)
            wallet_raw = f"0:{_w.hash_part.hex()}"
        except Exception:
            wallet_raw = self.wallet.lower()

        nft_actions = [a for a in actions if a.get("type") == "NftItemTransfer"]
        if nft_actions:
            a      = nft_actions[0]
            status = a.get("status", "ok")
            state  = "Success" if status == "ok" else "Failed"
            if state == "Failed":
                return None

            nft     = a.get("NftItemTransfer", {})
            sender  = nft.get("sender") or {}
            recip   = nft.get("recipient") or {}
            comment = self.clean_comment(nft.get("comment", ""))


            nft_obj  = nft.get("nft") or {}
            if isinstance(nft_obj, str):
                nft_obj = {}
            nft_name = (
                (nft_obj.get("metadata") or {}).get("name", "")
                or (nft_obj.get("collection") or {}).get("name", "")
            )

            fee_ton = sum(
                int(a2.get("TonTransfer", {}).get("amount", 0))
                for a2 in actions
                if a2.get("type") == "TonTransfer"
            ) / self.TON_DECIMALS

            rec: Dict[str, Any] = {
                "Hash":   event_id,
                "Time":   tx_time,
                "Action": "NFT Transfer",
                "From":   self._name_or_addr(sender),
                "To":     self._name_or_addr(recip),
                "State":  state,
            }
            if fee_ton > 0:
                fee_usd = fee_ton * self._dollar_rate
                rec["Price"] = f"-{fee_ton:.3f} TON ≈ {fee_usd:.5f} $ (fee)"
            if nft_name:
                rec["NFT"] = nft_name
            if comment:
                rec["Comment"] = comment
            return rec

        jet_actions = [a for a in actions if a.get("type") == "JettonTransfer"]
        if jet_actions:
            a      = jet_actions[0]
            status = a.get("status", "ok")
            state  = "Success" if status == "ok" else "Failed"
            if state == "Failed":
                return None

            jet     = a.get("JettonTransfer", {})
            sender  = jet.get("sender") or {}
            recip   = jet.get("recipient") or {}
            comment = self.clean_comment(jet.get("comment", ""))

            jetton_info   = jet.get("jetton") or {}
            if isinstance(jetton_info, str):
                jetton_info = {}
            jetton_symbol = jetton_info.get("symbol", "TOKEN")
            raw_amount    = int(jet.get("amount", 0))
            decimals      = int(jetton_info.get("decimals", 9))
            token_amount  = raw_amount / (10 ** decimals)

            gas_ton = sum(
                int(a2.get("TonTransfer", {}).get("amount", 0))
                for a2 in actions
                if a2.get("type") == "TonTransfer"
            ) / self.TON_DECIMALS
            gas_usd = gas_ton * self._dollar_rate

            price = f"{token_amount:.4f} {jetton_symbol}"
            if gas_ton > 0:
                price += f" | gas -{gas_ton:.3f} TON ≈ {gas_usd:.5f} $"

            rec = {
                "Hash":   event_id,
                "Time":   tx_time,
                "Action": "Transfer Token",
                "From":   self._name_or_addr(sender),
                "To":     self._name_or_addr(recip),
                "Price":  price,
                "State":  state,
            }
            if comment:
                rec["Comment"] = comment
            return rec

        ton_actions = [a for a in actions if a.get("type") == "TonTransfer"]
        if ton_actions:
            a      = ton_actions[0]
            status = a.get("status", "ok")
            state  = "Success" if status == "ok" else "Failed"
            if state == "Failed":
                return None

            transfer = a.get("TonTransfer", {})
            sender   = transfer.get("sender") or {}
            recip    = transfer.get("recipient") or {}
            amount   = int(transfer.get("amount", 0)) / self.TON_DECIMALS
            usd_val  = amount * self._dollar_rate
            comment  = self.clean_comment(transfer.get("comment", ""))

            recip_raw   = (recip.get("address") or "").lower()
            action_type = (
                "Received TON"
                if recip_raw == wallet_raw
                else "Sent TON"
            )
            sign = "+" if action_type == "Received TON" else "-"

            rec = {
                "Hash":   event_id,
                "Time":   tx_time,
                "Action": action_type,
                "From":   self._name_or_addr(sender),
                "To":     self._name_or_addr(recip),
                "Price":  f"{sign}{amount:.3f} TON ≈ {usd_val:.5f} $",
                "State":  state,
            }
            if comment:
                rec["Comment"] = comment
            return rec

        relay_types = {
            "AuctionBid", "SmartContractExec", "ContractDeploy",
            "Subscribe", "UnSubscribe", "DepositStake",
            "WithdrawStake", "WithdrawStakeRequest",
            "ElectionsDepositStake", "ElectionsRecoverStake",
            "JettonMint", "JettonBurn",
        }
        matched_relays = [
            a for a in actions if a.get("type") in relay_types
        ]
        if matched_relays:
            relay_a     = matched_relays[0]
            status      = relay_a.get("status", "ok")
            state       = "Success" if status == "ok" else "Failed"
            if state == "Failed":
                return None

            action_type = relay_a.get("type", "")
            inner       = relay_a.get(action_type) or {}
            sender_info = (
                inner.get("sender")
                or inner.get("account")
                or {}
            )
            recip_info  = (
                inner.get("recipient")
                or inner.get("account")
                or {}
            )

            ton_val = sum(
                int(a2.get("TonTransfer", {}).get("amount", 0))
                for a2 in actions
                if a2.get("type") == "TonTransfer"
            ) / self.TON_DECIMALS
            usd_val = ton_val * self._dollar_rate

            rec = {
                "Hash":   event_id,
                "Time":   tx_time,
                "Action": "Gas Relay",
                "From":   self._name_or_addr(sender_info),
                "To":     self._name_or_addr(recip_info),
                "State":  state,
            }
            if ton_val > 0:
                rec["Price"] = f"+{ton_val:.3f} TON ≈ {usd_val:.5f} $"
            return rec

        return None  # unrecognised event — counted as skipped

    @staticmethod
    def _normalise_action(action: str) -> str:
        _map = {
            "send":           "Sent TON",
            "sent":           "Sent TON",
            "send ton":       "Sent TON",
            "sent ton":       "Sent TON",
            "receive":        "Received TON",
            "received":       "Received TON",
            "receive ton":    "Received TON",
            "received ton":   "Received TON",
            "nft":            "NFT Transfer",
            "nft transfer":   "NFT Transfer",
            "transfer token": "Transfer Token",
            "jetton":         "Transfer Token",
            "token":          "Transfer Token",
            "gas relay":      "Gas Relay",
            "gas":            "Gas Relay",
            "relay":          "Gas Relay",
        }
        return _map.get(action.strip().lower(), action)


    def get(self, limit: int = 1) -> str:
        url = f"{self.base_url}/accounts/{self.wallet}/events?limit={limit}"

        try:
            response = requests.get(url)
            response.raise_for_status()
            data = response.json()

            results: List[Dict[str, Any]] = []
            skipped = 0
            counter = 1

            for event in data.get("events", []):
                rec = self._parse_event(event)
                if rec is None:
                    skipped += 1
                    continue
                rec["Limit"] = str(counter)
                results.append(rec)
                counter += 1

            output = {
                "transactions": results,
                "skipped": f"Skipped {skipped} unsupported transactions",
            }
            return json.dumps(output, indent=2, ensure_ascii=False)

        except requests.RequestException:
            return json.dumps(
                {"error": "Failed to fetch transactions"}, indent=2
            )

    def Action(self, action: str = "Sent TON", limit: int = 1) -> str:
        wanted = self._normalise_action(action)
        url    = f"{self.base_url}/accounts/{self.wallet}/events?limit={limit}"

        try:
            response = requests.get(url)
            response.raise_for_status()
            data = response.json()

            results: List[Dict[str, Any]] = []
            skipped_nft = 0
            counter     = 1

            for event in data.get("events", []):
                if len(results) >= limit:
                    break

                rec = self._parse_event(event)

                if rec is None:
                    skipped_nft += 1
                    continue

                if rec["Action"] != wanted:
                    if rec["Action"] == "NFT Transfer":
                        skipped_nft += 1
                    continue

                rec["Limit"] = str(counter)
                results.append(rec)
                counter += 1

            output = {
                "transactions": results,
                "skipped": f"Skipped {skipped_nft} NFT / unsupported Transactions",
            }
            return json.dumps(output, indent=2, ensure_ascii=False)

        except requests.RequestException:
            return json.dumps(
                {"error": "Failed to fetch transactions"}, indent=2
            )
