"""Tool functions for third-party data providers.

Thin wrappers around Unusual Whales and Massive APIs that return
plain dicts/lists for JSON serialization. Used by MCP server and CLI.
"""

from __future__ import annotations

import logging
from typing import Any

import httpx

logger = logging.getLogger(__name__)


def _safe(fn, *args, **kwargs) -> Any:
    """Call fn, return error dict on failure. Sanitizes error messages to prevent key leakage."""
    try:
        return fn(*args, **kwargs)
    except httpx.HTTPStatusError as e:
        logger.debug("Provider HTTP error: %s", e, exc_info=True)
        return {"error": f"HTTP {e.response.status_code} from {e.request.url.path}"}
    except httpx.RequestError as e:
        logger.debug("Provider request error: %s", e, exc_info=True)
        return {"error": f"Request failed: {type(e).__name__}: {e.args[0] if e.args else 'connection error'}"}
    except ValueError as e:
        return {"error": str(e)}
    except Exception as e:
        logger.debug("Provider error: %s", e, exc_info=True)
        return {"error": f"{type(e).__name__}: {e}"}


# ---------------------------------------------------------------------------
# Unusual Whales
# ---------------------------------------------------------------------------

def uw_options_flow(
    *,
    ticker: str | None = None,
    min_premium: int | None = None,
    is_sweep: bool | None = None,
    is_call: bool | None = None,
    is_put: bool | None = None,
    is_otm: bool | None = None,
    min_dte: int | None = None,
    max_dte: int | None = None,
    limit: int = 50,
) -> list[dict] | dict:
    """Get unusual options flow alerts."""
    from horizon.providers.unusual_whales import UnusualWhalesProvider as UW
    return _safe(
        UW.options_flow,
        ticker=ticker, min_premium=min_premium, is_sweep=is_sweep,
        is_call=is_call, is_put=is_put, is_otm=is_otm,
        min_dte=min_dte, max_dte=max_dte, limit=limit,
    )


def uw_stock_flow(ticker: str, *, side: str | None = None, min_premium: int | None = None) -> list[dict] | dict:
    """Get recent options flow for a ticker."""
    from horizon.providers.unusual_whales import UnusualWhalesProvider as UW
    return _safe(UW.stock_flow, ticker, side=side, min_premium=min_premium)


def uw_dark_pool_recent() -> list[dict] | dict:
    """Get recent market-wide dark pool prints."""
    from horizon.providers.unusual_whales import UnusualWhalesProvider as UW
    return _safe(UW.dark_pool_recent)


def uw_dark_pool(ticker: str, *, date: str | None = None, min_size: int | None = None, limit: int = 50) -> list[dict] | dict:
    """Get dark pool trades for a ticker."""
    from horizon.providers.unusual_whales import UnusualWhalesProvider as UW
    return _safe(UW.dark_pool, ticker, date=date, min_size=min_size, limit=limit)


def uw_congress_trades(*, limit: int = 50, ticker: str | None = None, date: str | None = None) -> list[dict] | dict:
    """Get recent Congress member stock trades."""
    from horizon.providers.unusual_whales import UnusualWhalesProvider as UW
    return _safe(UW.congress_trades, limit=limit, ticker=ticker, date=date)


def uw_congress_trader(name: str) -> list[dict] | dict:
    """Get trades for a specific Congress member."""
    from horizon.providers.unusual_whales import UnusualWhalesProvider as UW
    return _safe(UW.congress_trader, name)


def uw_insider_trades(
    *,
    ticker: str | None = None,
    min_value: int | None = None,
    is_officer: bool | None = None,
    is_director: bool | None = None,
    is_ten_percent_owner: bool | None = None,
    start_date: str | None = None,
    end_date: str | None = None,
    limit: int = 50,
) -> list[dict] | dict:
    """Get insider transactions (Form 4)."""
    from horizon.providers.unusual_whales import UnusualWhalesProvider as UW
    return _safe(
        UW.insider_trades,
        ticker=ticker, min_value=min_value, is_officer=is_officer,
        is_director=is_director, is_ten_percent_owner=is_ten_percent_owner,
        start_date=start_date, end_date=end_date, limit=limit,
    )


def uw_market_tide(*, date: str | None = None, otm_only: bool | None = None) -> list[dict] | dict:
    """Get Market Tide sentiment indicator."""
    from horizon.providers.unusual_whales import UnusualWhalesProvider as UW
    return _safe(UW.market_tide, date=date, otm_only=otm_only)


def uw_spike() -> dict:
    """Get SPIKE volatility indicator."""
    from horizon.providers.unusual_whales import UnusualWhalesProvider as UW
    return _safe(UW.spike)


def uw_economic_calendar() -> list[dict] | dict:
    """Get upcoming economic events."""
    from horizon.providers.unusual_whales import UnusualWhalesProvider as UW
    return _safe(UW.economic_calendar)


def uw_fda_calendar() -> list[dict] | dict:
    """Get FDA calendar events."""
    from horizon.providers.unusual_whales import UnusualWhalesProvider as UW
    return _safe(UW.fda_calendar)


def uw_stock_info(ticker: str) -> dict:
    """Get stock information."""
    from horizon.providers.unusual_whales import UnusualWhalesProvider as UW
    return _safe(UW.stock_info, ticker)


def uw_stock_state(ticker: str) -> dict:
    """Get current stock state."""
    from horizon.providers.unusual_whales import UnusualWhalesProvider as UW
    return _safe(UW.stock_state, ticker)


def uw_earnings(ticker: str) -> list[dict] | dict:
    """Get earnings history for a ticker."""
    from horizon.providers.unusual_whales import UnusualWhalesProvider as UW
    return _safe(UW.earnings, ticker)


def uw_earnings_schedule(time: str = "premarket") -> list[dict] | dict:
    """Get earnings schedule. time: 'premarket' or 'afterhours'."""
    if time not in ("premarket", "afterhours"):
        return {"error": f"Invalid time: {time!r} — must be 'premarket' or 'afterhours'"}
    from horizon.providers.unusual_whales import UnusualWhalesProvider as UW
    fn = UW.earnings_premarket if time == "premarket" else UW.earnings_afterhours
    return _safe(fn)


def uw_greek_exposure(ticker: str) -> dict:
    """Get aggregate Greek exposure (GEX, DEX)."""
    from horizon.providers.unusual_whales import UnusualWhalesProvider as UW
    return _safe(UW.greek_exposure, ticker)


def uw_greek_exposure_by_strike(ticker: str) -> list[dict] | dict:
    """Get Greek exposure by strike."""
    from horizon.providers.unusual_whales import UnusualWhalesProvider as UW
    return _safe(UW.greek_exposure_by_strike, ticker)


def uw_iv_rank(ticker: str) -> dict:
    """Get IV rank percentile."""
    from horizon.providers.unusual_whales import UnusualWhalesProvider as UW
    return _safe(UW.iv_rank, ticker)


def uw_vol_term_structure(ticker: str) -> list[dict] | dict:
    """Get IV term structure."""
    from horizon.providers.unusual_whales import UnusualWhalesProvider as UW
    return _safe(UW.vol_term_structure, ticker)


def uw_max_pain(ticker: str) -> dict:
    """Get max pain levels."""
    from horizon.providers.unusual_whales import UnusualWhalesProvider as UW
    return _safe(UW.max_pain, ticker)


def uw_option_chains(ticker: str) -> list[dict] | dict:
    """Get full option chains."""
    from horizon.providers.unusual_whales import UnusualWhalesProvider as UW
    return _safe(UW.option_chains, ticker)


def uw_short_interest(ticker: str) -> dict:
    """Get short interest and float data."""
    from horizon.providers.unusual_whales import UnusualWhalesProvider as UW
    return _safe(UW.short_interest, ticker)


def uw_short_volume(ticker: str) -> list[dict] | dict:
    """Get short volume and ratio history."""
    from horizon.providers.unusual_whales import UnusualWhalesProvider as UW
    return _safe(UW.short_volume, ticker)


def uw_ftd(ticker: str) -> list[dict] | dict:
    """Get failures to deliver."""
    from horizon.providers.unusual_whales import UnusualWhalesProvider as UW
    return _safe(UW.ftd, ticker)


def uw_screener_stocks() -> list[dict] | dict:
    """Screen stocks."""
    from horizon.providers.unusual_whales import UnusualWhalesProvider as UW
    return _safe(UW.screener_stocks)


def uw_screener_options(
    *,
    limit: int = 50,
    min_premium: int | None = None,
    is_otm: bool | None = None,
    min_volume_oi_ratio: float | None = None,
) -> list[dict] | dict:
    """Screen hottest option chains."""
    from horizon.providers.unusual_whales import UnusualWhalesProvider as UW
    return _safe(
        UW.screener_options,
        limit=limit, min_premium=min_premium, is_otm=is_otm,
        min_volume_oi_ratio=min_volume_oi_ratio,
    )


def uw_institutions() -> list[dict] | dict:
    """List tracked institutions."""
    from horizon.providers.unusual_whales import UnusualWhalesProvider as UW
    return _safe(UW.institutions)


def uw_institution_holdings(name: str) -> list[dict] | dict:
    """Get institution holdings."""
    from horizon.providers.unusual_whales import UnusualWhalesProvider as UW
    return _safe(UW.institution_holdings, name)


def uw_etf_holdings(ticker: str) -> list[dict] | dict:
    """Get ETF holdings."""
    from horizon.providers.unusual_whales import UnusualWhalesProvider as UW
    return _safe(UW.etf_holdings, ticker)


def uw_etf_flow(ticker: str) -> list[dict] | dict:
    """Get ETF inflow/outflow."""
    from horizon.providers.unusual_whales import UnusualWhalesProvider as UW
    return _safe(UW.etf_flow, ticker)


def uw_seasonality(ticker: str) -> list[dict] | dict:
    """Get monthly seasonality for a ticker."""
    from horizon.providers.unusual_whales import UnusualWhalesProvider as UW
    return _safe(UW.seasonality, ticker)


def uw_news() -> list[dict] | dict:
    """Get financial news headlines."""
    from horizon.providers.unusual_whales import UnusualWhalesProvider as UW
    return _safe(UW.news)


def uw_prediction_whales() -> list[dict] | dict:
    """Get prediction market whales."""
    from horizon.providers.unusual_whales import UnusualWhalesProvider as UW
    return _safe(UW.prediction_whales)


def uw_prediction_unusual() -> list[dict] | dict:
    """Get unusual prediction market activity."""
    from horizon.providers.unusual_whales import UnusualWhalesProvider as UW
    return _safe(UW.prediction_unusual)


def uw_crypto_whales() -> list[dict] | dict:
    """Get recent crypto whale transactions."""
    from horizon.providers.unusual_whales import UnusualWhalesProvider as UW
    return _safe(UW.crypto_whales)


def uw_greek_exposure_by_expiry(ticker: str) -> list[dict] | dict:
    """Get Greek exposure by expiry."""
    from horizon.providers.unusual_whales import UnusualWhalesProvider as UW
    return _safe(UW.greek_exposure_by_expiry, ticker)


def uw_institution_ownership(ticker: str) -> list[dict] | dict:
    """Get institutional ownership of a ticker."""
    from horizon.providers.unusual_whales import UnusualWhalesProvider as UW
    return _safe(UW.institution_ownership, ticker)


# ---------------------------------------------------------------------------
# Massive (Polygon.io)
# ---------------------------------------------------------------------------

def massive_aggregates(
    ticker: str,
    multiplier: int = 1,
    timespan: str = "day",
    from_date: str = "",
    to_date: str = "",
    *,
    limit: int = 5000,
) -> list[dict] | dict:
    """Get OHLCV bars for a ticker."""
    from horizon.providers.massive import MassiveProvider as M
    return _safe(M.aggregates, ticker, multiplier, timespan, from_date, to_date, limit=limit)


def massive_previous_close(ticker: str) -> list[dict] | dict:
    """Get previous close for a ticker."""
    from horizon.providers.massive import MassiveProvider as M
    return _safe(M.previous_close, ticker)


def massive_snapshot(ticker: str) -> dict:
    """Get real-time snapshot for a ticker."""
    from horizon.providers.massive import MassiveProvider as M
    return _safe(M.snapshot, ticker)


def massive_gainers_losers(direction: str = "gainers") -> list[dict] | dict:
    """Get top 20 gainers or losers."""
    from horizon.providers.massive import MassiveProvider as M
    return _safe(M.gainers_losers, direction)


def massive_last_trade(ticker: str) -> dict:
    """Get most recent trade for a ticker."""
    from horizon.providers.massive import MassiveProvider as M
    return _safe(M.last_trade, ticker)


def massive_last_quote(ticker: str) -> dict:
    """Get most recent NBBO quote for a ticker."""
    from horizon.providers.massive import MassiveProvider as M
    return _safe(M.last_quote, ticker)


def massive_ticker_details(ticker: str) -> dict:
    """Get ticker details: name, market cap, description, sector."""
    from horizon.providers.massive import MassiveProvider as M
    return _safe(M.ticker_details, ticker)


def massive_search_tickers(query: str = "", *, market: str | None = None, limit: int = 20) -> list[dict] | dict:
    """Search/list tickers."""
    from horizon.providers.massive import MassiveProvider as M
    return _safe(M.search_tickers, query, market=market, limit=limit)


def massive_market_status() -> dict:
    """Get current market status."""
    from horizon.providers.massive import MassiveProvider as M
    return _safe(M.market_status)


def massive_options_chain(
    underlying: str,
    *,
    contract_type: str | None = None,
    expiration_date: str | None = None,
    expiration_date_gte: str | None = None,
    strike_price_gte: float | None = None,
    strike_price_lte: float | None = None,
    limit: int = 250,
) -> list[dict] | dict:
    """Get options chain with Greeks for an underlying."""
    from horizon.providers.massive import MassiveProvider as M
    return _safe(
        M.options_chain, underlying,
        contract_type=contract_type, expiration_date=expiration_date,
        expiration_date_gte=expiration_date_gte,
        strike_price_gte=strike_price_gte, strike_price_lte=strike_price_lte,
        limit=limit,
    )


def massive_sma(ticker: str, *, timespan: str = "day", window: int = 50, limit: int = 50) -> list[dict] | dict:
    """Get Simple Moving Average."""
    from horizon.providers.massive import MassiveProvider as M
    return _safe(M.sma, ticker, timespan=timespan, window=window, limit=limit)


def massive_ema(ticker: str, *, timespan: str = "day", window: int = 50, limit: int = 50) -> list[dict] | dict:
    """Get Exponential Moving Average."""
    from horizon.providers.massive import MassiveProvider as M
    return _safe(M.ema, ticker, timespan=timespan, window=window, limit=limit)


def massive_rsi(ticker: str, *, timespan: str = "day", window: int = 14, limit: int = 50) -> list[dict] | dict:
    """Get Relative Strength Index."""
    from horizon.providers.massive import MassiveProvider as M
    return _safe(M.rsi, ticker, timespan=timespan, window=window, limit=limit)


def massive_macd(ticker: str, *, timespan: str = "day", limit: int = 50) -> list[dict] | dict:
    """Get MACD indicator."""
    from horizon.providers.massive import MassiveProvider as M
    return _safe(M.macd, ticker, timespan=timespan, limit=limit)


def massive_news(*, ticker: str | None = None, limit: int = 20) -> list[dict] | dict:
    """Get market news."""
    from horizon.providers.massive import MassiveProvider as M
    return _safe(M.news, ticker=ticker, limit=limit)


def massive_dividends(ticker: str, *, limit: int = 20) -> list[dict] | dict:
    """Get dividend history."""
    from horizon.providers.massive import MassiveProvider as M
    return _safe(M.dividends, ticker, limit=limit)


def massive_splits(ticker: str, *, limit: int = 20) -> list[dict] | dict:
    """Get stock split history."""
    from horizon.providers.massive import MassiveProvider as M
    return _safe(M.splits, ticker, limit=limit)


def massive_crypto_snapshot(ticker: str) -> dict:
    """Get crypto snapshot (e.g. X:BTCUSD)."""
    from horizon.providers.massive import MassiveProvider as M
    return _safe(M.crypto_snapshot, ticker)


def massive_forex_conversion(from_currency: str, to_currency: str) -> dict:
    """Get real-time forex conversion rate."""
    from horizon.providers.massive import MassiveProvider as M
    return _safe(M.forex_conversion, from_currency, to_currency)


def massive_grouped_daily(date: str, *, adjusted: bool = True) -> list[dict] | dict:
    """Get OHLCV for the entire market on a date."""
    from horizon.providers.massive import MassiveProvider as M
    return _safe(M.grouped_daily, date, adjusted=adjusted)


def massive_market_holidays() -> list[dict] | dict:
    """Get upcoming market holidays."""
    from horizon.providers.massive import MassiveProvider as M
    return _safe(M.market_holidays)


def massive_options_contracts(
    *,
    underlying_ticker: str | None = None,
    contract_type: str | None = None,
    expiration_date: str | None = None,
    strike_price: float | None = None,
    limit: int = 100,
) -> list[dict] | dict:
    """List options contracts with filters."""
    from horizon.providers.massive import MassiveProvider as M
    return _safe(
        M.options_contracts,
        underlying_ticker=underlying_ticker, contract_type=contract_type,
        expiration_date=expiration_date, strike_price=strike_price, limit=limit,
    )


def massive_crypto_gainers_losers(direction: str = "gainers") -> list[dict] | dict:
    """Get top crypto gainers or losers."""
    from horizon.providers.massive import MassiveProvider as M
    return _safe(M.crypto_gainers_losers, direction)


def massive_forex_snapshot(ticker: str) -> dict:
    """Get forex pair snapshot (e.g. C:EURUSD)."""
    from horizon.providers.massive import MassiveProvider as M
    return _safe(M.forex_snapshot, ticker)
