from __future__ import annotations

import urllib.error
from decimal import Decimal

from jarvis_finance.market.providers import CoinGeckoClient


class Resp:
    def __init__(self, payload: bytes): self.payload = payload
    def __enter__(self): return self
    def __exit__(self, *args): return False
    def read(self): return self.payload


def test_coingecko_client_retries_429_and_returns_quote_without_float() -> None:
    calls = {'n': 0}
    sleeps: list[float] = []
    def opener(url, timeout=20):
        calls['n'] += 1
        if calls['n'] < 3:
            raise urllib.error.HTTPError(url, 429, 'rate limited', None, None)
        return Resp(b'{"bitcoin":{"chf":0.00000001,"last_updated_at":1700000000}}')
    client = CoinGeckoClient(max_retries=3, initial_backoff_seconds=0.5, max_backoff_seconds=1.0, opener=opener, sleeper=sleeps.append)
    quote = client.get_crypto_price('bitcoin', 'CHF')
    assert calls['n'] == 3
    assert sleeps == [0.5, 1.0]
    assert quote.price == Decimal('1E-8')
    assert quote.quality_status == 'fresh'


def test_coingecko_client_429_after_retries_returns_stale_not_crash() -> None:
    def opener(url, timeout=20):
        raise urllib.error.HTTPError(url, 429, 'rate limited', None, None)
    client = CoinGeckoClient(max_retries=1, initial_backoff_seconds=0, opener=opener, sleeper=lambda s: None)
    quote = client.get_crypto_price('bitcoin', 'CHF')
    assert quote.price is None
    assert quote.quality_status == 'stale'
    assert '429' in (quote.error_message or '')
