"""Alpaca real-time snapshot service via Stock Oracle API.

Provides synchronous helpers for real-time price snapshots, suitable for use
in threaded engine code (which already runs in asyncio.to_thread).
"""
from __future__ import annotations

import logging
import os
from dataclasses import dataclass
from typing import Any

log = logging.getLogger(__name__)


@dataclass
class AlpacaSnapshot:
    """Real-time Alpaca snapshot for a single ticker."""

    ticker: str
    price: float | None = None         # last trade price
    bid: float | None = None
    ask: float | None = None
    bid_size: int | None = None
    ask_size: int | None = None
    open: float | None = None
    high: float | None = None
    low: float | None = None
    volume: int | None = None
    vwap: float | None = None
    prev_close: float | None = None
    change: float | None = None
    change_pct: float | None = None
    timestamp: str | None = None

    @property
    def mid(self) -> float | None:
        """Midpoint between bid and ask, or last trade if unavailable."""
        if self.bid is not None and self.ask is not None:
            return (self.bid + self.ask) / 2
        return self.price


def _base_url() -> str:
    return os.environ.get("ORACLE_URL", "http://localhost:18001").rstrip("/")


def _parse_snapshot(data: dict[str, Any]) -> AlpacaSnapshot:
    return AlpacaSnapshot(
        ticker=data["ticker"],
        price=data.get("price"),
        bid=data.get("bid"),
        ask=data.get("ask"),
        bid_size=data.get("bid_size"),
        ask_size=data.get("ask_size"),
        open=data.get("open"),
        high=data.get("high"),
        low=data.get("low"),
        volume=data.get("volume"),
        vwap=data.get("vwap"),
        prev_close=data.get("prev_close"),
        change=data.get("change"),
        change_pct=data.get("change_pct"),
        timestamp=data.get("timestamp"),
    )


def get_multi_daily_bars(
    tickers: list[str],
    start_date: str,
    end_date: str,
    base_url: str | None = None,
) -> dict[str, list[dict]]:
    """Fetch daily OHLCV bars for multiple tickers via Oracle API.

    Oracle handles symbol normalisation (e.g. BF-B → BF.B) and Alpaca batching
    internally.  Returns {ticker: [{date, open, high, low, close, volume}, ...]}.
    Missing/errored tickers are omitted.
    """
    import httpx

    if not tickers:
        return {}

    result: dict[str, list[dict]] = {}
    url = (base_url or _base_url()) + "/api/v1/price/data"

    # Alpaca rejects requests with ~100+ tickers in URL params (502 Bad Gateway).
    # Keep chunks at 75 to stay well under the limit.
    chunk_size = 75
    for i in range(0, len(tickers), chunk_size):
        chunk = tickers[i : i + chunk_size]
        try:
            resp = httpx.get(
                url,
                params={"tickers": ",".join(chunk), "start_date": start_date, "end_date": end_date},
                timeout=15.0,
            )
            resp.raise_for_status()
            for ticker, bars in resp.json().get("bars", {}).items():
                result[ticker] = bars
        except Exception as exc:
            log.warning("Oracle multi_daily_bars chunk %d failed: %s", i // chunk_size, exc)

    return result


def get_multi_intraday_bars(
    tickers: list[str],
    start_date: str,
    end_date: str,
    interval: str = "5min",
    base_url: str | None = None,
) -> dict[str, list[dict]]:
    """Fetch intraday bars for multiple tickers via Oracle API.

    Returns {ticker: [{timestamp (ISO8601), open, high, low, close, volume}, ...]}.
    """
    import httpx

    if not tickers:
        return {}

    result: dict[str, list[dict]] = {}
    url = (base_url or _base_url()) + "/api/v1/alpaca/intraday"

    chunk_size = 75
    for i in range(0, len(tickers), chunk_size):
        chunk = tickers[i : i + chunk_size]
        try:
            resp = httpx.get(
                url,
                params={
                    "tickers": ",".join(chunk),
                    "interval": interval,
                    "start_date": start_date,
                    "end_date": end_date,
                },
                timeout=15.0,
            )
            resp.raise_for_status()
            for ticker, bars in resp.json().get("bars", {}).items():
                result[ticker] = bars
        except Exception as exc:
            log.warning("Oracle multi_intraday_bars chunk %d failed: %s", i // chunk_size, exc)

    return result


def get_multi_intraday_bars_today(
    tickers: list[str],
    interval: str = "5min",
    base_url: str | None = None,
) -> dict[str, list[dict]]:
    """Fetch today's intraday bars (IEX real-time) via Oracle /alpaca/intraday/today.

    Uses IEX feed with force_refresh=True — suitable for live paper trading.
    Returns {ticker: [{timestamp (ISO8601), open, high, low, close, volume}, ...]}.
    """
    import httpx

    if not tickers:
        return {}

    result: dict[str, list[dict]] = {}
    url = (base_url or _base_url()) + "/api/v1/alpaca/intraday/today"

    chunk_size = 75
    for i in range(0, len(tickers), chunk_size):
        chunk = tickers[i : i + chunk_size]
        try:
            resp = httpx.get(
                url,
                params={"tickers": ",".join(chunk), "interval": interval},
                timeout=15.0,
            )
            resp.raise_for_status()
            for ticker, bars in resp.json().get("bars", {}).items():
                result[ticker] = bars
        except Exception as exc:
            log.warning("Oracle intraday_today chunk %d failed: %s", i // chunk_size, exc)

    return result


def get_snapshot(ticker: str, base_url: str | None = None) -> AlpacaSnapshot | None:
    """Fetch real-time snapshot for a single ticker (synchronous).

    Returns None on any error.
    """
    import httpx

    url = (base_url or _base_url()) + f"/api/v1/alpaca/snapshot/{ticker}"
    try:
        resp = httpx.get(url, timeout=10.0)
        resp.raise_for_status()
        return _parse_snapshot(resp.json())
    except Exception as exc:
        log.warning("Oracle snapshot(%s) failed: %s", ticker, exc)
        return None


def get_snapshots(
    tickers: list[str],
    base_url: str | None = None,
    batch_size: int = 200,
) -> dict[str, AlpacaSnapshot]:
    """Fetch real-time snapshots for multiple tickers (synchronous).

    Batches requests and returns {ticker: AlpacaSnapshot}.
    Missing/errored tickers are omitted.
    """
    import httpx

    if not tickers:
        return {}

    result: dict[str, AlpacaSnapshot] = {}
    url = (base_url or _base_url()) + "/api/v1/alpaca/snapshot"

    for i in range(0, len(tickers), batch_size):
        chunk = tickers[i : i + batch_size]
        try:
            resp = httpx.get(url, params={"tickers": ",".join(chunk)}, timeout=15.0)
            resp.raise_for_status()
            data = resp.json()
            for snap_data in data.get("snapshots", []):
                snap = _parse_snapshot(snap_data)
                result[snap.ticker] = snap
        except Exception as exc:
            log.warning("Oracle snapshots(batch %d) failed: %s", i // batch_size, exc)

    return result
