"""CLI `fxhnt bybit-ingest-worst-basis` — universe resolution (all/csv/@file/liquid) + 1m worst_basis ingest
into bybit_features. NO network: BybitKlines (intraday_worst_basis) + BybitFunding (universe) are fakes; the
operational DSN points at a temp sqlite db. The `liquid` mode resolves via liquid_universe over the store."""
from __future__ import annotations

from typer.testing import CliRunner

from fxhnt.cli import app

_DAY = 86_400


class _FakeUniverse:
    def active_universe(self):
        return ["BTCUSDT", "ETHUSDT"]

    def ever_listed_universe(self, *a, **k):
        return ["BTCUSDT", "ETHUSDT", "LUNAUSDT"]


class _FakeKlines:
    def intraday_worst_basis(self, symbol, *, start_ms=None, interval="1"):
        return {19000: -0.05, 19001: -0.10}


def _patch(monkeypatch, tmp_path):
    dsn = f"sqlite:///{tmp_path / 'op.db'}"
    monkeypatch.setenv("FXHNT_OPERATIONAL_DSN", dsn)
    from fxhnt import cli as climod
    climod.get_settings.cache_clear()
    monkeypatch.setattr("fxhnt.adapters.data.bybit_funding.BybitFunding", lambda *a, **k: _FakeUniverse())
    monkeypatch.setattr("fxhnt.adapters.data.bybit_klines.BybitKlines", lambda *a, **k: _FakeKlines())
    return dsn


def test_cli_active_only(monkeypatch, tmp_path):
    dsn = _patch(monkeypatch, tmp_path)
    res = CliRunner().invoke(app, ["bybit-ingest-worst-basis", "--symbols", "all"])
    assert res.exit_code == 0, res.output
    assert "2 symbols (active)" in res.output
    assert "done: 2 symbols" in res.output

    from fxhnt.adapters.warehouse.timescale_feature_store import TimescaleFeatureStore
    store = TimescaleFeatureStore(dsn, table="bybit_features")
    assert store.read_panel(["BTCUSDT"], "worst_basis") == {"BTCUSDT": {19000: -0.05, 19001: -0.10}}
    store.close()


def test_cli_liquid_mode_resolves_via_liquid_universe(monkeypatch, tmp_path):
    dsn = _patch(monkeypatch, tmp_path)
    # Pre-seed turnover so liquid_universe selects only the deep coin.
    from fxhnt.adapters.warehouse.timescale_feature_store import TimescaleFeatureStore
    store = TimescaleFeatureStore(dsn, table="bybit_features")
    for d in range(95):
        store.write_features("BTCUSDT", [(d * _DAY, {"close": 100.0, "turnover": 50_000_000.0})])
        store.write_features("THINUSDT", [(d * _DAY, {"close": 1.0, "turnover": 100_000.0})])
    store.close()

    res = CliRunner().invoke(
        app, ["bybit-ingest-worst-basis", "--symbols", "liquid", "--min-dollar-vol", "10000000"])
    assert res.exit_code == 0, res.output
    assert "1 symbols (liquid)" in res.output   # only BTCUSDT clears the floor

    store = TimescaleFeatureStore(dsn, table="bybit_features")
    assert store.read_panel(["BTCUSDT"], "worst_basis") == {"BTCUSDT": {19000: -0.05, 19001: -0.10}}
    assert store.read_panel(["THINUSDT"], "worst_basis") == {"THINUSDT": {}}   # not in liquid set
    store.close()


def test_cli_explicit_symbols(monkeypatch, tmp_path):
    _patch(monkeypatch, tmp_path)
    res = CliRunner().invoke(app, ["bybit-ingest-worst-basis", "--symbols", "BTCUSDT,ETHUSDT"])
    assert res.exit_code == 0, res.output
    assert "2 symbols (explicit)" in res.output


def test_cli_bad_from_month(monkeypatch, tmp_path):
    _patch(monkeypatch, tmp_path)
    res = CliRunner().invoke(app, ["bybit-ingest-worst-basis", "--symbols", "BTCUSDT", "--from", "2022"])
    assert res.exit_code == 1
    assert "must be YYYY-MM" in res.output
