"""
TradeStation Order Execution MCP Tools.

Provides tools for placing, confirming, replacing, and cancelling orders,
as well as querying available routes and activation triggers.

Safety note: The place_order tool description instructs the LLM to ALWAYS
call confirm_order first to preview cost/commission before placing a live trade.
"""

import json
import logging
from typing import Any

logger = logging.getLogger(__name__)


def register_order_execution_tools(mcp, ts_client):
    """Register all Order Execution tools on the given FastMCP server instance.

    Args:
        mcp: FastMCP server instance.
        ts_client: TradeStationClient instance for API calls.
    """

    # ── Helper: Build order body ───────────────────────────────────

    def _build_order_body(
        account_id: str,
        symbol: str,
        quantity: str,
        order_type: str,
        trade_action: str,
        duration: str = "DAY",
        limit_price: str | None = None,
        stop_price: str | None = None,
        route: str = "Intelligent",
        gtd_date: str | None = None,
        order_confirm_id: str | None = None,
        trailing_stop_amount: str | None = None,
        trailing_stop_percent: str | None = None,
        legs: str | None = None,
        oso_orders: str | None = None,
    ) -> dict[str, Any]:
        """Build the JSON body for an order request from individual parameters."""
        time_in_force: dict[str, Any] = {"Duration": duration}
        if gtd_date and duration == "GTD":
            time_in_force["Expiration"] = gtd_date

        body: dict[str, Any] = {
            "AccountID": account_id,
            "Symbol": symbol,
            "Quantity": quantity,
            "OrderType": order_type,
            "TradeAction": trade_action,
            "TimeInForce": time_in_force,
            "Route": route,
        }

        if limit_price:
            body["LimitPrice"] = limit_price
        if stop_price:
            body["StopPrice"] = stop_price
        if order_confirm_id:
            body["OrderConfirmID"] = order_confirm_id

        # Advanced options
        if trailing_stop_amount or trailing_stop_percent:
            advanced = {}
            if trailing_stop_amount:
                advanced["TrailingStop"] = {"Amount": trailing_stop_amount}
            if trailing_stop_percent:
                advanced["TrailingStop"] = {"Percent": trailing_stop_percent}
            body["AdvancedOptions"] = advanced

        # Multi-leg orders (options)
        if legs:
            parsed_legs = json.loads(legs) if isinstance(legs, str) else legs
            body["Legs"] = parsed_legs
            # Remove top-level Symbol for multi-leg
            if len(parsed_legs) > 0:
                body.pop("Symbol", None)

        # OSO (Order Sends Order)
        if oso_orders:
            parsed_oso = json.loads(oso_orders) if isinstance(oso_orders, str) else oso_orders
            body["OSOs"] = parsed_oso

        return body

    # ── Confirm Order ──────────────────────────────────────────────

    @mcp.tool()
    async def confirm_order(
        account_id: str,
        symbol: str,
        quantity: str,
        order_type: str,
        trade_action: str,
        duration: str = "DAY",
        limit_price: str | None = None,
        stop_price: str | None = None,
        route: str = "Intelligent",
        gtd_date: str | None = None,
        legs: str | None = None,
    ) -> str:
        """ORDER EXECUTION: Preview/confirm an order WITHOUT placing it — shows estimated cost and commission.

        ALWAYS call this BEFORE place_order so the user can review the estimated
        cost, commission, and order details before committing to a real trade.
        This does NOT execute the order — it only returns what WOULD happen.

        Example questions this tool answers:
        - "How much would it cost to buy 100 shares of MSFT?"
        - "Preview a limit order for AAPL at $150"
        - "What's the estimated commission for selling 50 GOOG?"
        - "Confirm my order before placing it"

        DO NOT use this for checking stock prices (use get_quote_snapshots)
        or account balances (use get_balances).

        Args:
            account_id: TradeStation Account ID (get from get_accounts).
            symbol: Ticker symbol to trade (e.g. "MSFT"). Not needed for multi-leg options with legs param.
            quantity: Number of shares or contracts as string (e.g. "10", "100").
            order_type: Order type — "Market", "Limit", "StopMarket", or "StopLimit".
            trade_action: The intent of the trade:
                Equities/Futures: "BUY", "SELL", "BUYTOCOVER", "SELLSHORT"
                Options: "BUYTOOPEN", "BUYTOCLOSE", "SELLTOOPEN", "SELLTOCLOSE"
            duration: How long the order stays active:
                "DAY" — expires end of day (default)
                "GTC" — good until cancelled
                "GTD" — good until specific date (set gtd_date)
                "IOC" — immediate or cancel
                "FOK" — fill or kill
                "OPG" — at market open
                "CLO" — at market close
            limit_price: Required for "Limit" and "StopLimit" orders (e.g. "150.00").
            stop_price: Required for "StopMarket" and "StopLimit" orders (e.g. "145.00").
            route: Order routing — "Intelligent" (default). Use get_routes for other options.
            gtd_date: Expiration date for GTD orders (e.g. "2025-12-31").
            legs: For multi-leg option orders — JSON string array of legs:
                  [{"Symbol": "AAPL 250321C150", "Quantity": "1", "TradeAction": "BUYTOOPEN"}]

        Returns:
            JSON with Confirmations array containing EstimatedCost, EstimatedCommission,
            EstimatedPrice, Route, Duration, SummaryMessage, and OrderConfirmId.
        """
        body = _build_order_body(
            account_id=account_id,
            symbol=symbol,
            quantity=quantity,
            order_type=order_type,
            trade_action=trade_action,
            duration=duration,
            limit_price=limit_price,
            stop_price=stop_price,
            route=route,
            gtd_date=gtd_date,
            legs=legs,
        )
        data = await ts_client.post("/v3/orderexecution/orderconfirm", json_body=body)
        return json.dumps(data, indent=2)

    # ── Place Order ────────────────────────────────────────────────

    @mcp.tool()
    async def place_order(
        account_id: str,
        symbol: str,
        quantity: str,
        order_type: str,
        trade_action: str,
        duration: str = "DAY",
        limit_price: str | None = None,
        stop_price: str | None = None,
        route: str = "Intelligent",
        gtd_date: str | None = None,
        order_confirm_id: str | None = None,
        trailing_stop_amount: str | None = None,
        trailing_stop_percent: str | None = None,
        legs: str | None = None,
        oso_orders: str | None = None,
    ) -> str:
        """ORDER EXECUTION: Place a LIVE trade order — BUY or SELL stocks, options, or futures.

        ⚠️ WARNING: This places a REAL order that WILL EXECUTE in the market.
        ALWAYS call confirm_order first to preview cost/commission before calling this.

        Example questions this tool answers:
        - "Buy 100 shares of MSFT"
        - "Sell my AAPL position"
        - "Place a limit order for GOOG at $150"
        - "Short sell 50 shares of TSLA"
        - "Buy to open 5 AAPL call options"

        Args:
            account_id: TradeStation Account ID.
            symbol: Ticker symbol to trade. Not needed for multi-leg options with legs param.
            quantity: Number of shares/contracts as string (e.g. "10").
            order_type: "Market", "Limit", "StopMarket", or "StopLimit".
            trade_action: Trade intent:
                Equities/Futures: "BUY", "SELL", "BUYTOCOVER", "SELLSHORT"
                Options: "BUYTOOPEN", "BUYTOCLOSE", "SELLTOOPEN", "SELLTOCLOSE"
            duration: "DAY" (default), "GTC", "GTD", "IOC", "FOK", "OPG", "CLO".
            limit_price: Required for Limit/StopLimit orders.
            stop_price: Required for StopMarket/StopLimit orders.
            route: Default "Intelligent".
            gtd_date: For GTD orders.
            order_confirm_id: Unique ID to prevent duplicate orders (1-22 chars).
            trailing_stop_amount: Dollar amount for trailing stop.
            trailing_stop_percent: Percentage for trailing stop (e.g. "5" for 5%).
            legs: For multi-leg option orders — JSON string array.
            oso_orders: OSO (Order Sends Order) child orders — JSON string array.

        Returns:
            JSON with Orders array (each with OrderID, Message) and any Errors.
        """
        body = _build_order_body(
            account_id=account_id,
            symbol=symbol,
            quantity=quantity,
            order_type=order_type,
            trade_action=trade_action,
            duration=duration,
            limit_price=limit_price,
            stop_price=stop_price,
            route=route,
            gtd_date=gtd_date,
            order_confirm_id=order_confirm_id,
            trailing_stop_amount=trailing_stop_amount,
            trailing_stop_percent=trailing_stop_percent,
            legs=legs,
            oso_orders=oso_orders,
        )
        data = await ts_client.post("/v3/orderexecution/orders", json_body=body)
        return json.dumps(data, indent=2)

    # ── Confirm Group Order ────────────────────────────────────────

    @mcp.tool()
    async def confirm_group_order(
        group_type: str,
        orders: str,
    ) -> str:
        """ORDER EXECUTION: Preview a group order (OCO or Bracket) WITHOUT placing it.

        Use this to preview OCO (one-cancels-other) or Bracket orders before committing.
        Always call this before place_group_order.

        Group types:
        - "OCO": One Cancels Other — if one leg fills, the others are cancelled.
        - "BRK": Bracket — simultaneous stop-loss and take-profit around a position.
        - "NORMAL": Grouped orders without special cancel logic.

        Args:
            group_type: "OCO", "BRK", or "NORMAL".
            orders: JSON string array of order objects. Each order has the same fields as place_order:
                    [{"AccountID":"123","Symbol":"MSFT","Quantity":"10","OrderType":"Limit",
                      "TradeAction":"BUY","LimitPrice":"150","TimeInForce":{"Duration":"DAY"}}]

        Returns:
            JSON with Confirmations for each order in the group.
        """
        parsed_orders = json.loads(orders) if isinstance(orders, str) else orders
        body = {"Type": group_type, "Orders": parsed_orders}
        data = await ts_client.post("/v3/orderexecution/ordergroupconfirm", json_body=body)
        return json.dumps(data, indent=2)

    # ── Place Group Order ──────────────────────────────────────────

    @mcp.tool()
    async def place_group_order(
        group_type: str,
        orders: str,
    ) -> str:
        """ORDER EXECUTION: Place a group order — OCO (one-cancels-other) or Bracket orders.

        ⚠️ WARNING: This places REAL orders. Call confirm_group_order first to preview.

        Use this for advanced order strategies:
        - Bracket orders: set stop-loss + take-profit around a position
        - OCO orders: breakout entries — buy above resistance OR sell below support
        - Normal groups: submit multiple related orders together

        Args:
            group_type: "OCO", "BRK" (bracket), or "NORMAL".
            orders: JSON string array of order objects (same format as confirm_group_order).

        Returns:
            JSON with Orders array (each with OrderID) and any Errors.
        """
        parsed_orders = json.loads(orders) if isinstance(orders, str) else orders
        body = {"Type": group_type, "Orders": parsed_orders}
        data = await ts_client.post("/v3/orderexecution/ordergroups", json_body=body)
        return json.dumps(data, indent=2)

    # ── Replace Order ──────────────────────────────────────────────

    @mcp.tool()
    async def replace_order(
        order_id: str,
        quantity: str | None = None,
        limit_price: str | None = None,
        stop_price: str | None = None,
        order_type: str | None = None,
        trailing_stop_amount: str | None = None,
        trailing_stop_percent: str | None = None,
    ) -> str:
        """ORDER EXECUTION: Modify/replace an existing open order — change price, quantity, or type.

        Use this when the user wants to update, modify, or change an existing
        pending order. Cannot modify already-filled orders.

        Example questions:
        - "Change my limit price to $150"
        - "Update my order quantity to 200 shares"
        - "Modify order 123456789 to a market order"

        Args:
            order_id: The OrderID to modify (no dashes — e.g. "123456789", NOT "1-2345-6789").
            quantity: New quantity (optional).
            limit_price: New limit price (optional).
            stop_price: New stop price (optional).
            order_type: Can only be changed to "Market" (optional).
            trailing_stop_amount: New trailing stop dollar amount (optional).
            trailing_stop_percent: New trailing stop percentage (optional).

        Returns:
            JSON with confirmation Message and OrderID.
        """
        body: dict[str, Any] = {}
        if quantity:
            body["Quantity"] = quantity
        if limit_price:
            body["LimitPrice"] = limit_price
        if stop_price:
            body["StopPrice"] = stop_price
        if order_type:
            body["OrderType"] = order_type
        if trailing_stop_amount or trailing_stop_percent:
            advanced: dict[str, Any] = {}
            if trailing_stop_amount:
                advanced["TrailingStop"] = {"Amount": trailing_stop_amount}
            if trailing_stop_percent:
                advanced["TrailingStop"] = {"Percent": trailing_stop_percent}
            body["AdvancedOptions"] = advanced
        data = await ts_client.put(f"/v3/orderexecution/orders/{order_id}", json_body=body)
        return json.dumps(data, indent=2)

    # ── Cancel Order ───────────────────────────────────────────────

    @mcp.tool()
    async def cancel_order(order_id: str) -> str:
        """ORDER EXECUTION: Cancel an open/pending order.

        Use this when the user wants to cancel, remove, or withdraw a pending order.
        Sends a cancellation request to the exchange.

        Example questions:
        - "Cancel my order"
        - "Cancel order 123456789"
        - "Remove my pending buy order"
        - "Cancel all open orders" (call once per order)

        Args:
            order_id: The OrderID to cancel (no dashes — e.g. "123456789").

        Returns:
            JSON with cancellation confirmation Message and OrderID.
        """
        data = await ts_client.delete(f"/v3/orderexecution/orders/{order_id}")
        return json.dumps(data, indent=2)

    # ── Get Activation Triggers ────────────────────────────────────

    @mcp.tool()
    async def get_activation_triggers() -> str:
        """ORDER EXECUTION: Get available conditional order activation trigger types.

        Use this when setting up conditional/triggered orders that activate based
        on market conditions (e.g. trigger a buy when a symbol crosses a price level).

        Returns:
            JSON with ActivationTriggers array, each with TriggerKey, description,
            and supported asset types.
        """
        data = await ts_client.get("/v3/orderexecution/activationtriggers")
        return json.dumps(data, indent=2)

    # ── Get Routes ─────────────────────────────────────────────────

    @mcp.tool()
    async def get_routes() -> str:
        """ORDER EXECUTION: Get available order routing destinations/exchanges.

        Use this when the user asks about order routing options, which exchanges
        or ECNs are available, or wants to specify a route for an order.

        Example questions:
        - "What order routes are available?"
        - "Which exchanges can I route orders to?"

        Returns:
            JSON with Routes array, each with Id, Name, and supported AssetTypes.
        """
        data = await ts_client.get("/v3/orderexecution/routes")
        return json.dumps(data, indent=2)
