"""Horizon SDK MCP Server.

Exposes the Horizon trading engine as MCP tools for Claude Code,
Cursor, Claude Desktop, and other MCP-compatible clients.

Run: python -m horizon.mcp
"""

from __future__ import annotations

from mcp.server.fastmcp import FastMCP

from horizon import tools

mcp = FastMCP(
    "horizon-sdk",
    instructions=(
        "Horizon SDK - prediction market trading engine. "
        "Use these tools to check positions, submit orders, manage risk, "
        "discover markets, analyze wallets, start live data feeds (Binance, "
        "Chainlink, Polymarket, Kalshi, ESPN, NWS, and custom REST), check feed "
        "health, run Monte Carlo simulations, execute arbitrage, compute "
        "Kelly-optimal sizing, run quantitative analytics, stress tests, and "
        "signal diagnostics on Polymarket and Kalshi."
    ),
)


# ---------------------------------------------------------------------------
# Tools
# ---------------------------------------------------------------------------

@mcp.tool()
def engine_status() -> dict:
    """Get trading engine status: PnL, open orders, positions, kill switch state, uptime."""
    return tools.engine_status()


@mcp.tool()
def list_positions() -> list[dict]:
    """List all open positions with size, entry price, PnL, and exchange."""
    return tools.list_positions()


@mcp.tool()
def list_open_orders(market_id: str | None = None) -> list[dict]:
    """List open orders. Optionally filter by market_id."""
    return tools.list_open_orders(market_id)


@mcp.tool()
def list_recent_fills(limit: int = 20) -> list[dict]:
    """List recent trade fills. Default last 20."""
    return tools.list_recent_fills(limit)


@mcp.tool()
def submit_order(
    market_id: str, side: str, price: float, size: float, market_side: str = "yes",
) -> dict:
    """Submit a limit order. side: 'buy' or 'sell'. market_side: 'yes' or 'no'. price: 0-1 (probability). Returns order ID."""
    return tools.submit_order(market_id, side, price, size, market_side)


@mcp.tool()
def cancel_order(order_id: str) -> dict:
    """Cancel a single order by its ID."""
    return tools.cancel_order(order_id)


@mcp.tool()
def cancel_all_orders() -> dict:
    """Cancel ALL open orders across all exchanges. Use with caution."""
    return tools.cancel_all_orders()


@mcp.tool()
def cancel_market_orders(market_id: str) -> dict:
    """Cancel all orders for a specific market."""
    return tools.cancel_market_orders(market_id)


@mcp.tool()
def activate_kill_switch(reason: str) -> dict:
    """EMERGENCY STOP. Activates the kill switch and cancels all orders. Use only in emergencies."""
    return tools.activate_kill_switch(reason)


@mcp.tool()
def deactivate_kill_switch() -> dict:
    """Deactivate the kill switch to resume normal trading."""
    return tools.deactivate_kill_switch()


@mcp.tool()
def get_feed_snapshot(name: str) -> dict:
    """Get the latest price/bid/ask snapshot for a named feed."""
    return tools.get_feed_snapshot(name)


@mcp.tool()
def list_all_feeds() -> list[dict]:
    """List all active feed snapshots with current prices."""
    return tools.list_all_feeds()


@mcp.tool()
def start_feed(
    name: str,
    feed_type: str,
    config_json: str | None = None,
    symbol: str | None = None,
    url: str | None = None,
    interval: float = 5.0,
) -> dict:
    """Start a live data feed. feed_type: 'binance_ws', 'polymarket_book', 'kalshi_book', 'rest', 'predictit', 'manifold', 'espn', 'nws', 'rest_json_path', 'chainlink'. For chainlink: config_json='{"contract_address":"0x...","rpc_url":"https://..."}'. For binance_ws: symbol='btcusdt'."""
    return tools.start_feed(name, feed_type, config_json, symbol, url, interval)


@mcp.tool()
def discover_markets(
    exchange: str = "polymarket",
    query: str = "",
    limit: int = 10,
    market_type: str = "all",
) -> list[dict]:
    """Search for prediction markets on Polymarket or Kalshi. market_type: 'all' (binary+multi), 'binary', or 'multi'."""
    return tools.discover(exchange, query, limit, market_type)


@mcp.tool()
def kelly_sizing(
    prob: float,
    price: float,
    bankroll: float = 1000.0,
    fraction: float = 0.25,
    max_size: float = 100.0,
) -> dict:
    """Compute Kelly-optimal position size. prob: estimated probability (0-1). price: market price (0-1)."""
    return tools.kelly_sizing(prob, price, bankroll, fraction, max_size)


@mcp.tool()
def check_parity(market_id: str, feed_name: str | None = None) -> dict:
    """Check YES/NO price parity for a market. Optionally specify feed_name to use a specific feed."""
    return tools.check_parity(market_id, feed_name)


@mcp.tool()
def get_feed_metrics(name: str) -> dict:
    """Get connection metrics for a feed: update count, errors, reconnects, connection status."""
    return tools.feed_metrics(name)


@mcp.tool()
def get_all_feed_metrics() -> list[dict]:
    """Get connection metrics for all active feeds."""
    return tools.all_feed_metrics()


@mcp.tool()
def add_stop_loss(
    market_id: str, side: str, order_side: str, size: float, trigger: float
) -> dict:
    """Add a stop-loss order. side: 'yes'/'no'. order_side: 'buy'/'sell'. Triggers at price."""
    return tools.add_stop_loss(market_id, side, order_side, size, trigger)


@mcp.tool()
def add_take_profit(
    market_id: str, side: str, order_side: str, size: float, trigger: float
) -> dict:
    """Add a take-profit order. side: 'yes'/'no'. order_side: 'buy'/'sell'. Triggers at price."""
    return tools.add_take_profit(market_id, side, order_side, size, trigger)


@mcp.tool()
def list_contingent_orders() -> list[dict]:
    """List all pending stop-loss and take-profit orders."""
    return tools.list_contingent_orders()


# ---------------------------------------------------------------------------
# Feed Health
# ---------------------------------------------------------------------------

@mcp.tool()
def check_feed_health(stale_threshold: float = 30.0) -> dict:
    """Check staleness and health of all feeds. Reports which feeds are stale, age in seconds, and whether all feeds are down."""
    return tools.check_feed_health(stale_threshold)


# ---------------------------------------------------------------------------
# Discovery
# ---------------------------------------------------------------------------

@mcp.tool()
def discover_events(query: str = "", limit: int = 10, min_volume: float = 0.0) -> list[dict]:
    """Discover multi-outcome events on Polymarket. Returns events with all outcomes and prices."""
    return tools.discover_event(query, limit, min_volume)


@mcp.tool()
def get_market_detail(slug_or_id: str, exchange: str = "polymarket") -> dict:
    """Get comprehensive detail for a single market: metadata, prices, price history, orderbook, trades, holders."""
    return tools.market_detail(slug_or_id, exchange)


@mcp.tool()
def top_markets(exchange: str = "polymarket", limit: int = 10, category: str = "") -> list[dict]:
    """Get the highest-volume active markets. Exchange: 'polymarket' or 'kalshi'."""
    return tools.get_top_markets(exchange, limit, category)


# ---------------------------------------------------------------------------
# Wallet Analytics (Polymarket public - no auth needed)
# ---------------------------------------------------------------------------

@mcp.tool()
def get_wallet_trades(address: str, limit: int = 50, condition_id: str | None = None) -> list[dict]:
    """Fetch trade history for a Polymarket wallet address (0x...). No auth required."""
    return tools.wallet_trades(address, limit, condition_id)


@mcp.tool()
def get_market_trades(condition_id: str, limit: int = 50, side: str | None = None, min_size: float = 0.0) -> list[dict]:
    """Fetch recent trades for a Polymarket market by condition ID. Filter by side ('BUY'/'SELL') and min USDC size."""
    return tools.market_trades(condition_id, limit, side, min_size)


@mcp.tool()
def get_wallet_positions(address: str, limit: int = 50, sort_by: str = "CURRENT") -> list[dict]:
    """Fetch open positions for a Polymarket wallet. Sort: TOKENS, CURRENT, INITIAL, CASHPNL, PERCENTPNL, PRICE, AVGPRICE."""
    return tools.wallet_positions(address, limit, sort_by)


@mcp.tool()
def get_wallet_value(address: str) -> dict:
    """Get total USD value of all open positions in a Polymarket wallet."""
    return tools.wallet_value(address)


@mcp.tool()
def get_wallet_profile(address: str) -> dict:
    """Get public profile for a Polymarket wallet: pseudonym, name, bio, X handle."""
    return tools.wallet_profile(address)


@mcp.tool()
def get_top_holders(condition_id: str, limit: int = 20) -> list[dict]:
    """Get top token holders for a Polymarket market. Returns wallet, amount, and optional pseudonym."""
    return tools.market_top_holders(condition_id, limit)


@mcp.tool()
def analyze_market_flow(condition_id: str, trade_limit: int = 500, top_n: int = 10) -> dict:
    """Analyze trade flow for a market: buy/sell volume, net flow, unique wallets, top buyers/sellers."""
    return tools.market_flow(condition_id, trade_limit, top_n)


# ---------------------------------------------------------------------------
# Simulation
# ---------------------------------------------------------------------------

@mcp.tool()
def simulate_portfolio(scenarios: int = 10000, seed: int | None = None) -> dict:
    """Run Monte Carlo simulation on current engine positions. Returns VaR, CVaR, win probability, mean/median PnL."""
    return tools.simulate_portfolio(scenarios, seed)


# ---------------------------------------------------------------------------
# Arbitrage
# ---------------------------------------------------------------------------

@mcp.tool()
def execute_arbitrage(
    market_id: str,
    buy_exchange: str,
    sell_exchange: str,
    buy_price: float,
    sell_price: float,
    size: float,
) -> dict:
    """Execute atomic cross-exchange arbitrage. Buys on one exchange, sells on another. Auto-cancels buy if sell fails."""
    return tools.execute_arb(market_id, buy_exchange, sell_exchange, buy_price, sell_price, size)


# ---------------------------------------------------------------------------
# Quantitative Analytics
# ---------------------------------------------------------------------------

@mcp.tool()
def shannon_entropy(p: float) -> dict:
    """Compute binary Shannon entropy for a probability (0-1). Maximum at p=0.5."""
    return tools.compute_shannon_entropy(p)


@mcp.tool()
def kl_divergence(p: list[float], q: list[float]) -> dict:
    """Compute KL divergence between two probability distributions. Measures information loss."""
    return tools.compute_kl_divergence(p, q)


@mcp.tool()
def hurst_exponent(prices: list[float]) -> dict:
    """Compute Hurst exponent for a price series. >0.5 trending, <0.5 mean-reverting, ~0.5 random walk."""
    return tools.compute_hurst_exponent(prices)


@mcp.tool()
def variance_ratio_test(returns: list[float], period: int = 2) -> dict:
    """Variance ratio test for market efficiency. 1.0 = random walk."""
    return tools.compute_variance_ratio(returns, period)


@mcp.tool()
def cornish_fisher_var(returns: list[float], confidence: float = 0.95) -> dict:
    """Cornish-Fisher VaR and CVaR (skew/kurtosis-adjusted). More accurate than Gaussian VaR for prediction markets."""
    return tools.compute_cornish_fisher_var(returns, confidence)


@mcp.tool()
def prediction_greeks(
    price: float, size: float, is_yes: bool = True, t_hours: float = 24.0, vol: float = 0.2,
) -> dict:
    """Compute prediction Greeks (delta, gamma, theta, vega) for a binary market position."""
    return tools.compute_prediction_greeks(price, size, is_yes, t_hours, vol)


@mcp.tool()
def deflated_sharpe(
    sharpe: float, n_obs: int, n_trials: int, skew: float = 0.0, kurt: float = 3.0,
) -> dict:
    """Deflated Sharpe Ratio test: is the observed Sharpe statistically significant given multiple strategy trials?"""
    return tools.compute_deflated_sharpe(sharpe, n_obs, n_trials, skew, kurt)


@mcp.tool()
def signal_diagnostics(predictions: list[float], outcomes: list[float]) -> dict:
    """Signal quality diagnostics: information coefficient (Spearman), half-life, Hurst exponent."""
    return tools.run_signal_diagnostics(predictions, outcomes)


@mcp.tool()
def market_efficiency_test(prices: list[float]) -> dict:
    """Analyze market efficiency: variance ratio, Hurst exponent, and efficiency flag."""
    return tools.run_market_efficiency(prices)


@mcp.tool()
def stress_test(n_simulations: int = 10000, seed: int | None = None) -> dict:
    """Run stress test on current positions under adverse scenarios (correlation spike, all-resolve-no, liquidity shock, tail risk)."""
    return tools.run_stress_test(n_simulations, seed)


# ---------------------------------------------------------------------------
# Resources
# ---------------------------------------------------------------------------

@mcp.resource("horizon://status")
def resource_status() -> str:
    """Current engine status as JSON context."""
    import json
    return json.dumps(tools.engine_status(), indent=2)


@mcp.resource("horizon://positions")
def resource_positions() -> str:
    """Current positions as JSON context."""
    import json
    return json.dumps(tools.list_positions(), indent=2)


# ---------------------------------------------------------------------------
# Entry point
# ---------------------------------------------------------------------------

def main() -> None:
    """Run the MCP server on stdio transport."""
    mcp.run(transport="stdio")
