"""Fetch US equity 5-minute RTH bars from IBKR (live signal path)."""

from __future__ import annotations

import asyncio
from typing import Any

import numpy as np
import pandas as pd

from RenTech.strategy_stack.alpaca_minute_loader import ohlcv_frame, resample_session_bars


async def fetch_intraday_5m_bars(
    ib: Any,
    symbol: str,
    *,
    duration_str: str = "5 D",
    exchange: str = "SMART",
    currency: str = "USD",
    pacing_sec: float = 0.25,
) -> pd.DataFrame:
    """Return session-aligned 5m OHLCV (Eastern wall clock, naive index)."""
    from ib_insync import Stock

    contract = Stock(str(symbol).upper(), str(exchange), str(currency))
    qualified = await ib.qualifyContractsAsync(contract)
    if not qualified:
        raise RuntimeError(f"IB could not qualify {symbol!r}")

    bars = await ib.reqHistoricalDataAsync(
        contract,
        endDateTime="",
        durationStr=str(duration_str),
        barSizeSetting="5 mins",
        whatToShow="TRADES",
        useRTH=True,
        formatDate=1,
    )
    if pacing_sec > 0:
        await asyncio.sleep(pacing_sec)
    if not bars:
        raise RuntimeError(f"No 5m bars for {symbol!r}")

    rows = []
    for b in bars:
        ts = pd.Timestamp(b.date)
        if ts.tzinfo is not None:
            ts = ts.tz_convert("America/New_York").tz_localize(None)
        rows.append(
            {
                "datetime": ts,
                "open": float(b.open),
                "high": float(b.high),
                "low": float(b.low),
                "close": float(b.close),
                "volume": float(b.volume),
            }
        )
    minute = pd.DataFrame(rows).set_index("datetime").sort_index()
    minute = minute.between_time("09:30", "16:00")
    if minute.empty:
        raise RuntimeError(f"{symbol}: no RTH 5m bars after filter")
    intra = resample_session_bars(minute, bar_minutes=5)
    return ohlcv_frame(intra)


async def fetch_intraday_panel_ibkr(
    ib: Any,
    symbols: list[str],
    *,
    duration_str: str = "5 D",
    pacing_sec: float = 0.25,
) -> dict[str, pd.DataFrame]:
    out: dict[str, pd.DataFrame] = {}
    errors: list[str] = []
    for sym in symbols:
        try:
            out[sym] = await fetch_intraday_5m_bars(
                ib, sym, duration_str=duration_str, pacing_sec=pacing_sec
            )
            print(f"  [IB 5m] {sym}: {len(out[sym])} bars", flush=True)
        except Exception as exc:
            errors.append(f"{sym}: {exc}")
            print(f"  [IB 5m] {sym}: FAILED ({exc})", flush=True)
    if not out:
        raise RuntimeError("No IB intraday panels loaded: " + "; ".join(errors[:5]))
    return out
