from decimal import Decimal

from src.hyperliquid.account_state import HyperliquidAccountStateClient, parse_account_state
from src.hyperliquid.market_data import HyperliquidMarketData, build_hyperliquid_info_url, summarize_l2_book


class FakeTransport:
    def __init__(self):
        self.posts = []

    def post(self, url, payload, timeout=10):
        self.posts.append((url, payload, timeout))
        if payload["type"] == "meta":
            return {"universe": [{"name": "BTC", "szDecimals": 5}, {"name": "ETH", "szDecimals": 4}]}
        if payload["type"] == "metaAndAssetCtxs":
            return [{"universe": [{"name": "BTC", "szDecimals": 5}]}, [{"dayNtlVlm": "1"}]]
        if payload["type"] == "allMids":
            return {"BTC": "100000"}
        if payload["type"] == "candleSnapshot":
            return [{"t": 1, "c": "100"}]
        if payload["type"] == "l2Book":
            return {"levels": [[{"px": "99", "sz": "1"}], [{"px": "101", "sz": "2"}]]}
        raise AssertionError(payload)


def test_readonly_market_data_uses_info_endpoint_without_order_calls():
    transport = FakeTransport()
    client = HyperliquidMarketData(env="testnet", transport=transport)

    assert build_hyperliquid_info_url("testnet").endswith("/info")
    assert client.get_sz_decimals("BTC") == 5
    assert client.get_all_mids()["BTC"] == Decimal("100000")
    assert len(client.get_candles("BTC", "1m", 1, 2)) == 1
    book = client.get_l2_book("BTC")
    summary = summarize_l2_book(book)

    assert summary.best_bid == Decimal("99")
    assert summary.best_ask == Decimal("101")
    assert summary.spread_pct > 0
    assert all(call[1]["type"] != "order" for call in transport.posts)


def test_account_state_client_parses_readonly_info_client():
    class FakeInfo:
        def user_state(self, address):
            return {"assetPositions": [{"position": {"coin": "BTC", "szi": "0.01", "unrealizedPnl": "2.5"}}], "marginSummary": {"accountValue": "502.5"}}

        def spot_user_state(self, address):
            return {"balances": [{"coin": "USDC", "total": "500", "hold": "50"}]}

        def open_orders(self, address):
            return [{"coin": "BTC", "oid": 1}]

    client = HyperliquidAccountStateClient(FakeInfo())

    state = client.get_user_state("0xabc")
    spot = client.get_spot_user_state("0xabc")
    parsed = parse_account_state("0xabc", state, spot)

    assert client.get_open_positions("0xabc")["BTC"]["szi"] == "0.01"
    assert client.get_open_orders("0xabc") == [{"coin": "BTC", "oid": 1}]
    assert client.get_account_equity("0xabc") == Decimal("502.5")
    assert client.get_free_usdc("0xabc") == Decimal("450")
    assert client.get_margin_usage("0xabc") == Decimal("10.0")
    assert parsed.open_positions["BTC"]["unrealizedPnl"] == "2.5"
