"""Helper tools: calculators and pre-flight validation."""

from __future__ import annotations

from typing import Any

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

from liquidtrading.errors import LiquidError, SymbolNotFoundError
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, min_order_info
from liquid_mcp.serializers import to_dict
from liquid_mcp.server import mcp


@mcp.tool(
    annotations=ToolAnnotations(readOnlyHint=True, idempotentHint=True),
)
def calculate_token_amount(symbol: str, usd_amount: float, ctx: Context = None) -> dict[str, Any]:
    """Convert a USD amount to token count at the current market price.

    Args:
        symbol: Trading pair, e.g. "BTC-PERP". Call get_markets to list valid symbols.
        usd_amount: Dollar amount to convert
    """
    try:
        client = get_client(get_config())
        ticker = client.get_ticker(symbol)
    except LiquidError as e:
        return handle_sdk_error("calculate_token_amount", e)

    if ticker.mark_price <= 0:
        return {
            "error": True,
            "message": f"Invalid mark price ({ticker.mark_price}) for {symbol}. Market may be halted.",
            "next_steps": ["get_markets", "get_ticker"],
        }

    token_amount = usd_amount / ticker.mark_price
    return {
        "symbol": symbol,
        "usd_amount": usd_amount,
        "mark_price": ticker.mark_price,
        "token_amount": token_amount,
        "min_order": min_order_info(ticker.mark_price),
        "next_steps": ["place_order", "place_bracket_order"],
    }


@mcp.tool(
    annotations=ToolAnnotations(readOnlyHint=True, idempotentHint=True),
)
def calculate_position_size(
    symbol: str, percent_of_equity: float, ctx: Context = None,
) -> dict[str, Any]:
    """Calculate USD position size as a percentage of account equity.

    Example: "buy 5% of my account" -> returns the USD size.

    Args:
        symbol: Trading pair, e.g. "BTC-PERP". Call get_markets to list valid symbols.
        percent_of_equity: Percentage of equity to use (e.g. 5.0 = 5%). Must be > 0.
    """
    if percent_of_equity <= 0:
        return {
            "error": True,
            "message": "percent_of_equity must be > 0",
            "next_steps": ["calculate_position_size"],
        }

    try:
        client = get_client(get_config())
        account = client.get_account()
        ticker = client.get_ticker(symbol)
    except LiquidError as e:
        return handle_sdk_error("calculate_position_size", e)

    if account.equity <= 0:
        return {
            "error": True,
            "message": f"Account equity is ${account.equity:.2f}. Cannot calculate position size.",
            "next_steps": ["get_account"],
        }

    if ticker.mark_price <= 0:
        return {
            "error": True,
            "message": f"Invalid mark price ({ticker.mark_price}) for {symbol}. Market may be halted.",
            "next_steps": ["get_markets", "get_ticker"],
        }

    usd_size = account.equity * (percent_of_equity / 100.0)
    token_amount = usd_size / ticker.mark_price
    return {
        "symbol": symbol,
        "percent_of_equity": percent_of_equity,
        "equity": account.equity,
        "usd_size": round(usd_size, 2),
        "mark_price": ticker.mark_price,
        "token_amount": token_amount,
        "min_order": min_order_info(ticker.mark_price),
        "next_steps": ["validate_trade", "place_order", "place_bracket_order"],
    }


@mcp.tool(
    annotations=ToolAnnotations(readOnlyHint=True, idempotentHint=True),
)
def validate_trade(
    symbol: str,
    side: str,
    size: float,
    leverage: int = 1,
    type: str = "market",
    ctx: Context = None,
) -> dict[str, Any]:
    """Pre-flight risk check. Call BEFORE place_order to verify the trade passes all guardrails.

    Checks: symbol validity, balance, max order size, daily loss limit, leverage range,
    and side/type validity.

    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
        leverage: Leverage multiplier (default 1)
        type: "market" or "limit" (default "market")
    """
    cfg = get_config()
    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(get_config().daily_loss_limit).check()
    if loss_err:
        issues.append(loss_err)

    if side not in ("buy", "sell"):
        issues.append("side must be 'buy' or 'sell'")

    if type not in ("market", "limit"):
        issues.append("type must be 'market' or 'limit'")

    if not 1 <= leverage <= 200:
        issues.append("leverage must be between 1 and 200")

    if leverage > 20:
        issues.append(
            f"Warning: {leverage}x leverage significantly increases liquidation risk"
        )

    min_order: dict[str, Any] | None = None
    try:
        client = get_client(cfg)
        account = client.get_account()
        if size > account.available_balance:
            issues.append(
                f"Insufficient balance: ${account.available_balance:.2f} "
                f"available, ${size:.2f} required"
            )
        ticker = client.get_ticker(symbol)
        if ticker.mark_price > 0:
            min_order = min_order_info(ticker.mark_price)
    except SymbolNotFoundError:
        issues.append(f"Symbol '{symbol}' not found. Call get_markets to list valid symbols.")
    except LiquidError as e:
        issues.append(f"Could not verify: {e.message}")

    ok = len(issues) == 0
    result: dict[str, Any] = {
        "ok": ok,
        "issues": issues,
        "trade": {"symbol": symbol, "side": side, "size": size, "leverage": leverage, "type": type},
        "next_steps": ["place_order", "place_bracket_order"] if ok else ["get_account"],
    }
    if min_order:
        result["min_order"] = min_order
    return result
