"""Data source abstract interface for OpenClaw Trading."""

from abc import ABC, abstractmethod
from dataclasses import dataclass
from datetime import datetime
from enum import Enum

import pandas as pd


class DataSourceError(Exception):
    """Base exception for data source errors."""

    pass


class DataNotAvailableError(DataSourceError):
    """Raised when requested data is not available."""

    pass


class Interval(Enum):
    """Standard time intervals for data fetching."""

    MINUTE_1 = "1m"
    MINUTE_5 = "5m"
    MINUTE_15 = "15m"
    MINUTE_30 = "30m"
    HOUR_1 = "1h"
    HOUR_4 = "4h"
    DAY_1 = "1d"
    WEEK_1 = "1wk"
    MONTH_1 = "1mo"


@dataclass(frozen=True)
class OHLCVData:
    """Standard OHLCV data structure.

    Attributes:
        timestamp: Data timestamp
        open: Opening price
        high: Highest price
        low: Lowest price
        close: Closing price
        volume: Trading volume
    """

    timestamp: datetime
    open: float
    high: float
    low: float
    close: float
    volume: float


@dataclass(frozen=True)
class RealtimeQuote:
    """Real-time market quote data.

    Attributes:
        symbol: Stock symbol
        price: Current price
        bid: Bid price
        ask: Ask price
        bid_size: Bid size
        ask_size: Ask size
        volume: Trading volume
        timestamp: Quote timestamp
    """

    symbol: str
    price: float
    bid: float
    ask: float
    bid_size: int
    ask_size: int
    volume: float
    timestamp: datetime


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

    All data source implementations must inherit from this class
    and implement the abstract methods.
    """

    def __init__(self, name: str) -> None:
        """Initialize the data source.

        Args:
            name: Unique identifier for this data source
        """
        self._name = name
        self._available = True

    @property
    def name(self) -> str:
        """Get the data source name."""
        return self._name

    @abstractmethod
    async def fetch_ohlcv(
        self,
        symbol: str,
        interval: Interval = Interval.DAY_1,
        start: datetime | None = None,
        end: datetime | None = None,
        limit: int = 100,
    ) -> pd.DataFrame:
        """Fetch OHLCV (Open, High, Low, Close, Volume) data.

        Args:
            symbol: Stock symbol (e.g., 'AAPL', 'MSFT')
            interval: Time interval for data points
            start: Start date/time (optional)
            end: End date/time (optional)
            limit: Maximum number of data points to return

        Returns:
            DataFrame with columns: timestamp, open, high, low, close, volume

        Raises:
            DataNotAvailableError: If data cannot be fetched
            DataSourceError: For other data source errors
        """
        pass

    @abstractmethod
    async def fetch_realtime(self, symbol: str) -> RealtimeQuote:
        """Fetch real-time quote for a symbol.

        Args:
            symbol: Stock symbol (e.g., 'AAPL', 'MSFT')

        Returns:
            RealtimeQuote with current market data

        Raises:
            DataNotAvailableError: If real-time data is not available
            DataSourceError: For other data source errors
        """
        pass

    @abstractmethod
    def is_available(self) -> bool:
        """Check if the data source is available.

        Returns:
            True if the data source can provide data, False otherwise
        """
        pass

    def set_availability(self, available: bool) -> None:
        """Set the availability status of the data source.

        Args:
            available: New availability status
        """
        self._available = available
