from __future__ import annotations

from datetime import datetime
from decimal import Decimal

from fuggers_py.core import Currency, Date
from fuggers_py.data import (
    CurrencyPair,
    CurveId,
    CurveInput,
    CurveInputSource,
    CurveInputs,
    CurveInstrumentType,
    CurvePoint,
    EtfId,
    EtfQuote,
    FxRate,
    FxRateSource,
    IndexFixing,
    InflationFixing,
    InMemoryCurveSource,
    InMemoryEtfQuoteSource,
    InMemoryFixingSource,
    InMemoryFxRateSource,
    InMemoryInflationFixingSource,
    InMemoryQuoteSource,
    InMemoryVolatilitySource,
    InstrumentId,
    MarketDataProvider,
    MarketDataSnapshot,
    PricingDataProvider,
    QuoteSide,
    QuoteSource,
    RawQuote,
    VolPoint,
    VolSurfaceId,
    VolSurfaceType,
    VolatilitySource,
    VolatilitySurface,
    YearMonth,
)


def test_in_memory_market_data_provider_serves_extended_sources() -> None:
    as_of = Date.from_ymd(2026, 3, 13)
    quote = RawQuote(
        instrument_id=InstrumentId("BOND-1"),
        value=Decimal("101.25"),
        side=QuoteSide.MID,
        as_of=as_of,
        timestamp=datetime(2026, 3, 13, 9, 0),
        currency=Currency.USD,
        bid=Decimal("101.10"),
        ask=Decimal("101.40"),
        source="dealer-run",
    )
    curve_inputs = CurveInputs.from_points(
        CurveId("usd.discount"),
        as_of,
        [
            CurvePoint(Decimal("5.0"), Decimal("0.0390")),
            CurvePoint(Decimal("1.0"), Decimal("0.0425")),
        ],
        instruments=[
            CurveInput(
                instrument_type=CurveInstrumentType.BOND,
                instrument_id=InstrumentId("BOND-1"),
                tenor=Decimal("5.0"),
                rate=Decimal("0.0390"),
            )
        ],
    )
    fixing = IndexFixing("SOFR", as_of, Decimal("0.0410"))
    surface = VolatilitySurface(
        surface_id=VolSurfaceId("usd.swaption"),
        surface_type=VolSurfaceType.SWAPTION,
        as_of=as_of,
        points=(VolPoint(expiry=YearMonth(2026, 6), tenor=YearMonth(2028, 6), strike=Decimal("0.03"), volatility=Decimal("0.22")),),
    )
    fx_rate = FxRate(
        currency_pair=CurrencyPair.parse("usd/eur"),
        rate=Decimal("0.9200"),
        bid=Decimal("0.9190"),
        ask=Decimal("0.9210"),
        as_of=as_of,
    )
    inflation = InflationFixing("CPI", YearMonth.parse("2026-02"), Decimal("307.12"))
    etf_quote = EtfQuote(etf_id=EtfId("agg-usd"), market_price=Decimal("101.20"), nav=Decimal("101.10"), as_of=as_of)

    provider = MarketDataProvider(
        quote_source=InMemoryQuoteSource([quote]),
        curve_input_source=InMemoryCurveSource([curve_inputs]),
        index_fixing_source=InMemoryFixingSource([fixing]),
        volatility_source=InMemoryVolatilitySource([surface]),
        fx_rate_source=InMemoryFxRateSource([fx_rate]),
        inflation_fixing_source=InMemoryInflationFixingSource([inflation]),
        etf_quote_source=InMemoryEtfQuoteSource([etf_quote]),
    )

    assert isinstance(provider, QuoteSource)
    assert isinstance(provider, CurveInputSource)
    assert isinstance(provider, PricingDataProvider)
    assert isinstance(provider, VolatilitySource)
    assert isinstance(provider, FxRateSource)
    assert provider.get_quote("BOND-1", QuoteSide.BID).value == Decimal("101.10")
    assert provider.get_curve_inputs("usd.discount") == curve_inputs
    assert provider.get_fixing("SOFR", as_of) == fixing
    assert provider.get_volatility_surface("usd.swaption") == surface
    assert provider.get_fx_rate("usd/eur", QuoteSide.ASK).rate == Decimal("0.9210")
    assert provider.get_inflation_fixing("CPI", "2026-02") == inflation
    assert provider.get_etf_quote("agg-usd") == etf_quote


def test_market_data_snapshot_builds_composite_provider() -> None:
    as_of = Date.from_ymd(2026, 3, 13)
    snapshot = MarketDataSnapshot(
        as_of=as_of,
        quotes=(RawQuote(InstrumentId("BOND-2"), Decimal("99.50"), QuoteSide.MID, as_of=as_of),),
        fx_rates=(FxRate(CurrencyPair.parse("eur/usd"), Decimal("1.09"), as_of=as_of),),
    )

    provider = snapshot.provider()

    assert provider.get_quote("BOND-2") is not None
    assert provider.get_fx_rate("EUR/USD") is not None

