"""Tests for SentimentClient."""

from __future__ import annotations

from decimal import Decimal

import pytest
from pytest_httpx import HTTPXMock

from cerbero_bite.clients._base import HttpToolClient
from cerbero_bite.clients._exceptions import McpDataAnomalyError
from cerbero_bite.clients.sentiment import (
    EXCHANGE_PERIODS_PER_YEAR,
    SentimentClient,
)


def _client() -> SentimentClient:
    http = HttpToolClient(
        service="sentiment",
        base_url="http://mcp-sentiment:9014",
        token="t",
        retry_max=1,
    )
    return SentimentClient(http)


@pytest.mark.asyncio
async def test_median_annualises_per_exchange_period(httpx_mock: HTTPXMock) -> None:
    httpx_mock.add_response(
        url="http://mcp-sentiment:9014/tools/get_cross_exchange_funding",
        json={
            "snapshot": {
                "ETH": {
                    "binance": 0.0001,
                    "bybit": 0.0002,
                    "okx": 0.00015,
                    "hyperliquid": 0.00002,
                }
            }
        },
    )
    median = await _client().funding_cross_median_annualized("eth")
    # Annualised values (Decimal):
    #   binance:    0.0001    * 1095 = 0.1095
    #   bybit:      0.0002    * 1095 = 0.2190
    #   okx:        0.00015   * 1095 = 0.16425
    #   hyperliquid:0.00002   * 8760 = 0.17520
    # sorted: 0.1095, 0.16425, 0.17520, 0.2190 → median = (0.16425+0.17520)/2 = 0.169725
    assert median == Decimal("0.169725")


@pytest.mark.asyncio
async def test_median_skips_missing_venues(httpx_mock: HTTPXMock) -> None:
    httpx_mock.add_response(
        json={
            "snapshot": {
                "ETH": {
                    "binance": 0.0001,
                    "bybit": None,
                    "okx": None,
                    "hyperliquid": None,
                }
            }
        }
    )
    median = await _client().funding_cross_median_annualized("ETH")
    assert median == Decimal("0.0001") * Decimal("1095")


@pytest.mark.asyncio
async def test_anomaly_when_snapshot_missing(httpx_mock: HTTPXMock) -> None:
    httpx_mock.add_response(json={"snapshot": {}})
    with pytest.raises(McpDataAnomalyError, match="snapshot missing"):
        await _client().funding_cross_median_annualized("ETH")


@pytest.mark.asyncio
async def test_anomaly_when_no_venue_responded(httpx_mock: HTTPXMock) -> None:
    httpx_mock.add_response(
        json={
            "snapshot": {
                "ETH": {
                    "binance": None,
                    "bybit": None,
                    "okx": None,
                    "hyperliquid": None,
                }
            }
        }
    )
    with pytest.raises(McpDataAnomalyError, match="no funding venues"):
        await _client().funding_cross_median_annualized("ETH")


def test_periods_table_covers_documented_venues() -> None:
    assert set(EXCHANGE_PERIODS_PER_YEAR) == {
        "binance", "bybit", "okx", "hyperliquid"
    }


@pytest.mark.asyncio
async def test_liquidation_heatmap_parses_high_risk(httpx_mock: HTTPXMock) -> None:
    httpx_mock.add_response(
        url="http://mcp-sentiment:9014/tools/get_liquidation_heatmap",
        json={
            "asset": "ETH",
            "avg_funding_rate": 0.00012,
            "oi_delta_pct_4h": 6.5,
            "oi_delta_pct_24h": 8.2,
            "long_squeeze_risk": "high",
            "short_squeeze_risk": "low",
        },
    )
    out = await _client().liquidation_heatmap("eth")
    assert out.asset == "ETH"
    assert out.avg_funding_rate == Decimal("0.00012")
    assert out.long_squeeze_risk == "high"
    assert out.has_high_squeeze_risk is True


@pytest.mark.asyncio
async def test_liquidation_heatmap_unknown_risk_levels_default_to_low(
    httpx_mock: HTTPXMock,
) -> None:
    httpx_mock.add_response(
        json={
            "asset": "ETH",
            "long_squeeze_risk": "extreme",
            "short_squeeze_risk": None,
        }
    )
    out = await _client().liquidation_heatmap("ETH")
    assert out.long_squeeze_risk == "low"
    assert out.short_squeeze_risk == "low"
    assert out.has_high_squeeze_risk is False


def test_sentiment_client_rejects_wrong_service() -> None:
    bad = HttpToolClient(
        service="macro",
        base_url="http://x:1",
        token="t",
        retry_max=1,
    )
    with pytest.raises(ValueError, match="requires service 'sentiment'"):
        SentimentClient(bad)
