"""
Event definitions for the Exchange Kernel.

All events that can occur during the order lifecycle, position management,
and account updates.
"""

from enum import StrEnum
from typing import Any

from pydantic import BaseModel, Field

from ..schema.order_spec import Float, Uint64


# ---------------------------------------------------------------------------
# Base Event Classes
# ---------------------------------------------------------------------------

class EventType(StrEnum):
    """Types of events emitted by the exchange kernel"""
    # Order lifecycle
    ORDER_SUBMITTED = "ORDER_SUBMITTED"
    ORDER_ACCEPTED = "ORDER_ACCEPTED"
    ORDER_REJECTED = "ORDER_REJECTED"
    ORDER_PARTIALLY_FILLED = "ORDER_PARTIALLY_FILLED"
    ORDER_FILLED = "ORDER_FILLED"
    ORDER_CANCELED = "ORDER_CANCELED"
    ORDER_MODIFIED = "ORDER_MODIFIED"
    ORDER_EXPIRED = "ORDER_EXPIRED"

    # Position events
    POSITION_OPENED = "POSITION_OPENED"
    POSITION_MODIFIED = "POSITION_MODIFIED"
    POSITION_CLOSED = "POSITION_CLOSED"

    # Account events
    ACCOUNT_BALANCE_UPDATED = "ACCOUNT_BALANCE_UPDATED"
    MARGIN_CALL_WARNING = "MARGIN_CALL_WARNING"

    # System events
    RECONCILIATION_FAILED = "RECONCILIATION_FAILED"
    CONNECTION_LOST = "CONNECTION_LOST"
    CONNECTION_RESTORED = "CONNECTION_RESTORED"


class BaseEvent(BaseModel):
    """Base class for all exchange kernel events"""

    model_config = {"extra": "forbid"}

    event_type: EventType = Field(description="Type of event")
    timestamp: Uint64 = Field(description="Event timestamp (Unix milliseconds)")
    exchange: str = Field(description="Exchange identifier")
    metadata: dict[str, Any] = Field(default_factory=dict, description="Additional event data")


# ---------------------------------------------------------------------------
# Order Events
# ---------------------------------------------------------------------------

class OrderEvent(BaseEvent):
    """Base class for order-related events"""

    intent_id: str = Field(description="Order intent ID")
    order_id: str | None = Field(default=None, description="Exchange order ID (if assigned)")
    symbol_id: str = Field(description="Symbol being traded")


class OrderSubmitted(OrderEvent):
    """Order has been submitted to the exchange"""

    event_type: EventType = Field(default=EventType.ORDER_SUBMITTED)
    client_order_id: str | None = Field(default=None, description="Client-assigned order ID")


class OrderAccepted(OrderEvent):
    """Order has been accepted by the exchange"""

    event_type: EventType = Field(default=EventType.ORDER_ACCEPTED)
    order_id: str = Field(description="Exchange-assigned order ID")
    accepted_at: Uint64 = Field(description="Exchange acceptance timestamp")


class OrderRejected(OrderEvent):
    """Order was rejected by the exchange"""

    event_type: EventType = Field(default=EventType.ORDER_REJECTED)
    reason: str = Field(description="Rejection reason")
    error_code: str | None = Field(default=None, description="Exchange error code")


class OrderPartiallyFilled(OrderEvent):
    """Order was partially filled"""

    event_type: EventType = Field(default=EventType.ORDER_PARTIALLY_FILLED)
    order_id: str = Field(description="Exchange order ID")
    fill_price: Float = Field(description="Fill price for this execution")
    fill_quantity: Float = Field(description="Quantity filled in this execution")
    total_filled: Float = Field(description="Total quantity filled so far")
    remaining_quantity: Float = Field(description="Remaining quantity to fill")
    commission: Float = Field(default=0.0, description="Commission/fee for this fill")
    commission_asset: str | None = Field(default=None, description="Asset used for commission")
    trade_id: str | None = Field(default=None, description="Exchange trade ID")


class OrderFilled(OrderEvent):
    """Order was completely filled"""

    event_type: EventType = Field(default=EventType.ORDER_FILLED)
    order_id: str = Field(description="Exchange order ID")
    average_fill_price: Float = Field(description="Average execution price")
    total_quantity: Float = Field(description="Total quantity filled")
    total_commission: Float = Field(default=0.0, description="Total commission/fees")
    commission_asset: str | None = Field(default=None, description="Asset used for commission")
    completed_at: Uint64 = Field(description="Completion timestamp")


class OrderCanceled(OrderEvent):
    """Order was canceled"""

    event_type: EventType = Field(default=EventType.ORDER_CANCELED)
    order_id: str = Field(description="Exchange order ID")
    reason: str = Field(description="Cancellation reason")
    filled_quantity: Float = Field(default=0.0, description="Quantity filled before cancellation")
    canceled_at: Uint64 = Field(description="Cancellation timestamp")


class OrderModified(OrderEvent):
    """Order was modified (price, quantity, etc.)"""

    event_type: EventType = Field(default=EventType.ORDER_MODIFIED)
    order_id: str = Field(description="Exchange order ID")
    old_price: Float | None = Field(default=None, description="Previous price")
    new_price: Float | None = Field(default=None, description="New price")
    old_quantity: Float | None = Field(default=None, description="Previous quantity")
    new_quantity: Float | None = Field(default=None, description="New quantity")
    modified_at: Uint64 = Field(description="Modification timestamp")


class OrderExpired(OrderEvent):
    """Order expired (GTD, DAY orders)"""

    event_type: EventType = Field(default=EventType.ORDER_EXPIRED)
    order_id: str = Field(description="Exchange order ID")
    filled_quantity: Float = Field(default=0.0, description="Quantity filled before expiration")
    expired_at: Uint64 = Field(description="Expiration timestamp")


# ---------------------------------------------------------------------------
# Position Events
# ---------------------------------------------------------------------------

class PositionEvent(BaseEvent):
    """Base class for position-related events"""

    position_id: str = Field(description="Position identifier")
    symbol_id: str = Field(description="Symbol identifier")
    asset_id: str = Field(description="Asset identifier")


class PositionOpened(PositionEvent):
    """New position was opened"""

    event_type: EventType = Field(default=EventType.POSITION_OPENED)
    quantity: Float = Field(description="Position quantity")
    entry_price: Float = Field(description="Entry price")
    side: str = Field(description="LONG or SHORT")
    leverage: Float | None = Field(default=None, description="Leverage")


class PositionModified(PositionEvent):
    """Existing position was modified (size change, etc.)"""

    event_type: EventType = Field(default=EventType.POSITION_MODIFIED)
    old_quantity: Float = Field(description="Previous quantity")
    new_quantity: Float = Field(description="New quantity")
    average_entry_price: Float = Field(description="Updated average entry price")
    unrealized_pnl: Float | None = Field(default=None, description="Current unrealized P&L")


class PositionClosed(PositionEvent):
    """Position was closed"""

    event_type: EventType = Field(default=EventType.POSITION_CLOSED)
    exit_price: Float = Field(description="Exit price")
    realized_pnl: Float = Field(description="Realized profit/loss")
    closed_at: Uint64 = Field(description="Closure timestamp")


# ---------------------------------------------------------------------------
# Account Events
# ---------------------------------------------------------------------------

class AccountEvent(BaseEvent):
    """Base class for account-related events"""

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


class AccountBalanceUpdated(AccountEvent):
    """Account balance was updated"""

    event_type: EventType = Field(default=EventType.ACCOUNT_BALANCE_UPDATED)
    asset_id: str = Field(description="Asset that changed")
    old_balance: Float = Field(description="Previous balance")
    new_balance: Float = Field(description="New balance")
    old_available: Float = Field(description="Previous available")
    new_available: Float = Field(description="New available")
    change_reason: str = Field(description="Why balance changed (TRADE, DEPOSIT, WITHDRAWAL, etc.)")


class MarginCallWarning(AccountEvent):
    """Margin level is approaching liquidation threshold"""

    event_type: EventType = Field(default=EventType.MARGIN_CALL_WARNING)
    margin_level: Float = Field(description="Current margin level")
    liquidation_threshold: Float = Field(description="Liquidation threshold")
    required_action: str = Field(description="Required action to avoid liquidation")
    estimated_liquidation_price: Float | None = Field(
        default=None,
        description="Estimated liquidation price for positions"
    )


# ---------------------------------------------------------------------------
# System Events
# ---------------------------------------------------------------------------

class ReconciliationFailed(BaseEvent):
    """Failed to reconcile intent with actual state"""

    event_type: EventType = Field(default=EventType.RECONCILIATION_FAILED)
    intent_id: str = Field(description="Order intent ID")
    error_message: str = Field(description="Error details")
    retry_count: int = Field(description="Number of retry attempts")


class ConnectionLost(BaseEvent):
    """Connection to exchange was lost"""

    event_type: EventType = Field(default=EventType.CONNECTION_LOST)
    reason: str = Field(description="Disconnection reason")


class ConnectionRestored(BaseEvent):
    """Connection to exchange was restored"""

    event_type: EventType = Field(default=EventType.CONNECTION_RESTORED)
    downtime_duration: int = Field(description="Duration of downtime in milliseconds")
