from typing import Any

from .base import OptionsDataProvider
from .commodity_base import CommodityOptionsDataProvider
from .greeks_base import OptionsGreeksDataProvider
from .sina import SinaOptionsProvider


class OptionsDataFactory:
    """Factory class for creating options data providers"""

    _providers: dict[str, type[OptionsDataProvider]] = {
        "sina": SinaOptionsProvider,
    }

    _greeks_providers: dict[str, type[OptionsGreeksDataProvider]] = {}
    _commodity_providers: dict[str, type[CommodityOptionsDataProvider]] = {}

    @classmethod
    def get_provider(cls, provider_name: str, **kwargs: Any) -> OptionsDataProvider:
        """Get an options data provider by name

        Args:
            provider_name: Name of the provider (e.g., 'sina')
            **kwargs: Additional arguments to pass to the provider's constructor

        Returns:
            OptionsDataProvider: An instance of the requested provider

        Raises:
            ValueError: If the requested provider is not found
        """
        provider_class = cls._providers.get(provider_name.lower())
        if not provider_class:
            raise ValueError(f"Unknown options data provider: {provider_name}")

        return provider_class(**kwargs)

    @classmethod
    def get_greeks_provider(cls, provider_name: str, **kwargs: Any) -> OptionsGreeksDataProvider:
        """Get an options Greeks data provider by name

        Args:
            provider_name: Name of the provider (e.g., 'sse', 'exchange')
            **kwargs: Additional arguments to pass to the provider's constructor

        Returns:
            OptionsGreeksDataProvider: An instance of the requested provider

        Raises:
            ValueError: If the requested provider is not found
        """
        if not cls._greeks_providers:
            cls._register_greeks_defaults()

        provider_class = cls._greeks_providers.get(provider_name.lower())
        if not provider_class:
            raise ValueError(f"Unknown options greeks provider: {provider_name}")

        return provider_class(**kwargs)

    @classmethod
    def register_provider(cls, name: str, provider_class: type[OptionsDataProvider]) -> None:
        """Register a new options data provider

        Args:
            name: Name to associate with this provider
            provider_class: The provider class to register
        """
        cls._providers[name.lower()] = provider_class

    @classmethod
    def register_greeks_provider(
        cls, name: str, provider_class: type[OptionsGreeksDataProvider]
    ) -> None:
        """Register a new options Greeks data provider

        Args:
            name: Name to associate with this provider
            provider_class: The provider class to register
        """
        cls._greeks_providers[name.lower()] = provider_class

    @classmethod
    def _register_greeks_defaults(cls) -> None:
        """Lazily register default Greeks providers to avoid circular imports."""
        from .exchange import ExchangeOptionsProvider
        from .sse import SSEOptionsGreeksProvider

        cls._greeks_providers.setdefault("sse", SSEOptionsGreeksProvider)
        cls._greeks_providers.setdefault("exchange", ExchangeOptionsProvider)

    @classmethod
    def get_commodity_provider(
        cls, provider_name: str, **kwargs: Any
    ) -> CommodityOptionsDataProvider:
        """Get a commodity options data provider by name.

        Args:
            provider_name: Provider name (e.g., 'sina').
            **kwargs: Additional arguments to pass to the provider's constructor.

        Returns:
            CommodityOptionsDataProvider: Provider instance.

        Raises:
            ValueError: If the requested provider is not found.
        """
        if not cls._commodity_providers:
            cls._register_commodity_defaults()

        provider_class = cls._commodity_providers.get(provider_name.lower())
        if not provider_class:
            raise ValueError(f"Unknown commodity options data provider: {provider_name}")
        return provider_class(**kwargs)

    @classmethod
    def register_commodity_provider(
        cls, name: str, provider_class: type[CommodityOptionsDataProvider]
    ) -> None:
        """Register a new commodity options data provider.

        Args:
            name: Provider name.
            provider_class: Provider class.
        """
        cls._commodity_providers[name.lower()] = provider_class

    @classmethod
    def _register_commodity_defaults(cls) -> None:
        """Lazily register default commodity options providers."""
        from .commodity_sina import SinaCommodityOptionsProvider

        cls._commodity_providers.setdefault("sina", SinaCommodityOptionsProvider)
