"""Shared tool functions for MCP server and OpenClaw skill.

Thin wrappers around the Horizon Engine API that return plain dicts/lists
suitable for JSON serialization. Both the MCP server and the OpenClaw CLI
delegate to these functions.
"""

from __future__ import annotations

import os
from typing import Any

from horizon._horizon import (
    Engine,
    EngineStatus,
    FeedSnapshot,
    Fill,
    Market,
    Order,
    OrderRequest,
    OrderSide,
    Position,
    RiskConfig,
    Side,
    ContingentOrder,
)
from horizon.discovery import discover_markets

_engine: Engine | None = None


def get_engine() -> Engine:
    """Lazy-init a singleton Engine from environment variables."""
    global _engine
    if _engine is not None:
        return _engine

    exchange = os.environ.get("HORIZON_EXCHANGE", "paper")
    db_path = os.environ.get("HORIZON_DB") or None

    kwargs: dict[str, Any] = {
        "exchange_type": exchange,
        "db_path": db_path,
        "horizon_key": os.environ.get("HORIZON_API_KEY"),
    }

    if exchange == "polymarket":
        kwargs["api_key"] = os.environ.get("POLYMARKET_API_KEY")
        kwargs["api_secret"] = os.environ.get("POLYMARKET_API_SECRET")
        kwargs["api_passphrase"] = os.environ.get("POLYMARKET_API_PASSPHRASE")
        kwargs["private_key"] = os.environ.get("POLYMARKET_PRIVATE_KEY")
        kwargs["clob_url"] = os.environ.get("POLYMARKET_CLOB_URL")
    elif exchange == "kalshi":
        kwargs["email"] = os.environ.get("KALSHI_EMAIL")
        kwargs["password"] = os.environ.get("KALSHI_PASSWORD")
        kwargs["api_key"] = os.environ.get("KALSHI_API_KEY")
        kwargs["api_url"] = os.environ.get("KALSHI_API_URL")

    _engine = Engine(**kwargs)
    return _engine


def reset_engine() -> None:
    """Reset the singleton engine (for testing)."""
    global _engine
    _engine = None


# ---------------------------------------------------------------------------
# Serialization helpers
# ---------------------------------------------------------------------------

def _status_to_dict(s: EngineStatus) -> dict:
    return {
        "running": s.running,
        "kill_switch_active": s.kill_switch_active,
        "kill_switch_reason": s.kill_switch_reason,
        "open_orders": s.open_orders,
        "active_positions": s.active_positions,
        "total_realized_pnl": round(s.total_realized_pnl, 6),
        "total_unrealized_pnl": round(s.total_unrealized_pnl, 6),
        "daily_pnl": round(s.daily_pnl, 6),
        "uptime_secs": s.uptime_secs,
    }


def _position_to_dict(p: Position) -> dict:
    return {
        "market_id": p.market_id,
        "side": "yes" if p.side == Side.Yes else "no",
        "size": round(p.size, 6),
        "avg_entry_price": round(p.avg_entry_price, 6),
        "realized_pnl": round(p.realized_pnl, 6),
        "unrealized_pnl": round(p.unrealized_pnl, 6),
        "exchange": p.exchange,
    }


def _order_to_dict(o: Order) -> dict:
    return {
        "id": o.id,
        "market_id": o.market_id,
        "side": "yes" if o.side == Side.Yes else "no",
        "order_side": "buy" if o.order_side == OrderSide.Buy else "sell",
        "price": round(o.price, 6),
        "size": round(o.size, 6),
        "filled_size": round(o.filled_size, 6),
        "remaining_size": round(o.remaining_size, 6),
        "status": str(o.status),
        "exchange": o.exchange,
        "created_at": o.created_at,
    }


def _fill_to_dict(f: Fill) -> dict:
    return {
        "fill_id": f.fill_id,
        "order_id": f.order_id,
        "market_id": f.market_id,
        "side": "yes" if f.side == Side.Yes else "no",
        "order_side": "buy" if f.order_side == OrderSide.Buy else "sell",
        "price": round(f.price, 6),
        "size": round(f.size, 6),
        "fee": round(f.fee, 6),
        "timestamp": f.timestamp,
        "exchange": f.exchange,
    }


def _feed_to_dict(name: str, s: FeedSnapshot) -> dict:
    return {
        "name": name,
        "price": round(s.price, 6),
        "bid": round(s.bid, 6),
        "ask": round(s.ask, 6),
        "volume_24h": round(s.volume_24h, 2),
        "source": s.source,
        "timestamp": s.timestamp,
    }


def _market_to_dict(m: Market) -> dict:
    return {
        "id": m.id,
        "name": m.name,
        "slug": m.slug,
        "exchange": m.exchange,
        "expiry": m.expiry,
        "active": m.active,
    }


def _contingent_to_dict(c: ContingentOrder) -> dict:
    return {
        "id": c.id,
        "trigger_type": str(c.trigger_type),
        "market_id": c.market_id,
        "side": "yes" if c.side == Side.Yes else "no",
        "order_side": "buy" if c.order_side == OrderSide.Buy else "sell",
        "size": round(c.size, 6),
        "trigger_price": round(c.trigger_price, 6),
        "trigger_pnl": c.trigger_pnl,
        "exchange": c.exchange,
        "triggered": c.triggered,
        "child_order_id": c.child_order_id,
    }


# ---------------------------------------------------------------------------
# Tool functions — each returns dict | list[dict] | str
# ---------------------------------------------------------------------------

def engine_status() -> dict:
    """Get trading engine status."""
    try:
        return _status_to_dict(get_engine().status())
    except Exception as e:
        return {"error": str(e)}


def list_positions() -> list[dict]:
    """List all open positions."""
    try:
        return [_position_to_dict(p) for p in get_engine().positions()]
    except Exception as e:
        return [{"error": str(e)}]


def list_open_orders(market_id: str | None = None) -> list[dict]:
    """List open orders, optionally filtered by market."""
    try:
        eng = get_engine()
        if market_id:
            orders = eng.open_orders_for_market(market_id)
        else:
            orders = eng.open_orders()
        return [_order_to_dict(o) for o in orders]
    except Exception as e:
        return [{"error": str(e)}]


def list_recent_fills(limit: int = 20) -> list[dict]:
    """List recent fills."""
    try:
        fills = get_engine().recent_fills()
        return [_fill_to_dict(f) for f in fills[-limit:]]
    except Exception as e:
        return [{"error": str(e)}]


def submit_order(market_id: str, side: str, price: float, size: float) -> dict:
    """Submit a limit order. side: 'buy' or 'sell'."""
    try:
        order_side = OrderSide.Buy if side.lower() == "buy" else OrderSide.Sell
        req = OrderRequest(
            market_id=market_id,
            side=Side.Yes,
            order_side=order_side,
            size=size,
            price=price,
        )
        order_id = get_engine().submit_order(req)
        return {"order_id": order_id, "market_id": market_id, "side": side, "price": price, "size": size}
    except Exception as e:
        return {"error": str(e)}


def cancel_order(order_id: str) -> dict:
    """Cancel a single order by ID."""
    try:
        get_engine().cancel(order_id)
        return {"canceled": order_id}
    except Exception as e:
        return {"error": str(e)}


def cancel_all_orders() -> dict:
    """Cancel all open orders."""
    try:
        count = get_engine().cancel_all()
        return {"canceled_count": count}
    except Exception as e:
        return {"error": str(e)}


def cancel_market_orders(market_id: str) -> dict:
    """Cancel all orders for a specific market."""
    try:
        count = get_engine().cancel_market(market_id)
        return {"market_id": market_id, "canceled_count": count}
    except Exception as e:
        return {"error": str(e)}


def activate_kill_switch(reason: str) -> dict:
    """Activate the kill switch — emergency stop. Cancels all orders."""
    try:
        get_engine().activate_kill_switch(reason)
        return {"kill_switch": "activated", "reason": reason}
    except Exception as e:
        return {"error": str(e)}


def deactivate_kill_switch() -> dict:
    """Deactivate the kill switch — resume trading."""
    try:
        get_engine().deactivate_kill_switch()
        return {"kill_switch": "deactivated"}
    except Exception as e:
        return {"error": str(e)}


def get_feed_snapshot(name: str) -> dict:
    """Get price/bid/ask snapshot for a named feed."""
    try:
        snap = get_engine().feed_snapshot(name)
        if snap is None:
            return {"error": f"feed not found: {name}"}
        return _feed_to_dict(name, snap)
    except Exception as e:
        return {"error": str(e)}


def list_all_feeds() -> list[dict]:
    """List all feed snapshots."""
    try:
        snapshots = get_engine().all_feed_snapshots()
        return [_feed_to_dict(name, snap) for name, snap in snapshots.items()]
    except Exception as e:
        return [{"error": str(e)}]


def discover(exchange: str = "polymarket", query: str = "", limit: int = 10) -> list[dict]:
    """Search for markets on an exchange."""
    try:
        markets = discover_markets(exchange=exchange, query=query, limit=limit)
        return [_market_to_dict(m) for m in markets]
    except Exception as e:
        return [{"error": str(e)}]


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."""
    try:
        from horizon._horizon import kelly_size, edge as kelly_edge

        size = kelly_size(prob, price, bankroll, fraction, max_size)
        e = kelly_edge(prob, price)
        return {
            "optimal_size": round(size, 4),
            "edge": round(e, 4),
            "prob": prob,
            "price": price,
            "bankroll": bankroll,
            "fraction": fraction,
        }
    except Exception as e:
        return {"error": str(e)}


def check_parity(market_id: str) -> dict:
    """Check YES/NO price parity for a market."""
    try:
        result = get_engine().parity_check(market_id)
        if result is None:
            return {"error": f"no parity data for market: {market_id}"}
        return {
            "market_id": result.market_id,
            "yes_price": round(result.yes_price, 6),
            "no_price": round(result.no_price, 6),
            "deviation": round(result.deviation, 6),
            "is_violation": result.is_violation,
            "exchange": result.exchange,
        }
    except Exception as e:
        return {"error": str(e)}


def add_stop_loss(
    market_id: str,
    side: str,
    order_side: str,
    size: float,
    trigger: float,
) -> dict:
    """Add a stop-loss contingent order."""
    try:
        s = Side.Yes if side.lower() == "yes" else Side.No
        os_ = OrderSide.Buy if order_side.lower() == "buy" else OrderSide.Sell
        cid = get_engine().add_stop_loss(market_id, s, os_, size, trigger)
        return {"contingent_id": cid, "type": "stop_loss", "market_id": market_id}
    except Exception as e:
        return {"error": str(e)}


def add_take_profit(
    market_id: str,
    side: str,
    order_side: str,
    size: float,
    trigger: float,
) -> dict:
    """Add a take-profit contingent order."""
    try:
        s = Side.Yes if side.lower() == "yes" else Side.No
        os_ = OrderSide.Buy if order_side.lower() == "buy" else OrderSide.Sell
        cid = get_engine().add_take_profit(market_id, s, os_, size, trigger)
        return {"contingent_id": cid, "type": "take_profit", "market_id": market_id}
    except Exception as e:
        return {"error": str(e)}


def list_contingent_orders() -> list[dict]:
    """List pending stop-loss and take-profit orders."""
    try:
        return [_contingent_to_dict(c) for c in get_engine().pending_contingent_orders()]
    except Exception as e:
        return [{"error": str(e)}]
