"""The nightly `deribit_funding_bars` asset ingests Deribit perp funding into bybit_features and is a member
of `combined_book_forward_job`. Its only forward consumer (`xvenue_carry_nav`) was retired 2026-07-07; the
ingestion is KEPT so the deribit panel stays warm for a possible cross-venue-carry revival.
NO network — the DeribitFunding fetcher is faked and the store is a file-backed sqlite bybit_features."""
from __future__ import annotations


def test_deribit_funding_bars_asset_ingests(tmp_path, monkeypatch) -> None:
    """The asset writes `deribit_funding` rows for the default instruments, date-resuming per symbol — proven
    by mocking the DeribitFunding fetcher + pointing the operational DSN at a file sqlite store."""
    from dagster import build_op_context

    import fxhnt.adapters.orchestration.assets as assets
    from fxhnt.adapters.warehouse.timescale_feature_store import TimescaleFeatureStore
    from fxhnt.application.deribit_funding_ingest import DEFAULT_DERIBIT_INSTRUMENTS
    from fxhnt.config import Settings

    dsn = f"sqlite:///{tmp_path / 'op.db'}"
    # Pre-seed an OLD deribit_funding day for BTCUSDT so the date-resume floor is exercised.
    seed = TimescaleFeatureStore(dsn, table="bybit_features")
    seed.write_features("BTCUSDT", [(100 * 86_400, {"deribit_funding": 0.0002})])
    seed.close()

    monkeypatch.setattr("fxhnt.config.get_settings", lambda: Settings(operational_dsn=dsn))

    captured: dict = {}

    class _FakeDeribit:
        def __init__(self) -> None:
            pass

        def funding_history(self, instrument, *, start_ms=None):  # noqa: ANN001, ANN202
            captured[instrument] = start_ms
            # Serve an extra new day (101) for BTC; one day for the rest.
            return {100: 0.0002, 101: 0.0004} if instrument == "BTC-PERPETUAL" else {101: 0.0009}

    monkeypatch.setattr("fxhnt.adapters.data.deribit_funding.DeribitFunding", _FakeDeribit)

    summary = assets.deribit_funding_bars(build_op_context())

    # Every default instrument was fetched; BTC resumed from its last persisted day, the rest from the floor.
    assert set(captured) == set(DEFAULT_DERIBIT_INSTRUMENTS)
    assert captured["BTC-PERPETUAL"] == 100 * 86_400_000     # date-resume floor (last persisted day)
    assert captured["ETH-PERPETUAL"] is None                 # fresh -> default floor
    assert summary["symbols"] == len(DEFAULT_DERIBIT_INSTRUMENTS)

    # The deribit_funding panel advanced (BTC now carries day 101 too).
    chk = TimescaleFeatureStore(dsn, table="bybit_features")
    panel = chk.read_panel(["BTCUSDT", "ETHUSDT"], "deribit_funding")
    chk.close()
    assert panel["BTCUSDT"] == {100: 0.0002, 101: 0.0004}
    assert panel["ETHUSDT"] == {101: 0.0009}


def test_deribit_funding_bars_transient_failure_is_nonfatal(tmp_path, monkeypatch) -> None:
    """A transient Deribit network error logs + returns an empty summary rather than killing the nightly run."""
    from dagster import build_op_context

    import fxhnt.adapters.orchestration.assets as assets
    from fxhnt.config import Settings

    monkeypatch.setattr("fxhnt.config.get_settings",
                        lambda: Settings(operational_dsn=f"sqlite:///{tmp_path / 'op.db'}"))

    class _BoomDeribit:
        def funding_history(self, instrument, *, start_ms=None):  # noqa: ANN001, ANN202
            raise ConnectionError("deribit unreachable")

    monkeypatch.setattr("fxhnt.adapters.data.deribit_funding.DeribitFunding", _BoomDeribit)
    out = assets.deribit_funding_bars(build_op_context())
    assert out == {"symbols": 0, "day_rows": 0, "oldest_day": None}


def test_deribit_funding_bars_in_job() -> None:
    """Wiring: `deribit_funding_bars` is a member of combined_book_forward_job. Its former forward consumer
    `xvenue_carry_nav` was retired 2026-07-07, so the ingest now runs standalone (keeps the panel warm)."""
    from dagster import AssetKey

    from fxhnt.adapters.orchestration.definitions import defs

    repo = defs.get_repository_def()
    job = repo.get_job("combined_book_forward_job")
    job_keys = {k.to_user_string() for k in job.asset_layer.executable_asset_keys}
    assert "deribit_funding_bars" in job_keys, f"deribit_funding_bars not in job: {job_keys}"
    # the retired consumer is gone
    assert AssetKey("xvenue_carry_nav") not in repo.assets_defs_by_key, "xvenue_carry_nav must be retired"
