from __future__ import annotations

from datetime import datetime, timezone
from enum import Enum
from pathlib import Path
from typing import Any
from uuid import uuid4

from pydantic import BaseModel, Field


class ExecutionVenue(str, Enum):
    DERIVE = "derive"
    HYPERLIQUID = "hyperliquid"
    LIGHTER = "lighter"
    BEBOP = "bebop"


class ExecutionInstrumentType(str, Enum):
    OPTION = "option"
    PERP = "perp"
    SPOT = "spot"


class ExecutionOrderSide(str, Enum):
    BUY = "buy"
    SELL = "sell"


class ExecutionOrderType(str, Enum):
    MARKET = "market"
    LIMIT = "limit"
    POST_ONLY_LIMIT = "post_only_limit"
    IOC = "ioc"
    FOK = "fok"
    REDUCE_ONLY_LIMIT = "reduce_only_limit"


class ExecutionTimeInForce(str, Enum):
    GTC = "gtc"
    IOC = "ioc"
    FOK = "fok"
    POST_ONLY = "post_only"


class ExecutionOrderStatus(str, Enum):
    RECEIVED = "received"
    ACCEPTED = "accepted"
    FILLED = "filled"
    CANCELED = "canceled"
    REJECTED = "rejected"
    EXPIRED = "expired"
    DRY_RUN = "dry_run"
    PAPER = "paper"


class ExecutionRequestMode(str, Enum):
    DRY_RUN = "dry_run"
    PAPER = "paper"
    LIVE = "live"


class ExecutionFailureCode(str, Enum):
    AUTH_FAILURE = "auth_failure"
    NONCE_MISMATCH = "nonce_mismatch"
    INSUFFICIENT_BALANCE = "insufficient_balance"
    REDUCE_ONLY_VIOLATION = "reduce_only_violation"
    MARKET_UNSUPPORTED = "market_unsupported"
    QUOTE_EXPIRED = "quote_expired"
    VENUE_DISCONNECTED = "venue_disconnected"
    RISK_BLOCKED = "risk_blocked"
    NOT_IMPLEMENTED = "not_implemented"


class VenueCapabilities(BaseModel):
    venue: ExecutionVenue
    supported_instrument_types: list[ExecutionInstrumentType]
    supported_order_types: list[ExecutionOrderType]
    supports_post_only: bool = False
    supports_reduce_only: bool = False
    supports_amend: bool = False
    supports_mass_cancel: bool = False
    supports_private_ws: bool = False
    supports_quote_routing: bool = False


class ExecutionAccountConfig(BaseModel):
    label: str
    venue: ExecutionVenue
    environment: str = "mainnet"
    wallet_address: str | None = None
    private_key_env: str | None = None
    session_key_env: str | None = None
    api_private_key_env: str | None = None
    rpc_url_env: str | None = None
    subaccount_id: int | None = None
    account_index: int | None = None
    api_key_index: int = 255
    vault_address: str | None = None
    enabled_assets: list[str] = Field(default_factory=lambda: ["BTC", "ETH"])
    enabled_symbols: list[str] = Field(default_factory=list)
    max_order_notional_usd: float = 50_000.0
    max_position_notional_usd: float = 250_000.0
    enabled: bool = True
    kill_switch: bool = False
    metadata: dict[str, Any] = Field(default_factory=dict)


class ExecutionIntent(BaseModel):
    request_id: str = Field(default_factory=lambda: uuid4().hex)
    idempotency_key: str = Field(default_factory=lambda: uuid4().hex)
    venue: ExecutionVenue
    account: str
    instrument_type: ExecutionInstrumentType
    asset: str
    symbol: str
    side: ExecutionOrderSide
    order_type: ExecutionOrderType
    time_in_force: ExecutionTimeInForce = ExecutionTimeInForce.GTC
    price: float | None = None
    size: float
    notional_usd: float | None = None
    reduce_only: bool = False
    mode: ExecutionRequestMode = ExecutionRequestMode.DRY_RUN
    client_order_id: str | None = None
    quote_id: str | None = None
    metadata: dict[str, Any] = Field(default_factory=dict)


class ExecutionQuoteRequest(BaseModel):
    venue: ExecutionVenue
    account: str
    asset: str
    symbol: str
    side: ExecutionOrderSide
    size: float
    notional_usd: float | None = None
    mode: ExecutionRequestMode = ExecutionRequestMode.DRY_RUN
    idempotency_key: str = Field(default_factory=lambda: uuid4().hex)
    metadata: dict[str, Any] = Field(default_factory=dict)


class ExecutionCancelRequest(BaseModel):
    venue: ExecutionVenue
    account: str
    request_id: str | None = None
    venue_order_id: str | None = None
    asset: str | None = None
    symbol: str | None = None
    mode: ExecutionRequestMode = ExecutionRequestMode.DRY_RUN
    idempotency_key: str = Field(default_factory=lambda: uuid4().hex)
    metadata: dict[str, Any] = Field(default_factory=dict)


class ExecutionCancelAllRequest(BaseModel):
    venue: ExecutionVenue
    account: str
    asset: str | None = None
    symbol: str | None = None
    mode: ExecutionRequestMode = ExecutionRequestMode.DRY_RUN
    idempotency_key: str = Field(default_factory=lambda: uuid4().hex)
    metadata: dict[str, Any] = Field(default_factory=dict)


class ExecutionActionResult(BaseModel):
    request_id: str
    venue: ExecutionVenue
    account: str
    status: ExecutionOrderStatus
    mode: ExecutionRequestMode
    message: str
    venue_order_id: str | None = None
    quote_id: str | None = None
    failure_code: ExecutionFailureCode | None = None
    payload: dict[str, Any] = Field(default_factory=dict)
    emitted_at: datetime = Field(default_factory=lambda: datetime.now(tz=timezone.utc))


class ExecutionVenueHealth(BaseModel):
    venue: ExecutionVenue
    account: str
    status: str
    ws_connected: bool
    private_ws_connected: bool
    latency_ms: int | None = None
    last_heartbeat_ms: int | None = None
    last_error: str | None = None
    reconnect_count: int = 0


class ExecutionSettingsView(BaseModel):
    accounts_config_path: Path
    default_mode: ExecutionRequestMode
    global_kill_switch: bool
    max_order_notional_usd: float
    max_position_notional_usd: float


VENUE_CAPABILITIES: dict[ExecutionVenue, VenueCapabilities] = {
    ExecutionVenue.DERIVE: VenueCapabilities(
        venue=ExecutionVenue.DERIVE,
        supported_instrument_types=[ExecutionInstrumentType.OPTION, ExecutionInstrumentType.PERP],
        supported_order_types=[
            ExecutionOrderType.MARKET,
            ExecutionOrderType.LIMIT,
            ExecutionOrderType.POST_ONLY_LIMIT,
            ExecutionOrderType.IOC,
            ExecutionOrderType.REDUCE_ONLY_LIMIT,
        ],
        supports_post_only=True,
        supports_reduce_only=True,
        supports_amend=True,
        supports_mass_cancel=True,
        supports_private_ws=True,
    ),
    ExecutionVenue.HYPERLIQUID: VenueCapabilities(
        venue=ExecutionVenue.HYPERLIQUID,
        supported_instrument_types=[ExecutionInstrumentType.PERP],
        supported_order_types=[
            ExecutionOrderType.MARKET,
            ExecutionOrderType.LIMIT,
            ExecutionOrderType.IOC,
            ExecutionOrderType.REDUCE_ONLY_LIMIT,
        ],
        supports_reduce_only=True,
        supports_amend=True,
        supports_mass_cancel=True,
        supports_private_ws=True,
    ),
    ExecutionVenue.LIGHTER: VenueCapabilities(
        venue=ExecutionVenue.LIGHTER,
        supported_instrument_types=[ExecutionInstrumentType.PERP],
        supported_order_types=[
            ExecutionOrderType.MARKET,
            ExecutionOrderType.LIMIT,
            ExecutionOrderType.IOC,
            ExecutionOrderType.REDUCE_ONLY_LIMIT,
        ],
        supports_reduce_only=True,
        supports_amend=True,
        supports_mass_cancel=True,
        supports_private_ws=True,
    ),
    ExecutionVenue.BEBOP: VenueCapabilities(
        venue=ExecutionVenue.BEBOP,
        supported_instrument_types=[ExecutionInstrumentType.SPOT],
        supported_order_types=[ExecutionOrderType.MARKET],
        supports_quote_routing=True,
    ),
}
