"""
Pydantic models for the MCP Paradex application.

This module contains all Pydantic models used across the application.
"""

from __future__ import annotations

from typing import Annotated, Literal

from pydantic import BaseModel, Field


# System models
class SystemState(BaseModel):
    """Model representing the current state of the Paradex system."""

    status: str
    timestamp: int = Field(default=0)


# Market models
class BBO(BaseModel):
    """Best Bid and Offer model for a market."""

    market: Annotated[str, Field(description="Symbol of the market")]
    seq_no: Annotated[int, Field(description="Sequence number of the orderbook")]
    ask: Annotated[float, Field(description="Best ask price")]
    ask_size: Annotated[float, Field(description="Best ask size")]
    bid: Annotated[float, Field(description="Best bid price")]
    bid_size: Annotated[float, Field(description="Best bid size")]
    last_updated_at: Annotated[
        int, Field(description="Last update to the orderbook in milliseconds")
    ]


class Trade(BaseModel):
    """Trade model representing a completed trade on Paradex."""

    id: Annotated[str, Field(description="Unique Trade ID per TradeType")]
    market: Annotated[str, Field(description="Market for which trade was done")]
    side: Annotated[str, Field(description="Taker side")]
    size: Annotated[float, Field(description="Trade size")]
    price: Annotated[float, Field(description="Trade price")]
    created_at: Annotated[
        int, Field(description="Unix Millisecond timestamp at which trade was done")
    ]
    trade_type: Annotated[str, Field(description="Trade type, can be FILL or LIQUIDATION")]


# Account models
class Position(BaseModel):
    """Position model representing a trading position on Paradex."""

    id: Annotated[str, Field(description="Unique string ID for the position")]
    account: Annotated[str, Field(description="Account ID of the position")]
    market: Annotated[str, Field(description="Market for position")]
    status: Annotated[
        str, Field(description="Status of Position : Open or Closed", enum=["OPEN", "CLOSED"])
    ]
    side: Annotated[str, Field(description="Position Side : Long or Short", enum=["SHORT", "LONG"])]
    size: Annotated[
        float,
        Field(description="Size of the position with sign (positive if long or negative if short)"),
    ]
    average_entry_price: Annotated[float, Field(description="Average entry price")]
    average_entry_price_usd: Annotated[float, Field(description="Average entry price in USD")]
    average_exit_price: Annotated[float, Field(description="Average exit price")]
    unrealized_pnl: Annotated[
        float, Field(description="Unrealized P&L of the position in the quote asset")
    ]
    unrealized_funding_pnl: Annotated[
        float, Field(description="Unrealized running funding P&L for the position")
    ]
    cost: Annotated[float, Field(description="Position cost")]
    cost_usd: Annotated[float, Field(description="Position cost in USD")]
    cached_funding_index: Annotated[float, Field(description="Position cached funding index")]
    last_updated_at: Annotated[int, Field(description="Position last update time")]
    last_fill_id: Annotated[
        str, Field(description="Last fill ID to which the position is referring")
    ]
    seq_no: Annotated[
        int,
        Field(
            description="Unique increasing number (non-sequential) that is assigned to this position update. Can be used to deduplicate multiple feeds"
        ),
    ]
    liquidation_price: Annotated[
        str, Field(default="", description="Liquidation price of the position")
    ]
    leverage: Annotated[float, Field(default=0, description="Leverage of the position")]
    realized_positional_pnl: Annotated[
        float,
        Field(
            default=0,
            description="Realized PnL including both positional PnL and funding payments. Reset to 0 when position is closed or flipped.",
        ),
    ]
    created_at: Annotated[int, Field(default=0, description="Position creation time")]
    closed_at: Annotated[int, Field(default=0, description="Position closed time")]
    realized_positional_funding_pnl: Annotated[
        str,
        Field(
            default="",
            description="Realized Funding PnL for the position. Reset to 0 when position is closed or flipped.",
        ),
    ]


class Fill(BaseModel):
    """Fill model representing a trade fill on Paradex."""

    id: Annotated[str, Field(description="Unique string ID of fill per FillType")]
    side: Annotated[str, Field(description="Taker side")]
    liquidity: Annotated[str, Field(description="Maker or Taker")]
    market: Annotated[str, Field(description="Market name")]
    order_id: Annotated[str, Field(description="Order ID")]
    price: Annotated[float, Field(description="Price at which order was filled")]
    size: Annotated[float, Field(description="Size of the fill")]
    fee: Annotated[float, Field(description="Fee paid by the user")]
    fee_currency: Annotated[str, Field(description="Asset that fee is charged in")]
    created_at: Annotated[int, Field(description="Fill time")]
    remaining_size: Annotated[float, Field(description="Remaining size of the order")]
    client_id: Annotated[str, Field(description="Unique client assigned ID for the order")]
    fill_type: Annotated[str, Field(description="Fill type, can be FILL, LIQUIDATION or TRANSFER")]
    realized_pnl: Annotated[float, Field(description="Realized PnL of the fill")]
    realized_funding: Annotated[float, Field(description="Realized funding of the fill")]
    account: Annotated[str, Field(default="", description="Account that made the fill")]
    underlying_price: Annotated[
        str, Field(default="", description="Underlying asset price of the fill (spot price)")
    ]


class Transaction(BaseModel):
    """Transaction model representing an account transaction on Paradex."""

    id: Annotated[
        str, Field(description="Unique string ID of the event that triggered the transaction")
    ]
    type: Annotated[str, Field(description="Event that triggered the transaction")]
    hash: Annotated[str, Field(description="Tx Hash of the settled trade")]
    state: Annotated[str, Field(description="Status of the transaction on Starknet")]
    created_at: Annotated[
        int, Field(description="Timestamp from when the transaction was sent to blockchain gateway")
    ]
    completed_at: Annotated[
        int, Field(description="Timestamp from when the transaction was completed")
    ]


class Balance(BaseModel):
    """Model representing an account token balance on Paradex."""

    token: Annotated[str, Field(description="Token name (e.g. USDC, DIME)")]
    size: Annotated[str, Field(description="Balance amount")]
    last_updated_at: Annotated[int, Field(description="Balance last updated time in milliseconds")]


# Order models
# Define allowed order types
OrderTypeEnum = Literal[
    "MARKET",
    "LIMIT",
    "STOP_LIMIT",
    "STOP_MARKET",
    "TAKE_PROFIT_LIMIT",
    "TAKE_PROFIT_MARKET",
    "STOP_LOSS_MARKET",
    "STOP_LOSS_LIMIT",
]

# Define allowed instruction types
InstructionEnum = Literal["GTC", "IOC", "POST_ONLY"]
OrderSideEnum = Literal["BUY", "SELL"]


class OrderState(BaseModel):
    """Order state model representing the current state of an order on Paradex."""

    id: Annotated[str, Field(description="Unique order identifier generated by Paradex")]
    account: Annotated[str, Field(description="Paradex Account")]
    market: Annotated[str, Field(description="Market")]
    side: Annotated[str, Field(description="Order side")]
    type: Annotated[str, Field(description="Order type")]
    size: Annotated[float, Field(description="Order size")]
    remaining_size: Annotated[float, Field(description="Remaining size of the order")]
    price: Annotated[float, Field(description="Order price. 0 for MARKET orders")]
    status: Annotated[str, Field(description="Order status")]
    created_at: Annotated[int, Field(description="Order creation time")]
    last_updated_at: Annotated[
        int, Field(description="Order last update time. No changes once status=CLOSED")
    ]
    timestamp: Annotated[int, Field(description="Order signature timestamp")]
    cancel_reason: Annotated[
        str, Field(description="Reason for order cancellation if it was closed by cancel")
    ]
    client_id: Annotated[
        str, Field(description="Client order id provided by the client at order creation")
    ]
    seq_no: Annotated[
        int,
        Field(
            description="Unique increasing number that is assigned to this order update and changes on every order update"
        ),
    ]
    instruction: Annotated[str, Field(description="Execution instruction for order matching")]
    avg_fill_price: Annotated[str, Field(description="Average fill price of the order")]
    stp: Annotated[str, Field(description="Self Trade Prevention mode")]
    received_at: Annotated[
        int, Field(description="Timestamp in milliseconds when order was received by API service")
    ]
    published_at: Annotated[
        int, Field(description="Timestamp in milliseconds when order was sent to the client")
    ]
    flags: Annotated[list[str], Field(description="Order flags, allow flag: REDUCE_ONLY")]
    trigger_price: Annotated[str, Field(description="Trigger price for stop order")]


class VaultStrategy(BaseModel):
    """Vault strategy model representing a strategy for a vault on Paradex."""

    address: Annotated[str, Field(description="Contract address of the sub-operator")]
    name: Annotated[str, Field(description="Strategy name")]


# Vault models
class Vault(BaseModel):
    """Vault model representing a trading vault on Paradex."""

    address: Annotated[str, Field(default="", description="Contract address of the vault")]
    name: Annotated[str, Field(default="", description="Name of the vault")]
    description: Annotated[str, Field(default="", description="Description of the vault")]
    owner_account: Annotated[str, Field(default="", description="Owner account of the vault")]
    operator_account: Annotated[str, Field(default="", description="Operator account of the vault")]
    strategies: Annotated[
        list[str], Field(default_factory=list, description="Strategies of the vault")
    ]
    token_address: Annotated[str, Field(default="", description="LP token address")]
    status: Annotated[str, Field(default="", description="Status of the vault")]
    kind: Annotated[
        str,
        Field(
            default="",
            description="Kind of the vault: 'user' for user-defined vaults, 'protocol' for vaults controlled by Paradex",
        ),
    ]
    profit_share: Annotated[
        int,
        Field(default=0, description="Profit share of the vault in percentage, i.e. 10 means 10%"),
    ]
    lockup_period: Annotated[
        int, Field(default=0, description="Lockup period of the vault in days")
    ]
    max_tvl: Annotated[
        int, Field(default=0, description="Maximum amount of assets the vault can hold in USDC")
    ]
    created_at: Annotated[
        int,
        Field(
            default=0,
            description="Unix timestamp in milliseconds of when the vault has been created",
        ),
    ]
    last_updated_at: Annotated[
        int,
        Field(
            default=0,
            description="Unix timestamp in milliseconds of when the vault was last updated",
        ),
    ]


class VaultBalance(BaseModel):
    """Model representing the balance of a vault."""

    token: Annotated[str, Field(description="Name of the token")]
    size: Annotated[str, Field(description="Balance amount of settlement token")]
    last_updated_at: Annotated[int, Field(description="Balance last updated time")]


class VaultSummary(BaseModel):
    """Model representing a summary of a vault's performance and statistics."""

    address: Annotated[str, Field(default="", description="Contract address of the vault")]
    owner_equity: Annotated[
        str,
        Field(
            default="",
            description="Vault equity of the owner (% of ownership) in percentage, i.e. 0.1 means 10%",
        ),
    ]
    vtoken_supply: Annotated[
        str, Field(default="", description="Total amount of available vault tokens")
    ]
    vtoken_price: Annotated[
        str, Field(default="", description="Current value of vault token price in USD")
    ]
    tvl: Annotated[
        str,
        Field(
            default="",
            description="Net deposits of the vault in USDC (deprecated; use net_deposits instead)",
        ),
    ]
    net_deposits: Annotated[str, Field(default="", description="Net deposits of the vault in USDC")]
    total_roi: Annotated[
        str,
        Field(default="", description="Total ROI of the vault in percentage, i.e. 0.1 means 10%"),
    ]
    roi_24h: Annotated[
        str,
        Field(
            default="",
            description="Return of the vault in the last 24 hours in percentage, i.e. 0.1 means 10%",
        ),
    ]
    roi_7d: Annotated[
        str,
        Field(
            default="",
            description="Return of the vault in the last 7 days in percentage, i.e. 0.1 means 10%",
        ),
    ]
    roi_30d: Annotated[
        str,
        Field(
            default="",
            description="Return of the vault in the last 30 days in percentage, i.e. 0.1 means 10%",
        ),
    ]
    last_month_return: Annotated[
        str,
        Field(
            default="",
            description="APR return of the vault in the last trailing month in percentage, i.e. 0.1 means 10%",
        ),
    ]
    total_pnl: Annotated[str, Field(default="", description="Total P&L of the vault in USD")]
    pnl_24h: Annotated[
        str, Field(default="", description="P&L of the vault in the last 24 hours in USD")
    ]
    pnl_7d: Annotated[
        str, Field(default="", description="P&L of the vault in the last 7 days in USD")
    ]
    pnl_30d: Annotated[
        str, Field(default="", description="P&L of the vault in the last 30 days in USD")
    ]
    max_drawdown: Annotated[
        str,
        Field(
            default="",
            description="Max all time drawdown realized by the vault in percentage, i.e. 0.1 means 10%",
        ),
    ]
    max_drawdown_24h: Annotated[
        str,
        Field(
            default="",
            description="Max drawdown realized by the vault in the last 24 hours in percentage, i.e. 0.1 means 10%",
        ),
    ]
    max_drawdown_7d: Annotated[
        str,
        Field(
            default="",
            description="Max drawdown realized by the vault in the last 7 days in percentage, i.e. 0.1 means 10%",
        ),
    ]
    max_drawdown_30d: Annotated[
        str,
        Field(
            default="",
            description="Max drawdown realized by the vault in the last 30 days in percentage, i.e. 0.1 means 10%",
        ),
    ]
    volume: Annotated[
        str, Field(default="", description="All time volume traded by the vault in USD")
    ]
    volume_24h: Annotated[
        str, Field(default="", description="Volume traded by the vault in the last 24 hours in USD")
    ]
    volume_7d: Annotated[
        str, Field(default="", description="Volume traded by the vault in the last 7 days in USD")
    ]
    volume_30d: Annotated[
        str, Field(default="", description="Volume traded by the vault in the last 30 days in USD")
    ]
    num_depositors: Annotated[
        int, Field(default=0, description="Number of depositors on the vault")
    ]


class VaultAccountSummary(BaseModel):
    """Model representing an account summary for a vault."""

    address: Annotated[str, Field(description="Contract address of the vault")]
    deposited_amount: Annotated[
        str, Field(description="Amount deposited on the vault by the user in USDC")
    ]
    vtoken_amount: Annotated[str, Field(description="Amount of vault tokens owned by the user")]
    total_roi: Annotated[
        str, Field(description="Total ROI realized by the user in percentage, i.e. 0.1 means 10%")
    ]
    total_pnl: Annotated[str, Field(description="Total P&L realized by the user in USD")]
    created_at: Annotated[
        int, Field(description="Unix timestamp in milliseconds of when the user joined the vault")
    ]


class Greeks(BaseModel):
    """Model representing the Greeks of a market."""

    delta: Annotated[float, Field(default=0.0, description="Market Delta")]
    gamma: Annotated[float, Field(default=0.0, description="Market Gamma")]
    vega: Annotated[float, Field(default=0.0, description="Market Vega")]
    rho: Annotated[float, Field(default=0.0, description="Market Rho")]
    vanna: Annotated[float, Field(default=0.0, description="Market Vanna")]
    volga: Annotated[float, Field(default=0.0, description="Market Volga")]

    # Allow additional fields beyond the defined ones
    model_config = {"extra": "allow"}


class MarketSummary(BaseModel):
    """Model representing a summary of a market."""

    symbol: Annotated[str, Field(default="", description="Market symbol")]
    mark_price: Annotated[str, Field(default="", description="Mark price")]
    delta: Annotated[str, Field(default="", description="Deprecated: Use greeks.delta instead")]
    greeks: Annotated[
        Greeks,
        Field(
            default=None, description="Greeks (delta, gamma, vega). Partial for perpetual futures"
        ),
    ]
    last_traded_price: Annotated[str, Field(default="", description="Last traded price")]
    bid: Annotated[str, Field(default="", description="Best bid price")]
    ask: Annotated[str, Field(default="", description="Best ask price")]
    volume_24h: Annotated[str, Field(default="", description="24 hour volume in USD")]
    total_volume: Annotated[
        str, Field(default="", description="Lifetime total traded volume in USD")
    ]
    created_at: Annotated[int, Field(default=0, description="Market summary creation time")]
    underlying_price: Annotated[
        str, Field(default="", description="Underlying asset price (spot price)")
    ]
    open_interest: Annotated[str, Field(default="", description="Open interest in base currency")]
    funding_rate: Annotated[str, Field(default="", description="8 hour funding rate")]
    price_change_rate_24h: Annotated[
        str, Field(default="", description="Price change rate in the last 24 hours")
    ]


class MarketDetails(BaseModel):
    """Model representing the details of a market."""

    symbol: Annotated[str, Field(default="", description="Market symbol")]
    base_currency: Annotated[str, Field(default="", description="Base currency of the market")]
    quote_currency: Annotated[str, Field(default="", description="Quote currency of the market")]
    settlement_currency: Annotated[
        str, Field(default="", description="Settlement currency of the market")
    ]
    order_size_increment: Annotated[
        str, Field(default="", description="Minimum size increment for base currency")
    ]
    price_tick_size: Annotated[
        float, Field(default=0.0, description="Minimum price increment of the market in USD")
    ]
    min_notional: Annotated[float, Field(default=0.0, description="Minimum order size in USD")]
    open_at: Annotated[int, Field(default=0, description="Market open time in milliseconds")]
    expiry_at: Annotated[int, Field(default=0, description="Market expiry time")]
    asset_kind: Annotated[
        str, Field(default="", description="Type of asset", examples=["PERP", "PERP_OPTION"])
    ]
    market_kind: Annotated[str, Field(default="", description="Type of market - always 'cross'")]
    position_limit: Annotated[float, Field(default=0.0, description="Position limit")]
    price_bands_width: Annotated[
        float,
        Field(
            default=0.0,
            description="Price Bands Width, 0.05 means 5% price deviation allowed from mark price",
        ),
    ]
    max_open_orders: Annotated[int, Field(default=0, description="Max open orders")]
    max_funding_rate: Annotated[float, Field(default=0.0, description="Max funding rate")]
    delta1_cross_margin_params: Annotated[
        dict[str, float], Field(default_factory=dict, description="Delta1 Cross margin parameters")
    ]
    option_cross_margin_params: Annotated[
        dict[str, dict[str, float]],
        Field(default_factory=dict, description="Option Cross margin parameters"),
    ]
    price_feed_id: Annotated[
        str,
        Field(
            default="",
            description="Price feed id. Pyth price account used to price underlying asset",
        ),
    ]
    oracle_ewma_factor: Annotated[float, Field(default=0.0, description="Oracle EWMA factor")]
    max_order_size: Annotated[
        float, Field(default=0.0, description="Maximum order size in base currency")
    ]
    max_funding_rate_change: Annotated[
        float, Field(default=0.0, description="Max funding rate change")
    ]
    max_tob_spread: Annotated[
        float,
        Field(
            default=0.0, description="The maximum TOB spread allowed to apply funding rate changes"
        ),
    ]
    interest_rate: Annotated[float, Field(default=0.0, description="Interest rate")]
    clamp_rate: Annotated[float, Field(default=0.0, description="Clamp rate")]
    funding_period_hours: Annotated[int, Field(default=0, description="Funding period in hours")]
    tags: Annotated[list[str], Field(default_factory=list, description="Market tags")]
    option_type: Annotated[str, Field(default="", description="Type of option (PUT or CALL)")]
    strike_price: Annotated[float, Field(default=0.0, description="Strike price for option market")]
    iv_bands_width: Annotated[float, Field(default=0.0, description="IV Bands Width")]


class AccountSummary(BaseModel):
    """Model representing an account summary response from Paradex."""

    account: Annotated[str, Field(description="User's starknet account")]
    account_value: Annotated[str, Field(description="Current account value [with unrealized P&Ls]")]
    free_collateral: Annotated[
        str,
        Field(
            description="Free collateral available (Account value in excess of Initial Margin required)"
        ),
    ]
    initial_margin_requirement: Annotated[
        str, Field(description="Amount required to open trade for the existing positions")
    ]
    maintenance_margin_requirement: Annotated[
        str, Field(description="Amount required to maintain exisiting positions")
    ]
    margin_cushion: Annotated[
        str, Field(description="Acc value in excess of maintenance margin required")
    ]
    seq_no: Annotated[
        int,
        Field(
            description="Unique increasing number (non-sequential) that is assigned to this account update. Can be used to deduplicate multiple feeds"
        ),
    ]
    settlement_asset: Annotated[str, Field(description="Settlement asset for the account")]
    status: Annotated[str, Field(description="Status of the acc - like ACTIVE, LIQUIDATION")]
    total_collateral: Annotated[str, Field(description="User's total collateral")]
    updated_at: Annotated[int, Field(description="Account last updated time")]


class FundingData(BaseModel):
    """
    Model representing funding data for a perpetual market.
    https://docs.paradex.trade/documentation/risk-system/funding-mechanism
    """

    market: Annotated[str, Field(description="Market represents the market identifier")]
    created_at: Annotated[
        int, Field(description="Timestamp in milliseconds when the funding data was calculated")
    ]
    funding_index: Annotated[
        str,
        Field(
            description="Funding Index is accrued funding for 1 unit of the asset since market launchs and is calculated as a time-weighted sum of the Funding Premium. This value expressed in the settlement asset of the instrument (USDC)"
        ),
    ]
    funding_premium: Annotated[
        str,
        Field(
            description="Funding Premium represents the 8h amount paid by long positions to short positions. This value expressed in the settlement asset of the instrument (USDC)"
        ),
    ]
    funding_rate: Annotated[
        str,
        Field(
            description="Clamped [mark price]/[spot price] - 1. Full details - https://docs.paradex.trade/documentation/risk-system/funding-mechanism#funding-rate"
        ),
    ]


# Paginated response models
class PagedMarketDetails(BaseModel):
    """Paginated list of market details."""

    results: list[MarketDetails]
    total: int
    limit: int
    offset: int


class PagedMarketSummaries(BaseModel):
    """Paginated list of market summaries."""

    results: list[MarketSummary]
    total: int
    limit: int
    offset: int


class PagedVaults(BaseModel):
    """Paginated list of vaults."""

    results: list[Vault]
    total: int
    limit: int
    offset: int


class PagedVaultSummaries(BaseModel):
    """Paginated list of vault summaries."""

    results: list[VaultSummary]
    total: int
    limit: int
    offset: int


class PagedOrderStates(BaseModel):
    """Paginated list of order states."""

    results: list[OrderState]
    total: int
    limit: int
    offset: int


# Composite overview models
class AccountOverview(BaseModel):
    """Complete account snapshot with margin health, token balances, and open positions."""

    summary: AccountSummary
    balances: list[Balance]
    positions: list[Position]


class VaultOverview(BaseModel):
    """Complete vault operational snapshot with token balances, open positions, and account health."""

    balances: list[VaultBalance]
    positions: list[Position]
    account_summary: list[VaultAccountSummary]


class PreTradeMarketConstraints(BaseModel):
    """Market-level trading constraints relevant to a pre-trade check."""

    min_notional: float
    order_size_increment: str
    position_limit: float
    price_tick_size: float


class PreTradeBBO(BaseModel):
    """Best bid/offer snapshot used to estimate entry price and slippage."""

    bid: str
    ask: str
    mark_price: str
    funding_rate: str


class PreTradeEstimates(BaseModel):
    """Estimated financial impact of the proposed order.

    All monetary values are in USDC. Fee and funding estimates are approximations
    — actual fees depend on the account's volume-based fee tier.
    """

    estimated_entry_price: float = Field(
        description="Best available execution price (ask for BUY, bid for SELL)"
    )
    estimated_fee_usdc: float = Field(
        description="Estimated taker fee in USDC assuming default 0.05% taker rate"
    )
    slippage_bps: float = Field(
        description="Half-spread slippage from mid price, in basis points (1 bps = 0.01%)"
    )
    daily_funding_cost_usdc: float = Field(
        description=(
            "Estimated daily funding payment in USDC at the current 8h funding rate "
            "(negative = cost for longs / revenue for shorts when funding is positive)"
        )
    )
    break_even_price_change_pct: float = Field(
        description=("Minimum price move required to cover round-trip taker fees, as a percentage")
    )
    existing_unrealized_pnl_usdc: float | None = Field(
        default=None,
        description="Unrealized PnL of the existing position in this market (trading + funding)",
    )


class PreTradeCheckResult(BaseModel):
    """Result of a pre-trade readiness check for a specific market and order size."""

    market_id: str
    side: str
    size: float
    account_status: str
    free_collateral: str
    current_position: Position | None
    bbo: PreTradeBBO
    market_constraints: PreTradeMarketConstraints
    estimates: PreTradeEstimates
    ready_to_trade: bool
    not_ready_reasons: list[str]
