"""Order tools (trade scope required)."""

from __future__ import annotations

from typing import Any

from mcp.server.fastmcp import Context
from mcp.types import ToolAnnotations

from liquidtrading.errors import LiquidError
from liquid_mcp.audit import log_trade
from liquid_mcp.client_manager import get_client
from liquid_mcp.config import get_config
from liquid_mcp.error_handling import handle_sdk_error
from liquid_mcp.guardrails import check_max_order, get_loss_tracker
from liquid_mcp.serializers import to_dict
from liquid_mcp.server import mcp


@mcp.tool(annotations=ToolAnnotations(destructiveHint=True))
def place_order(
    symbol: str,
    side: str,
    size: float,
    type: str = "market",
    leverage: int = 1,
    price: float | None = None,
    tp: float | None = None,
    sl: float | None = None,
    reduce_only: bool = False,
    time_in_force: str = "gtc",
    dry_run: bool = False,
    ctx: Context = None,
) -> dict[str, Any]:
    """Place an order on Liquid.

    IMPORTANT: 'size' is in USD notional, NOT token count.
    size=100 means $100 worth, not 100 tokens.

    Note: dry_run=True still logs to the audit trail for record-keeping,
    but no order is sent to the exchange.

    Args:
        symbol: Trading pair, e.g. "BTC-PERP". Call get_markets to list valid symbols.
        side: "buy" or "sell"
        size: Order size in USD notional (NOT tokens)
        type: "market" or "limit" (default "market")
        leverage: Leverage 1-200 (default 1). High leverage (>20x) significantly increases liquidation risk.
        price: Limit price (required for limit orders)
        tp: Take-profit trigger price
        sl: Stop-loss trigger price
        reduce_only: Only reduce existing position
        time_in_force: "gtc" or "ioc" (default "gtc")
        dry_run: Preview without executing (default False)
    """
    cfg = get_config()
    params = {
        "symbol": symbol, "side": side, "size": size, "type": type,
        "leverage": leverage, "price": price, "tp": tp, "sl": sl,
        "reduce_only": reduce_only, "time_in_force": time_in_force,
    }

    issues = _preflight_checks(size, cfg)
    if issues:
        log_trade(cfg.audit_path, "place_order", params, error=issues[0], dry_run=dry_run)
        raise ValueError(issues[0])

    if dry_run:
        result = {"dry_run": True, "would_execute": params, "status": "ok"}
        log_trade(cfg.audit_path, "place_order", params, result=result, dry_run=True)
        return {**result, "next_steps": ["place_order", "validate_trade"]}

    warnings = _build_warnings(size, tp, sl, leverage, cfg)

    try:
        client = get_client(cfg)
        order = client.place_order(
            symbol=symbol, side=side, type=type, size=size, leverage=leverage,
            price=price, tp=tp, sl=sl, reduce_only=reduce_only,
            time_in_force=time_in_force,
        )
    except LiquidError as e:
        log_trade(cfg.audit_path, "place_order", params, error=str(e))
        return handle_sdk_error("place_order", e)

    result = to_dict(order)
    if warnings:
        result["warnings"] = warnings
    log_trade(cfg.audit_path, "place_order", params, result=result)
    return {**result, "next_steps": ["get_positions", "set_tp_sl"]}


@mcp.tool(annotations=ToolAnnotations(destructiveHint=True))
def place_bracket_order(
    symbol: str,
    side: str,
    size: float,
    tp: float,
    sl: float,
    leverage: int = 1,
    price: float | None = None,
    type: str = "market",
    time_in_force: str = "gtc",
    dry_run: bool = False,
    ctx: Context = None,
) -> dict[str, Any]:
    """Place an order with mandatory TP and SL (atomic bracket order).

    Use this instead of place_order when you want guaranteed risk management.
    Equivalent to place_order + set_tp_sl in one atomic call.

    IMPORTANT: 'size' is in USD notional, NOT token count.

    Note: dry_run=True still logs to the audit trail for record-keeping,
    but no order is sent to the exchange.

    Args:
        symbol: Trading pair, e.g. "BTC-PERP". Call get_markets to list valid symbols.
        side: "buy" or "sell"
        size: Order size in USD notional
        tp: Take-profit trigger price (required)
        sl: Stop-loss trigger price (required)
        leverage: Leverage 1-200 (default 1). High leverage (>20x) significantly increases liquidation risk.
        price: Limit price (required for limit orders)
        type: "market" or "limit" (default "market")
        time_in_force: "gtc" or "ioc" (default "gtc")
        dry_run: Preview without executing (default False)
    """
    cfg = get_config()
    params = {
        "symbol": symbol, "side": side, "size": size, "type": type,
        "leverage": leverage, "price": price, "tp": tp, "sl": sl,
    }

    issues = _preflight_checks(size, cfg)
    if issues:
        log_trade(cfg.audit_path, "place_bracket_order", params, error=issues[0], dry_run=dry_run)
        raise ValueError(issues[0])

    if dry_run:
        result = {"dry_run": True, "would_execute": params, "status": "ok"}
        log_trade(cfg.audit_path, "place_bracket_order", params, result=result, dry_run=True)
        return {**result, "next_steps": ["place_bracket_order", "validate_trade"]}

    try:
        client = get_client(cfg)
        order = client.place_order(
            symbol=symbol, side=side, type=type, size=size, leverage=leverage,
            price=price, tp=tp, sl=sl, time_in_force=time_in_force,
        )
    except LiquidError as e:
        log_trade(cfg.audit_path, "place_bracket_order", params, error=str(e))
        return handle_sdk_error("place_bracket_order", e)

    result = to_dict(order)
    log_trade(cfg.audit_path, "place_bracket_order", params, result=result)
    return {**result, "next_steps": ["get_positions", "get_order"]}


@mcp.tool(annotations=ToolAnnotations(readOnlyHint=True))
def get_open_orders(ctx: Context) -> dict[str, Any]:
    """List all open orders."""
    try:
        client = get_client(get_config())
        orders = client.get_open_orders()
    except LiquidError as e:
        return handle_sdk_error("get_open_orders", e)

    return {
        "orders": to_dict(orders),
        "next_steps": ["cancel_order", "cancel_all_orders", "get_order"],
    }


@mcp.tool(annotations=ToolAnnotations(readOnlyHint=True))
def get_order(order_id: str, ctx: Context) -> dict[str, Any]:
    """Get details of a specific order by ID.

    Args:
        order_id: The order ID to look up
    """
    try:
        client = get_client(get_config())
        order = client.get_order(order_id)
    except LiquidError as e:
        return handle_sdk_error("get_order", e)

    return {
        **to_dict(order),
        "next_steps": ["cancel_order", "get_open_orders"],
    }


@mcp.tool(annotations=ToolAnnotations(destructiveHint=True))
def cancel_order(
    order_id: str, dry_run: bool = False, ctx: Context = None,
) -> dict[str, Any]:
    """Cancel an open order by ID.

    Args:
        order_id: The order ID to cancel
        dry_run: Preview without executing (default False)
    """
    cfg = get_config()
    params = {"order_id": order_id}

    if dry_run:
        result = {"dry_run": True, "would_execute": params, "status": "ok"}
        log_trade(cfg.audit_path, "cancel_order", params, result=result, dry_run=True)
        return {**result, "next_steps": ["get_open_orders"]}

    try:
        client = get_client(cfg)
        client.cancel_order(order_id)
    except LiquidError as e:
        log_trade(cfg.audit_path, "cancel_order", params, error=str(e))
        return handle_sdk_error("cancel_order", e)

    result = {"order_id": order_id, "status": "cancelled"}
    log_trade(cfg.audit_path, "cancel_order", params, result=result)
    return {**result, "next_steps": ["get_open_orders", "get_positions"]}


@mcp.tool(annotations=ToolAnnotations(destructiveHint=True))
def cancel_all_orders(dry_run: bool = False, ctx: Context = None) -> dict[str, Any]:
    """Cancel all open orders.

    Args:
        dry_run: Preview without executing (default False)
    """
    cfg = get_config()
    params: dict[str, Any] = {}

    if dry_run:
        result = {"dry_run": True, "would_execute": params, "status": "ok"}
        log_trade(cfg.audit_path, "cancel_all_orders", params, result=result, dry_run=True)
        return {**result, "next_steps": ["get_open_orders"]}

    try:
        client = get_client(cfg)
        count = client.cancel_all_orders()
    except LiquidError as e:
        log_trade(cfg.audit_path, "cancel_all_orders", params, error=str(e))
        return handle_sdk_error("cancel_all_orders", e)

    result = {"cancelled_count": count, "status": "ok"}
    log_trade(cfg.audit_path, "cancel_all_orders", params, result=result)
    return {**result, "next_steps": ["get_open_orders", "get_positions"]}


def _preflight_checks(size: float, cfg) -> list[str]:
    """Run guardrail checks, return list of issues (empty = ok)."""
    issues: list[str] = []
    max_err = check_max_order(size, cfg.max_order_usd)
    if max_err:
        issues.append(max_err)
    loss_err = get_loss_tracker(cfg.daily_loss_limit).check()
    if loss_err:
        issues.append(loss_err)
    return issues


def _build_warnings(
    size: float,
    tp: float | None,
    sl: float | None,
    leverage: int,
    cfg,
) -> list[str]:
    """Build advisory warnings for the agent (non-blocking)."""
    warnings: list[str] = []
    if size > cfg.max_order_usd * 0.5:
        warnings.append(
            "Large order (>50% of max). Consider calling validate_trade first."
        )
    if tp is None and sl is None:
        warnings.append(
            "No TP/SL set. Consider using place_bracket_order or calling set_tp_sl after."
        )
    if leverage > 20:
        warnings.append(
            f"High leverage ({leverage}x). Liquidation risk is significantly elevated."
        )
    return warnings
