"""Stock market hedging pipeline functions.

Pipeline functions:
    hedge_monitor - Monitor hedge ratio, effectiveness, basis risk
    hedge_executor - Execute hedge rebalancing when drift exceeds threshold
    correlation_tracker - Track pairwise correlations across feeds
    stock_hedge - All-in-one hedge monitor + executor + correlation tracker

Standalone functions:
    compute_hedge_report - Generate a one-shot hedge analysis report
    run_scenario - Run scenario analysis on a hedged position
"""

from __future__ import annotations

import logging
from dataclasses import dataclass, field
from typing import Any, Callable

from horizon._horizon import (
    HedgeCostTracker,
    OrderRequest,
    OrderSide,
    Side,
    auth_require_ultra,
    basis_risk as _basis_risk,
    compute_hedge_sensitivities as _compute_sensitivities,
    hedge_effectiveness as _hedge_effectiveness,
    hedge_ratio_ols as _hedge_ratio_ols,
    hedge_scenario as _hedge_scenario,
    multi_ticker_hedge as _multi_ticker_hedge,
    optimal_hedge_size as _optimal_hedge_size,
    rolling_correlation as _rolling_correlation,
)
from horizon.context import Context

logger = logging.getLogger("horizon")


@dataclass
class HedgeConfig:
    """Configuration for stock hedge pipeline functions."""

    prediction_feed: str = ""
    stock_feed: str = ""
    stock_symbol: str = ""
    window: int = 60
    rebalance_threshold: float = 0.05
    max_hedge_notional: float = 100_000.0
    auto_rebalance: bool = False
    exchange: str = "alpaca"


def hedge_monitor(
    prediction_feed: str,
    stock_feed: str,
    window: int = 60,
) -> Callable[[Context], dict[str, Any]]:
    """Create a hedge monitoring pipeline function.

    Tracks rolling hedge ratio, effectiveness, basis risk, and correlation
    between a prediction market feed and a stock feed.

    Args:
        prediction_feed: Feed name for prediction market prices.
        stock_feed: Feed name for stock/ETF prices.
        window: Rolling window size for calculations.

    Returns:
        Pipeline function: (Context) -> dict with keys:
            hedge_ratio, hedge_effectiveness, basis_risk, correlation,
            recommended_hedge_size, spread_zscore
    """
    auth_require_ultra()

    price_history: dict[str, list[float]] = {}

    def _hedge_monitor(ctx: Context) -> dict[str, Any]:
        market_id = ctx.market.id if ctx.market else "__default__"

        if market_id not in price_history:
            price_history[market_id] = []

        pred_fd = ctx.feeds.get(prediction_feed)
        stock_fd = ctx.feeds.get(stock_feed)

        result: dict[str, Any] = {
            "hedge_ratio": 0.0,
            "hedge_effectiveness": 0.0,
            "basis_risk": 0.0,
            "correlation": 0.0,
            "recommended_hedge_size": 0.0,
            "spread_zscore": 0.0,
        }

        if pred_fd is None or stock_fd is None:
            return result
        if pred_fd.price <= 0 or stock_fd.price <= 0:
            return result

        # Build key for separate price histories
        pred_key = f"{market_id}:pred"
        stock_key = f"{market_id}:stock"

        if pred_key not in price_history:
            price_history[pred_key] = []
        if stock_key not in price_history:
            price_history[stock_key] = []

        price_history[pred_key].append(pred_fd.price)
        price_history[stock_key].append(stock_fd.price)

        # Keep history bounded
        max_hist = window * 3
        if len(price_history[pred_key]) > max_hist:
            price_history[pred_key] = price_history[pred_key][-max_hist:]
        if len(price_history[stock_key]) > max_hist:
            price_history[stock_key] = price_history[stock_key][-max_hist:]

        pred_prices = price_history[pred_key]
        stock_prices = price_history[stock_key]

        if len(pred_prices) < 5 or len(stock_prices) < 5:
            return result

        # Compute hedge metrics
        h = _hedge_ratio_ols(pred_prices, stock_prices, window)
        result["hedge_ratio"] = h

        # Compute returns for effectiveness/basis risk
        pred_returns = _log_returns(pred_prices)
        stock_returns = _log_returns(stock_prices)

        if len(pred_returns) >= 3 and len(stock_returns) >= 3:
            result["hedge_effectiveness"] = _hedge_effectiveness(pred_returns, stock_returns, h)
            result["basis_risk"] = _basis_risk(pred_returns, stock_returns, h)

        # Correlation
        corr = _rolling_correlation(pred_prices, stock_prices, min(window, len(pred_prices)))
        if corr:
            result["correlation"] = corr[-1]

        # Recommended hedge size (per unit of spot notional)
        result["recommended_hedge_size"] = _optimal_hedge_size(1.0, h)

        # Spread z-score
        if len(pred_prices) >= window:
            spreads = [
                pred_prices[i] - h * stock_prices[i]
                for i in range(len(pred_prices))
                if i < len(stock_prices)
            ]
            if spreads:
                mean_s = sum(spreads) / len(spreads)
                var_s = sum((s - mean_s) ** 2 for s in spreads) / max(len(spreads) - 1, 1)
                std_s = var_s**0.5
                if std_s > 1e-15:
                    result["spread_zscore"] = (spreads[-1] - mean_s) / std_s

        return result

    _hedge_monitor.__name__ = "hedge_monitor"
    return _hedge_monitor


def hedge_executor(
    config: HedgeConfig,
) -> Callable[[Context], dict[str, Any]]:
    """Create a hedge execution pipeline function.

    Monitors hedge drift and flags when rebalancing is needed.
    If auto_rebalance is True, generates OrderRequests for the stock exchange.

    Args:
        config: HedgeConfig with thresholds and exchange settings.

    Returns:
        Pipeline function: (Context) -> dict with keys:
            rebalance_needed, current_hedge_ratio, target_hedge_ratio,
            drift, orders, total_cost
    """
    auth_require_ultra()

    cost_tracker = HedgeCostTracker()
    last_hedge_ratio: dict[str, float] = {}

    def _hedge_executor(ctx: Context) -> dict[str, Any]:
        market_id = ctx.market.id if ctx.market else "__default__"

        result: dict[str, Any] = {
            "rebalance_needed": False,
            "current_hedge_ratio": last_hedge_ratio.get(market_id, 0.0),
            "target_hedge_ratio": 0.0,
            "drift": 0.0,
            "orders": [],
            "total_cost": cost_tracker.total_cost(),
            "cost_pct": cost_tracker.cost_as_pct_of_notional(),
        }

        # Get hedge ratio from pipeline state
        hr = ctx.state.get("hedge_ratio", 0.0) if hasattr(ctx, "state") and isinstance(ctx.state, dict) else 0.0
        if hr == 0.0:
            return result

        result["target_hedge_ratio"] = hr

        # Calculate drift
        current = last_hedge_ratio.get(market_id, 0.0)
        drift = abs(hr - current)
        result["drift"] = drift
        result["current_hedge_ratio"] = current

        if drift > config.rebalance_threshold:
            result["rebalance_needed"] = True
            last_hedge_ratio[market_id] = hr

            if config.auto_rebalance and config.stock_symbol:
                # Calculate order size
                stock_fd = ctx.feeds.get(config.stock_feed)
                stock_price = stock_fd.price if stock_fd and stock_fd.price > 0 else 0.0

                if stock_price > 0:
                    # Get spot notional from position
                    positions = ctx.engine.positions() if ctx.engine else []
                    spot_notional = 0.0
                    for pos in positions:
                        if pos.market_id == market_id:
                            spot_notional = pos.size * pos.avg_entry_price
                            break

                    hedge_notional = _optimal_hedge_size(
                        spot_notional, hr, config.max_hedge_notional
                    )
                    shares = hedge_notional / stock_price

                    if shares > 0.01:
                        order = OrderRequest(
                            market_id=config.stock_symbol,
                            side=Side.Yes,
                            order_side=OrderSide.Buy if hr > 0 else OrderSide.Sell,
                            size=round(shares, 2),
                            price=stock_price,
                        )
                        result["orders"] = [order]

                        # Track cost (estimated)
                        cost_tracker.record_trade(hedge_notional, 0.0, stock_price * 0.001)

        result["total_cost"] = cost_tracker.total_cost()
        result["cost_pct"] = cost_tracker.cost_as_pct_of_notional()

        return result

    _hedge_executor.__name__ = "hedge_executor"
    return _hedge_executor


def correlation_tracker(
    feeds: list[str],
    window: int = 60,
) -> Callable[[Context], dict[str, Any]]:
    """Create a correlation tracking pipeline function.

    Tracks pairwise rolling correlations across multiple feeds.

    Args:
        feeds: List of feed names to track.
        window: Rolling window for correlation calculation.

    Returns:
        Pipeline function: (Context) -> dict with keys:
            correlations (dict of pair -> correlation),
            top_pairs (list of (pair, corr) sorted by |corr|)
    """
    auth_require_ultra()

    price_histories: dict[str, list[float]] = {}

    def _correlation_tracker(ctx: Context) -> dict[str, Any]:
        result: dict[str, Any] = {
            "correlations": {},
            "top_pairs": [],
        }

        # Collect prices
        for feed_name in feeds:
            fd = ctx.feeds.get(feed_name)
            if fd is not None and fd.price > 0:
                if feed_name not in price_histories:
                    price_histories[feed_name] = []
                price_histories[feed_name].append(fd.price)
                # Bound history
                max_hist = window * 3
                if len(price_histories[feed_name]) > max_hist:
                    price_histories[feed_name] = price_histories[feed_name][-max_hist:]

        # Compute pairwise correlations
        correlations: dict[str, float] = {}
        for i, f1 in enumerate(feeds):
            for f2 in feeds[i + 1 :]:
                h1 = price_histories.get(f1, [])
                h2 = price_histories.get(f2, [])
                min_len = min(len(h1), len(h2))
                if min_len >= window:
                    corr = _rolling_correlation(
                        h1[-min_len:], h2[-min_len:], window
                    )
                    if corr:
                        pair_key = f"{f1}/{f2}"
                        correlations[pair_key] = corr[-1]

        result["correlations"] = correlations

        # Sort by absolute correlation
        sorted_pairs = sorted(
            correlations.items(), key=lambda x: abs(x[1]), reverse=True
        )
        result["top_pairs"] = sorted_pairs

        return result

    _correlation_tracker.__name__ = "correlation_tracker"
    return _correlation_tracker


def stock_hedge(
    config: HedgeConfig,
) -> Callable[[Context], dict[str, Any]]:
    """Create an all-in-one stock hedge pipeline function.

    Combines hedge_monitor + hedge_executor.

    Args:
        config: HedgeConfig with all settings.

    Returns:
        Pipeline function: (Context) -> dict with combined hedge state.
    """
    auth_require_ultra()

    monitor = hedge_monitor(
        config.prediction_feed, config.stock_feed, config.window
    )
    executor = hedge_executor(config)

    def _stock_hedge(ctx: Context) -> dict[str, Any]:
        monitor_result = monitor(ctx)
        # Inject hedge_ratio into state for executor
        if hasattr(ctx, "state") and isinstance(ctx.state, dict):
            ctx.state["hedge_ratio"] = monitor_result.get("hedge_ratio", 0.0)
        executor_result = executor(ctx)

        return {**monitor_result, **executor_result}

    _stock_hedge.__name__ = "stock_hedge"
    return _stock_hedge


def compute_hedge_report(
    spot_prices: list[float],
    hedge_prices: list[float],
    window: int = 60,
    position_size: float = 1.0,
) -> dict[str, Any]:
    """Generate a one-shot hedge analysis report.

    Args:
        spot_prices: Historical spot (prediction market) prices.
        hedge_prices: Historical hedge (stock) prices.
        window: Rolling window for calculations.
        position_size: Position size for sensitivity calculations.

    Returns:
        dict with hedge_ratio, effectiveness, basis_risk, sensitivities,
        correlation history.
    """
    auth_require_ultra()

    h = _hedge_ratio_ols(spot_prices, hedge_prices, window)

    spot_ret = _log_returns(spot_prices)
    hedge_ret = _log_returns(hedge_prices)

    he = _hedge_effectiveness(spot_ret, hedge_ret, h) if len(spot_ret) >= 3 else 0.0
    br = _basis_risk(spot_ret, hedge_ret, h) if len(spot_ret) >= 3 else 0.0

    sens = _compute_sensitivities(spot_prices, hedge_prices, position_size, window)
    corr_history = _rolling_correlation(spot_prices, hedge_prices, window)

    return {
        "hedge_ratio": h,
        "hedge_effectiveness": he,
        "basis_risk": br,
        "sensitivities": {
            "delta": sens.delta,
            "cross_gamma": sens.cross_gamma,
            "beta": sens.beta,
            "tracking_error": sens.tracking_error,
            "hedge_decay": sens.hedge_decay,
        },
        "correlation_history": corr_history,
        "current_correlation": corr_history[-1] if corr_history else 0.0,
        "recommended_hedge_size": _optimal_hedge_size(1.0, h),
    }


def run_scenario(
    spot_position: float,
    spot_price: float,
    hedge_position: float,
    hedge_price: float,
    spot_moves: list[float],
    correlation: float = 0.8,
    hedge_vol: float = 0.2,
    spot_vol: float = 0.2,
) -> list[dict[str, float]]:
    """Run multiple scenario analyses on a hedged position.

    Args:
        spot_position: Number of spot units held.
        spot_price: Current spot price.
        hedge_position: Number of hedge units held (negative for short).
        hedge_price: Current hedge price.
        spot_moves: List of spot move percentages to simulate (e.g., [-0.10, -0.05, 0.05, 0.10]).
        correlation: Correlation between spot and hedge.
        hedge_vol: Hedge instrument annualized volatility.
        spot_vol: Spot instrument annualized volatility.

    Returns:
        List of scenario outcome dicts.
    """
    auth_require_ultra()

    results = []
    for move_pct in spot_moves:
        outcome = _hedge_scenario(
            spot_position, spot_price, hedge_position, hedge_price,
            move_pct, correlation, hedge_vol, spot_vol,
        )
        results.append({
            "spot_move_pct": outcome.spot_move_pct,
            "hedge_move_pct": outcome.hedge_move_pct,
            "portfolio_pnl": outcome.portfolio_pnl,
            "unhedged_pnl": outcome.unhedged_pnl,
            "hedge_benefit": outcome.hedge_benefit,
        })
    return results


def _log_returns(prices: list[float]) -> list[float]:
    """Compute log returns from a price series."""
    import math
    if len(prices) < 2:
        return []
    returns = []
    for i in range(1, len(prices)):
        if prices[i] > 0 and prices[i - 1] > 0:
            returns.append(math.log(prices[i] / prices[i - 1]))
        else:
            returns.append(0.0)
    return returns
