from __future__ import annotations
import abc
from abc import ABC
import dataclasses
from typing import Any

import cachebox
from cachebox import Cache

from ..chain import Chain, EthereumChain, SolanaChain, TronChain


# Proxy for a chain-specific client
@dataclasses.dataclass(frozen=True, kw_only=True, slots=True)
class ChainClient[ChainType: Chain](ABC):
    chain: ChainType

    @staticmethod
    @cachebox.cached(Cache(0))
    async def _create(chain: ChainType) -> ChainClient[ChainType]:
        match chain:
            case EthereumChain():
                return await EthereumClient.create(chain)
            case SolanaChain():
                return await SolanaClient.create(chain)
            case TronChain():
                return await TronClient.create(chain)
            case _:
                raise NotImplementedError(f"Unsupported chain: {chain}")

    @classmethod
    @abc.abstractmethod
    async def create(cls, chain: ChainType) -> ChainClient[ChainType]:
        client = await cls._create(chain)

        if not await client.is_connected():
            await client.reconnect()
            assert await client.is_connected(), f"Failed to connect {chain} client"

        return client

    @abc.abstractmethod
    async def close(self) -> None:
        pass

    @abc.abstractmethod
    async def is_connected(self) -> bool:
        pass

    @abc.abstractmethod
    async def reconnect(self) -> None:
        pass

    def __eq__(self, other: Any) -> bool:
        return isinstance(other, ChainClient) and self.chain == other.chain

    def __hash__(self) -> int:
        return hash(self.chain)


# Avoid a circular import
from .ethereum import EthereumClient  # noqa: E402
from .solana import SolanaClient  # noqa: E402
from .tron import TronClient  # noqa: E402
