"""CombinedBook reads through an injected CombinedBookSource; default keeps the raw-file behavior."""
from __future__ import annotations

import numpy as np

from fxhnt.application.combined_book import CombinedBook
from fxhnt.domain.models import AssetClass, Market, PriceSeries


_N_SYMS = 12  # xs_momentum_book requires min_names=10


class _FakeSource:
    def crypto_panel(self):
        days = list(range(19000, 19000 + 400))
        # Different drift per symbol so xs_momentum_book produces non-zero cross-sectional signal
        rng = np.random.default_rng(42)
        rates = np.linspace(0.003, -0.003, _N_SYMS)
        noise = rng.normal(0, 0.01, (400, _N_SYMS))
        daily = rates + noise
        close = np.cumprod(1.0 + daily, axis=0) * 100.0
        ret = np.full_like(close, np.nan); ret[1:] = close[1:] / close[:-1] - 1.0
        syms = [f"S{i:02d}USDT" for i in range(_N_SYMS)]
        return days, syms, close, ret

    def futures_series(self, root):
        import datetime as dt
        start = dt.date(2022, 1, 8)
        dates = tuple((start + dt.timedelta(days=i)).strftime("%Y-%m-%d") for i in range(400))
        rng = np.random.default_rng(7)
        daily = rng.normal(0.0005, 0.01, 400)
        close = np.cumprod(1.0 + daily) * 100.0
        return PriceSeries(market=Market(symbol=root, asset_class=AssetClass.FUTURE), dates=dates, close=close)


def test_combined_book_uses_injected_source() -> None:
    book = CombinedBook("unused", ["ES"], source=_FakeSource())
    res, dates, combo = book.build()
    assert res.overlap_days > 0 and len(combo) == len(dates)


def test_default_source_is_rawfile(tmp_path) -> None:
    book = CombinedBook(str(tmp_path), ["ES"])     # no source → defaults to RawFileSource(data_dir)
    assert book is not None
