"""
Data models for the Exchange Kernel.

Defines order intents, order state, positions, assets, and account state.
"""

from enum import StrEnum
from typing import Any

from pydantic import BaseModel, Field

from ..schema.order_spec import (
    StandardOrder,
    StandardOrderStatus,
    AssetType,
    Float,
    Uint64,
)


# ---------------------------------------------------------------------------
# Order Intent and State
# ---------------------------------------------------------------------------

class OrderIntent(BaseModel):
    """
    Desired order state from the strategy kernel.

    This represents what the strategy wants, not what currently exists.
    The exchange kernel will work to reconcile actual state with this intent.
    """

    model_config = {"extra": "forbid"}

    intent_id: str = Field(description="Unique identifier for this intent (client-assigned)")
    order: StandardOrder = Field(description="The desired order specification")
    group_id: str | None = Field(default=None, description="Group ID for OCO relationships")
    created_at: Uint64 = Field(description="When this intent was created")
    updated_at: Uint64 = Field(description="When this intent was last modified")
    metadata: dict[str, Any] = Field(default_factory=dict, description="Strategy-specific metadata")


class ReconciliationStatus(StrEnum):
    """Status of reconciliation between intent and actual state"""
    PENDING = "PENDING"  # Not yet submitted to exchange
    SUBMITTING = "SUBMITTING"  # Currently being submitted
    ACTIVE = "ACTIVE"  # Successfully placed on exchange
    RECONCILING = "RECONCILING"  # Intent changed, updating exchange order
    FAILED = "FAILED"  # Failed to submit or reconcile
    COMPLETED = "COMPLETED"  # Order fully filled
    CANCELED = "CANCELED"  # Order canceled


class OrderState(BaseModel):
    """
    Actual current state of an order on the exchange.

    This represents reality - what the exchange reports about the order.
    May differ from OrderIntent during reconciliation.
    """

    model_config = {"extra": "forbid"}

    intent_id: str = Field(description="Links back to the OrderIntent")
    exchange_order_id: str = Field(description="Exchange-assigned order ID")
    status: StandardOrderStatus = Field(description="Current order status from exchange")
    reconciliation_status: ReconciliationStatus = Field(description="Reconciliation state")
    last_sync_at: Uint64 = Field(description="Last time we synced with exchange")
    error_message: str | None = Field(default=None, description="Error details if FAILED")


# ---------------------------------------------------------------------------
# Position and Asset Models
# ---------------------------------------------------------------------------

class AssetMetadata(BaseModel):
    """
    Metadata describing an asset type.

    Provides context for positions, balances, and trading.
    """

    model_config = {"extra": "forbid"}

    asset_id: str = Field(description="Unique asset identifier")
    symbol: str = Field(description="Asset symbol (e.g., 'BTC', 'ETH', 'USD')")
    asset_type: AssetType = Field(description="Type of asset")
    name: str = Field(description="Full name")

    # Contract specifications (for derivatives)
    contract_size: Float | None = Field(default=None, description="Contract multiplier")
    settlement_asset: str | None = Field(default=None, description="Settlement currency")
    expiry_timestamp: Uint64 | None = Field(default=None, description="Expiration timestamp")

    # Trading parameters
    tick_size: Float | None = Field(default=None, description="Minimum price increment")
    lot_size: Float | None = Field(default=None, description="Minimum quantity increment")

    # Margin requirements (for leveraged products)
    initial_margin_rate: Float | None = Field(default=None, description="Initial margin requirement")
    maintenance_margin_rate: Float | None = Field(default=None, description="Maintenance margin requirement")

    # Additional metadata
    metadata: dict[str, Any] = Field(default_factory=dict, description="Exchange-specific metadata")


class Asset(BaseModel):
    """
    An asset holding (spot, margin, derivative position, etc.)
    """

    model_config = {"extra": "forbid"}

    asset_id: str = Field(description="References AssetMetadata")
    quantity: Float = Field(description="Amount held (positive or negative for short positions)")
    available: Float = Field(description="Amount available for trading (not locked in orders)")
    locked: Float = Field(description="Amount locked in open orders")

    # For derivative positions
    entry_price: Float | None = Field(default=None, description="Average entry price")
    mark_price: Float | None = Field(default=None, description="Current mark price")
    liquidation_price: Float | None = Field(default=None, description="Estimated liquidation price")
    unrealized_pnl: Float | None = Field(default=None, description="Unrealized profit/loss")
    realized_pnl: Float | None = Field(default=None, description="Realized profit/loss")

    # Margin info
    margin_used: Float | None = Field(default=None, description="Margin allocated to this position")

    updated_at: Uint64 = Field(description="Last update timestamp")


class Position(BaseModel):
    """
    A trading position (spot, margin, perpetual, futures, etc.)

    Tracks both the asset holdings and associated metadata.
    """

    model_config = {"extra": "forbid"}

    position_id: str = Field(description="Unique position identifier")
    symbol_id: str = Field(description="Trading symbol")
    asset: Asset = Field(description="Asset holding details")
    metadata: AssetMetadata = Field(description="Asset metadata")

    # Position-level info
    leverage: Float | None = Field(default=None, description="Current leverage")
    side: str | None = Field(default=None, description="LONG or SHORT (for derivatives)")

    updated_at: Uint64 = Field(description="Last update timestamp")


class Balance(BaseModel):
    """Account balance for a single currency/asset"""

    model_config = {"extra": "forbid"}

    asset_id: str = Field(description="Asset identifier")
    total: Float = Field(description="Total balance")
    available: Float = Field(description="Available for trading")
    locked: Float = Field(description="Locked in orders/positions")

    # For margin accounts
    borrowed: Float = Field(default=0.0, description="Borrowed amount (margin)")
    interest: Float = Field(default=0.0, description="Accrued interest")

    updated_at: Uint64 = Field(description="Last update timestamp")


class AccountState(BaseModel):
    """
    Complete account state including balances, positions, and margin info.
    """

    model_config = {"extra": "forbid"}

    account_id: str = Field(description="Account identifier")
    exchange: str = Field(description="Exchange identifier")

    balances: list[Balance] = Field(default_factory=list, description="All asset balances")
    positions: list[Position] = Field(default_factory=list, description="All open positions")

    # Margin account info
    total_equity: Float | None = Field(default=None, description="Total account equity")
    total_margin_used: Float | None = Field(default=None, description="Total margin in use")
    total_available_margin: Float | None = Field(default=None, description="Available margin")
    margin_level: Float | None = Field(default=None, description="Margin level (equity/margin_used)")

    # Risk metrics
    total_unrealized_pnl: Float | None = Field(default=None, description="Total unrealized P&L")
    total_realized_pnl: Float | None = Field(default=None, description="Total realized P&L")

    updated_at: Uint64 = Field(description="Last update timestamp")
    metadata: dict[str, Any] = Field(default_factory=dict, description="Exchange-specific data")
