"""Multi-leg atomic execution pipeline functions."""

from __future__ import annotations

import logging
from typing import Any, Callable

from horizon._horizon import (
    FillPolicy,
    LegStatus,
    Leg,
    LegResult,
    MultiLegResult,
    validate_legs,
    aggregate_leg_risk,
    build_mock_result,
)

logger = logging.getLogger("horizon")


def multi_leg(
    legs_config: list[dict[str, Any]] | None = None,
    policy: str = "best_effort",
) -> Callable:
    """Execute multiple legs atomically.

    legs_config: list of {"market_id": str, "side": str, "size": float, "price": float, "exchange": str}
    policy: "all_or_none", "best_effort", "delta_neutral"
    """
    from horizon._horizon import auth_require_ultra
    auth_require_ultra()

    policy_map = {
        "all_or_none": FillPolicy.AllOrNone,
        "best_effort": FillPolicy.BestEffort,
        "delta_neutral": FillPolicy.DeltaNeutral,
    }
    fill_policy = policy_map.get(policy, FillPolicy.BestEffort)

    def _fn(ctx) -> dict[str, Any]:
        configs = legs_config or ctx.params.get("legs", [])
        if not configs:
            return {"success": False, "error": "no legs provided", "legs": []}

        legs = []
        for lc in configs:
            leg = Leg(
                market_id=lc.get("market_id", ""),
                side=lc.get("side", "buy"),
                size=float(lc.get("size", 10.0)),
                price=float(lc.get("price", 0.5)),
                exchange=lc.get("exchange", "paper"),
            )
            legs.append(leg)

        # Validate
        error = validate_legs(legs)
        if error:
            return {"success": False, "error": error, "legs": []}

        # Risk check
        total_notional, max_leg, n_exchanges = aggregate_leg_risk(legs)
        max_notional = ctx.params.get("max_notional", 10000.0)
        if total_notional > max_notional:
            return {
                "success": False,
                "error": f"total notional {total_notional:.2f} exceeds max {max_notional:.2f}",
                "legs": [],
            }

        # Submit via engine if available, otherwise mock
        engine = ctx.params.get("engine")
        if engine and hasattr(engine, "submit_order"):
            results = []
            for i, leg in enumerate(legs):
                try:
                    from horizon._horizon import OrderRequest, OrderSide, Side
                    side_enum = OrderSide.Buy if leg.side == "buy" else OrderSide.Sell
                    req = OrderRequest(
                        market_id=leg.market_id,
                        side=side_enum,
                        size=leg.size,
                        price=leg.price,
                    )
                    order = engine.submit_order(req, leg.exchange)
                    results.append({
                        "index": i,
                        "market": leg.market_id,
                        "status": "submitted",
                        "order_id": order.id if order else "",
                    })
                except Exception as e:
                    results.append({
                        "index": i,
                        "market": leg.market_id,
                        "status": "failed",
                        "error": str(e),
                    })

                    if fill_policy == FillPolicy.AllOrNone:
                        # Cancel all previously submitted
                        for prev in results[:-1]:
                            if prev.get("order_id"):
                                try:
                                    engine.cancel_order(prev["order_id"], leg.exchange)
                                except Exception:
                                    pass
                        return {"success": False, "error": str(e), "legs": results}

            return {
                "success": all(r.get("status") == "submitted" for r in results),
                "legs": results,
                "total_notional": total_notional,
                "policy": policy,
            }
        else:
            # Mock execution
            result = build_mock_result(legs, fill_policy, True)
            return {
                "success": result.success,
                "total_filled": result.total_filled,
                "total_legs": result.total_legs,
                "total_notional": result.total_notional,
                "policy": policy,
            }

    _fn.__name__ = "multi_leg"
    return _fn
