"""Databento data adapter — survivorship-free, point-in-time daily bars across futures/equities/etc.
Implements the DataProvider port.

The cost guard is the point: ALWAYS estimate cost via the metadata API and refuse any fetch above the
configured cap BEFORE downloading (a runaway download once cost $3.5k). databento is an optional extra.
The API key is read from DATABENTO_API_KEY by the client — never passed or logged here.
"""
from __future__ import annotations

import datetime as dt
from typing import TYPE_CHECKING

import numpy as np

from fxhnt.domain.models import AssetClass, Market, PriceSeries

if TYPE_CHECKING:
    import pandas as pd


def _df_to_series(market: Market, df: "pd.DataFrame") -> PriceSeries:
    """Pure transform: an ohlcv-1d dataframe -> PriceSeries with NaN-guarded high/low."""
    dates = tuple(str(idx.date()) for idx in df.index)
    close = df["close"].to_numpy(dtype=float)
    high = df["high"].to_numpy(dtype=float) if "high" in df.columns else close
    low = df["low"].to_numpy(dtype=float) if "low" in df.columns else close
    high = np.where(np.isfinite(high), high, close)        # NaN bar -> flat (close)
    low = np.where(np.isfinite(low), low, close)
    return PriceSeries(market=market, dates=dates, close=close, high=high, low=low)


class DatabentoCostExceeded(RuntimeError):
    """Raised when an estimated fetch cost exceeds the configured cap — the spend circuit-breaker."""


class DatabentoDataProvider:
    name = "databento"

    def __init__(self, *, max_cost_usd: float = 0.50, equity_dataset: str = "DBEQ.BASIC",
                 future_dataset: str = "GLBX.MDP3", default_start: str = "2018-05-01") -> None:
        self._max_cost = max_cost_usd
        self._equity_dataset = equity_dataset
        self._future_dataset = future_dataset
        self._default_start = default_start

    def _resolve(self, market: Market) -> tuple[str, str, str]:
        ac = market.asset_class
        if ac == AssetClass.FUTURE:
            return self._future_dataset, f"{market.symbol}.c.0", "continuous"  # front-month continuous
        if ac in (AssetClass.EQUITY, AssetClass.ETF):
            return self._equity_dataset, market.symbol, "raw_symbol"
        raise NotImplementedError(f"databento mapping for asset class {ac} not implemented yet")

    def fetch(self, market: Market, start: str | None = None, end: str | None = None) -> PriceSeries:
        import databento as db

        dataset, symbol, stype = self._resolve(market)
        start = start or self._default_start
        end = end or dt.datetime.now(dt.timezone.utc).strftime("%Y-%m-%d")
        client = db.Historical()

        cost = client.metadata.get_cost(
            dataset=dataset, symbols=[symbol], schema="ohlcv-1d", start=start, end=end, stype_in=stype,
        )
        if cost > self._max_cost:
            raise DatabentoCostExceeded(
                f"estimated ${cost:.4f} > cap ${self._max_cost:.2f} for {market} ({dataset}/{symbol}) — refusing"
            )

        data = client.timeseries.get_range(
            dataset=dataset, symbols=[symbol], schema="ohlcv-1d", start=start, end=end, stype_in=stype,
        )
        df = data.to_df()
        if df.empty:
            raise RuntimeError(f"databento returned no data for {market} ({dataset}/{symbol})")
        return _df_to_series(market, df)
