"""
Abstract DataSource interface.

Inspired by TradingView's Datafeed API with extensions for flexible column schemas
and AI-native metadata.
"""

from abc import ABC, abstractmethod
from typing import Callable, List, Optional

from .schema import DatafeedConfig, HistoryResult, SearchResult, SymbolInfo


class DataSource(ABC):
    """
    Abstract base class for time-series data sources.

    Provides a standardized interface for:
    - Symbol search and metadata retrieval
    - Historical data queries (time-based, paginated)
    - Real-time data subscriptions

    All data rows must have a timestamp. Additional columns are flexible
    and described via ColumnInfo metadata.
    """

    @abstractmethod
    async def get_config(self) -> DatafeedConfig:
        """
        Get datafeed configuration and capabilities.

        Called once during initialization to understand what this data source
        supports (resolutions, exchanges, search, etc.).

        Returns:
            DatafeedConfig describing this datafeed's capabilities
        """
        pass

    @abstractmethod
    async def search_symbols(
        self,
        query: str,
        type: Optional[str] = None,
        exchange: Optional[str] = None,
        limit: int = 30,
    ) -> List[SearchResult]:
        """
        Search for symbols matching a text query.

        Args:
            query: Free-text search string
            type: Optional filter by instrument type
            exchange: Optional filter by exchange
            limit: Maximum number of results

        Returns:
            List of matching symbols with basic metadata
        """
        pass

    @abstractmethod
    async def resolve_symbol(self, symbol: str) -> SymbolInfo:
        """
        Get complete metadata for a symbol.

        Called after a symbol is selected to retrieve full information including
        supported resolutions, column schema, trading session, etc.

        Args:
            symbol: Symbol identifier

        Returns:
            Complete SymbolInfo including column definitions

        Raises:
            ValueError: If symbol is not found
        """
        pass

    @abstractmethod
    async def get_bars(
        self,
        symbol: str,
        resolution: str,
        from_time: int,
        to_time: int,
        countback: Optional[int] = None,
    ) -> HistoryResult:
        """
        Get historical bars for a symbol and resolution.

        Time range is specified in Unix timestamps (seconds). If more data is
        available beyond the requested range, the result should include a
        nextTime value for pagination.

        Args:
            symbol: Symbol identifier
            resolution: Time resolution (e.g., "1", "5", "60", "1D")
            from_time: Start time (Unix timestamp in seconds)
            to_time: End time (Unix timestamp in seconds)
            countback: Optional limit on number of bars to return

        Returns:
            HistoryResult with bars, column schema, and pagination info

        Raises:
            ValueError: If symbol or resolution is not supported
        """
        pass

    @abstractmethod
    async def subscribe_bars(
        self,
        symbol: str,
        resolution: str,
        on_tick: Callable[[dict], None],
    ) -> str:
        """
        Subscribe to real-time bar updates.

        The callback will be invoked with new bar data as it becomes available.
        The data dict will match the column schema from resolve_symbol().

        Args:
            symbol: Symbol identifier
            resolution: Time resolution
            on_tick: Callback function receiving bar data dict

        Returns:
            Subscription ID for later unsubscribe

        Raises:
            ValueError: If symbol or resolution is not supported
        """
        pass

    @abstractmethod
    async def unsubscribe_bars(self, subscription_id: str) -> None:
        """
        Unsubscribe from real-time updates.

        Args:
            subscription_id: ID returned from subscribe_bars()
        """
        pass
