import logging
from typing import Callable, Protocol, runtime_checkable

from aprsd.utils import singleton

LOG = logging.getLogger('APRSD')


@runtime_checkable
class StatsProducer(Protocol):
    """The StatsProducer protocol is used to define the interface for collecting stats."""

    def stats(self, serializable=False) -> dict:
        """provide stats in a dictionary format."""
        ...


@singleton
class Collector:
    """The Collector class is used to collect stats from multiple StatsProducer instances."""

    def __init__(self):
        self.producers: list[Callable] = []

    def collect(self, serializable=False) -> dict:
        stats = {}
        for name in self.producers:
            cls = name()
            try:
                stats[cls.__class__.__name__] = cls.stats(
                    serializable=serializable
                ).copy()
            except Exception as e:
                LOG.error(f'Error in producer {name} (stats): {e}')
                raise e
        return stats

    def register_producer(self, producer_name: Callable):
        if not isinstance(producer_name, StatsProducer):
            raise TypeError(f'Producer {producer_name} is not a StatsProducer')
        self.producers.append(producer_name)

    def unregister_producer(self, producer_name: Callable):
        if not isinstance(producer_name, StatsProducer):
            raise TypeError(f'Producer {producer_name} is not a StatsProducer')
        self.producers.remove(producer_name)

    def stop_all(self):
        """Stop and unregister all registered stats producers."""
        for producer in self.producers[:]:
            LOG.info(f'Stopping Stats producer {producer}')
            self.unregister_producer(producer)
