"""WarehousePriceProvider — implements the DataProvider port by reading OHLC bars from the warehouse
SSOT and returning a PriceSeries. Consumers (research/forward) read the SSOT with no change to the
backtest. Reads are bounded by start/end (and the warehouse is point-in-time by construction)."""
from __future__ import annotations

import datetime as dt

import numpy as np

from fxhnt.domain.models import Market, PriceSeries
from fxhnt.ports.warehouse import FeatureStore


def _iso_to_ts(iso: str | None) -> int | None:
    if iso is None:
        return None
    return int(dt.datetime.strptime(iso, "%Y-%m-%d").replace(tzinfo=dt.timezone.utc).timestamp())


def _ts_to_iso(ts: int) -> str:
    return dt.datetime.fromtimestamp(int(ts), dt.timezone.utc).strftime("%Y-%m-%d")


class WarehousePriceProvider:
    name = "warehouse"

    def __init__(self, store: FeatureStore) -> None:
        self._store = store

    def fetch(self, market: Market, start: str | None = None, end: str | None = None) -> PriceSeries:
        rows = self._store.read_features(market.symbol, start_ts=_iso_to_ts(start),
                                         end_ts=_iso_to_ts(end), features=["close", "high", "low"])
        if not rows:
            raise RuntimeError(f"warehouse has no bars for {market.symbol}")
        dates = tuple(_ts_to_iso(ts) for ts, _ in rows)
        close = np.array([f["close"] for _, f in rows], dtype=float)
        high = np.array([f.get("high", f["close"]) for _, f in rows], dtype=float)
        low = np.array([f.get("low", f["close"]) for _, f in rows], dtype=float)
        return PriceSeries(market=market, dates=dates, close=close, high=high, low=low)
