"""
Base interface for Exchange Kernels.

Defines the abstract API that all exchange kernel implementations must support.
Each exchange (or exchange type) will have its own kernel implementation.
"""

from abc import ABC, abstractmethod
from typing import Callable, Any

from .models import (
    OrderIntent,
    OrderState,
    Position,
    AccountState,
    AssetMetadata,
)
from .events import BaseEvent
from ..schema.order_spec import (
    StandardOrder,
    StandardOrderGroup,
    SymbolMetadata,
)


class ExchangeKernel(ABC):
    """
    Abstract base class for exchange kernels.

    An exchange kernel manages the lifecycle of orders on a specific exchange,
    maintaining both desired state (intents from strategy kernel) and actual
    state (current orders on exchange), and continuously reconciling them.

    Think of it as a Kubernetes-style controller for trading orders.
    """

    def __init__(self, exchange_id: str):
        """
        Initialize the exchange kernel.

        Args:
            exchange_id: Unique identifier for this exchange instance
        """
        self.exchange_id = exchange_id

    # -------------------------------------------------------------------------
    # Command API - Strategy kernel sends intents
    # -------------------------------------------------------------------------

    @abstractmethod
    async def place_order(self, order: StandardOrder, metadata: dict[str, Any] | None = None) -> str:
        """
        Place a single order on the exchange.

        This creates an OrderIntent and begins the reconciliation process to
        get the order onto the exchange.

        Args:
            order: The order specification
            metadata: Optional strategy-specific metadata

        Returns:
            intent_id: Unique identifier for this order intent

        Raises:
            ValidationError: If order violates market constraints
            ExchangeError: If exchange rejects the order
        """
        pass

    @abstractmethod
    async def place_order_group(
        self,
        group: StandardOrderGroup,
        metadata: dict[str, Any] | None = None
    ) -> list[str]:
        """
        Place a group of orders with OCO (One-Cancels-Other) relationship.

        Args:
            group: Group of orders with OCO mode
            metadata: Optional strategy-specific metadata

        Returns:
            intent_ids: List of intent IDs for each order in the group

        Raises:
            ValidationError: If any order violates market constraints
            ExchangeError: If exchange rejects the group
        """
        pass

    @abstractmethod
    async def cancel_order(self, intent_id: str) -> None:
        """
        Cancel an order by intent ID.

        Updates the intent to indicate cancellation is desired, and the
        reconciliation loop will handle the actual exchange cancellation.

        Args:
            intent_id: Intent ID of the order to cancel

        Raises:
            NotFoundError: If intent_id doesn't exist
            ExchangeError: If exchange rejects cancellation
        """
        pass

    @abstractmethod
    async def modify_order(
        self,
        intent_id: str,
        new_order: StandardOrder,
    ) -> None:
        """
        Modify an existing order.

        Updates the order intent, and the reconciliation loop will update
        the exchange order (via modify API if available, or cancel+replace).

        Args:
            intent_id: Intent ID of the order to modify
            new_order: New order specification

        Raises:
            NotFoundError: If intent_id doesn't exist
            ValidationError: If new order violates market constraints
            ExchangeError: If exchange rejects modification
        """
        pass

    @abstractmethod
    async def cancel_all_orders(self, symbol_id: str | None = None) -> int:
        """
        Cancel all orders, optionally filtered by symbol.

        Args:
            symbol_id: If provided, only cancel orders for this symbol

        Returns:
            count: Number of orders canceled
        """
        pass

    # -------------------------------------------------------------------------
    # Query API - Read desired and actual state
    # -------------------------------------------------------------------------

    @abstractmethod
    async def get_order_intent(self, intent_id: str) -> OrderIntent:
        """
        Get the desired order state (what strategy kernel wants).

        Args:
            intent_id: Intent ID to query

        Returns:
            The order intent

        Raises:
            NotFoundError: If intent_id doesn't exist
        """
        pass

    @abstractmethod
    async def get_order_state(self, intent_id: str) -> OrderState:
        """
        Get the actual order state (what's currently on exchange).

        Args:
            intent_id: Intent ID to query

        Returns:
            The current order state

        Raises:
            NotFoundError: If intent_id doesn't exist
        """
        pass

    @abstractmethod
    async def get_all_intents(self, symbol_id: str | None = None) -> list[OrderIntent]:
        """
        Get all order intents, optionally filtered by symbol.

        Args:
            symbol_id: If provided, only return intents for this symbol

        Returns:
            List of order intents
        """
        pass

    @abstractmethod
    async def get_all_orders(self, symbol_id: str | None = None) -> list[OrderState]:
        """
        Get all actual order states, optionally filtered by symbol.

        Args:
            symbol_id: If provided, only return orders for this symbol

        Returns:
            List of order states
        """
        pass

    @abstractmethod
    async def get_positions(self, symbol_id: str | None = None) -> list[Position]:
        """
        Get current positions, optionally filtered by symbol.

        Args:
            symbol_id: If provided, only return positions for this symbol

        Returns:
            List of positions
        """
        pass

    @abstractmethod
    async def get_account_state(self) -> AccountState:
        """
        Get current account state (balances, margin, etc.).

        Returns:
            Current account state
        """
        pass

    @abstractmethod
    async def get_symbol_metadata(self, symbol_id: str) -> SymbolMetadata:
        """
        Get metadata for a symbol (constraints, capabilities, etc.).

        Args:
            symbol_id: Symbol to query

        Returns:
            Symbol metadata

        Raises:
            NotFoundError: If symbol doesn't exist on this exchange
        """
        pass

    @abstractmethod
    async def get_asset_metadata(self, asset_id: str) -> AssetMetadata:
        """
        Get metadata for an asset.

        Args:
            asset_id: Asset to query

        Returns:
            Asset metadata

        Raises:
            NotFoundError: If asset doesn't exist
        """
        pass

    @abstractmethod
    async def list_symbols(self) -> list[str]:
        """
        List all available symbols on this exchange.

        Returns:
            List of symbol IDs
        """
        pass

    # -------------------------------------------------------------------------
    # Event Subscription API
    # -------------------------------------------------------------------------

    @abstractmethod
    def subscribe_events(
        self,
        callback: Callable[[BaseEvent], None],
        event_filter: dict[str, Any] | None = None,
    ) -> str:
        """
        Subscribe to events from this exchange kernel.

        Args:
            callback: Function to call when events occur
            event_filter: Optional filter criteria (event_type, symbol_id, etc.)

        Returns:
            subscription_id: Unique ID for this subscription (for unsubscribe)
        """
        pass

    @abstractmethod
    def unsubscribe_events(self, subscription_id: str) -> None:
        """
        Unsubscribe from events.

        Args:
            subscription_id: Subscription ID returned from subscribe_events
        """
        pass

    # -------------------------------------------------------------------------
    # Lifecycle Management
    # -------------------------------------------------------------------------

    @abstractmethod
    async def start(self) -> None:
        """
        Start the exchange kernel.

        Initializes connections, starts reconciliation loops, etc.
        """
        pass

    @abstractmethod
    async def stop(self) -> None:
        """
        Stop the exchange kernel.

        Closes connections, stops reconciliation loops, etc.
        Does NOT cancel open orders - call cancel_all_orders() first if desired.
        """
        pass

    @abstractmethod
    async def health_check(self) -> dict[str, Any]:
        """
        Check health status of the exchange kernel.

        Returns:
            Health status dict with connection state, latency, error counts, etc.
        """
        pass

    # -------------------------------------------------------------------------
    # Reconciliation Control (advanced)
    # -------------------------------------------------------------------------

    @abstractmethod
    async def force_reconciliation(self, intent_id: str | None = None) -> None:
        """
        Force immediate reconciliation.

        Args:
            intent_id: If provided, only reconcile this specific intent.
                      If None, reconcile all intents.
        """
        pass

    @abstractmethod
    def get_reconciliation_metrics(self) -> dict[str, Any]:
        """
        Get metrics about the reconciliation process.

        Returns:
            Metrics dict with reconciliation lag, error rates, retry counts, etc.
        """
        pass
