from typing import Callable, Union
from .interfaces import MetricProvider
from .config import MonitoringConfig

class MonitoringFacade:
    _instance: MetricProvider = None
    _config: MonitoringConfig = None
    _config_factory: Callable[[], MonitoringConfig] = None

    @classmethod
    def configure(cls, config: Union[MonitoringConfig, Callable[[], MonitoringConfig]]):
        """
        Accept either a static MonitoringConfig or a zero-argument callable that
        returns a MonitoringConfig (e.g. a lambda reading from constance/env).

        Using a callable allows config changes (e.g. via Django Admin + constance)
        to be picked up automatically on warm Lambda starts without a redeploy.
        """
        if callable(config):
            cls._config_factory = config
            cls._config = None
        else:
            cls._config_factory = None
            cls._config = config
        cls._instance = None  # Reset instance so it picks up new config

    @classmethod
    def _resolve_config(cls) -> MonitoringConfig:
        """Return the current config, invoking the factory if one was provided."""
        if cls._config_factory is not None:
            return cls._config_factory()
        return cls._config

    @classmethod
    def _create_provider(cls, config: MonitoringConfig) -> MetricProvider:
        """Instantiate and return the correct provider for the given config."""
        if config.provider == "datadog":
            from .providers.datadog import DatadogProvider
            return DatadogProvider(
                api_key=config.datadog_api_key,
                app_key=config.datadog_app_key,
                host_name=config.datadog_hostname,
            )
        elif config.provider == "sumologic":
            from .providers.sumologic import SumologicProvider
            return SumologicProvider(sumologic_url=config.sumologic_url)
        else:
            from .providers.console import ConsoleProvider
            return ConsoleProvider()

    @classmethod
    def get_instance(cls) -> MetricProvider:
        if cls._config is None and cls._config_factory is None:
            raise ValueError(
                "MonitoringFacade is not configured. Call MonitoringFacade.configure() first."
            )

        current_config = cls._resolve_config()

        # Recreate provider only when config has actually changed (Pydantic equality).
        # On warm Lambda starts this short-circuits immediately when nothing changed.
        if cls._instance is None or current_config != cls._config:
            cls._config = current_config
            cls._instance = cls._create_provider(current_config)

        return cls._instance
