# app/modules/core/services/stats_aggregator.py
"""
Stats aggregator service for collecting metrics from all modules.

This service lives in core because dashboards are core functionality that should
always be available. It discovers and aggregates MetricsProviders from all enabled
modules, providing a unified interface for dashboard statistics.

Benefits:
- Dashboards always work (aggregator is in core)
- Each module owns its metrics (no cross-module coupling)
- Optional modules are truly optional (can be removed without breaking app)
- Easy to add new metrics (just implement MetricsProviderProtocol in your module)

Usage:
    from app.modules.core.services.stats_aggregator import stats_aggregator

    # Get vendor dashboard stats
    stats = stats_aggregator.get_vendor_dashboard_stats(
        db=db, vendor_id=123, platform_id=1
    )

    # Get admin dashboard stats
    stats = stats_aggregator.get_admin_dashboard_stats(
        db=db, platform_id=1
    )
"""

import logging
from typing import TYPE_CHECKING, Any

from sqlalchemy.orm import Session

from app.modules.contracts.metrics import (
    MetricValue,
    MetricsContext,
    MetricsProviderProtocol,
)

if TYPE_CHECKING:
    from app.modules.base import ModuleDefinition

logger = logging.getLogger(__name__)


class StatsAggregatorService:
    """
    Aggregates metrics from all module providers.

    This service discovers MetricsProviders from enabled modules and provides
    a unified interface for dashboard statistics. It handles graceful degradation
    when modules are disabled or providers fail.
    """

    def _get_enabled_providers(
        self, db: Session, platform_id: int
    ) -> list[tuple["ModuleDefinition", MetricsProviderProtocol]]:
        """
        Get metrics providers from enabled modules.

        Args:
            db: Database session
            platform_id: Platform ID to check module enablement

        Returns:
            List of (module, provider) tuples for enabled modules with providers
        """
        from app.modules.registry import MODULES
        from app.modules.service import module_service

        providers: list[tuple[ModuleDefinition, MetricsProviderProtocol]] = []

        for module in MODULES.values():
            # Skip modules without metrics providers
            if not module.has_metrics_provider():
                continue

            # Core modules are always enabled, check others
            if not module.is_core:
                try:
                    if not module_service.is_module_enabled(db, platform_id, module.code):
                        continue
                except Exception as e:
                    logger.warning(
                        f"Failed to check if module {module.code} is enabled: {e}"
                    )
                    continue

            # Get the provider instance
            try:
                provider = module.get_metrics_provider_instance()
                if provider is not None:
                    providers.append((module, provider))
            except Exception as e:
                logger.warning(
                    f"Failed to get metrics provider for module {module.code}: {e}"
                )

        return providers

    def get_vendor_dashboard_stats(
        self,
        db: Session,
        vendor_id: int,
        platform_id: int,
        context: MetricsContext | None = None,
    ) -> dict[str, list[MetricValue]]:
        """
        Get all metrics for a vendor, grouped by category.

        Called by the vendor dashboard to display vendor-scoped statistics.

        Args:
            db: Database session
            vendor_id: ID of the vendor to get metrics for
            platform_id: Platform ID (for module enablement check)
            context: Optional filtering/scoping context

        Returns:
            Dict mapping category name to list of MetricValue objects
        """
        providers = self._get_enabled_providers(db, platform_id)
        result: dict[str, list[MetricValue]] = {}

        for module, provider in providers:
            try:
                metrics = provider.get_vendor_metrics(db, vendor_id, context)
                if metrics:
                    result[provider.metrics_category] = metrics
            except Exception as e:
                logger.warning(
                    f"Failed to get vendor metrics from module {module.code}: {e}"
                )
                # Continue with other providers - graceful degradation

        return result

    def get_admin_dashboard_stats(
        self,
        db: Session,
        platform_id: int,
        context: MetricsContext | None = None,
    ) -> dict[str, list[MetricValue]]:
        """
        Get all metrics for a platform, grouped by category.

        Called by the admin dashboard to display platform-wide statistics.

        Args:
            db: Database session
            platform_id: ID of the platform to get metrics for
            context: Optional filtering/scoping context

        Returns:
            Dict mapping category name to list of MetricValue objects
        """
        providers = self._get_enabled_providers(db, platform_id)
        result: dict[str, list[MetricValue]] = {}

        for module, provider in providers:
            try:
                metrics = provider.get_platform_metrics(db, platform_id, context)
                if metrics:
                    result[provider.metrics_category] = metrics
            except Exception as e:
                logger.warning(
                    f"Failed to get platform metrics from module {module.code}: {e}"
                )
                # Continue with other providers - graceful degradation

        return result

    def get_vendor_stats_flat(
        self,
        db: Session,
        vendor_id: int,
        platform_id: int,
        context: MetricsContext | None = None,
    ) -> dict[str, Any]:
        """
        Get vendor metrics as a flat dictionary.

        This is a convenience method that flattens the category-grouped metrics
        into a single dictionary with metric keys as keys. Useful for backward
        compatibility with existing dashboard code.

        Args:
            db: Database session
            vendor_id: ID of the vendor to get metrics for
            platform_id: Platform ID (for module enablement check)
            context: Optional filtering/scoping context

        Returns:
            Flat dict mapping metric keys to values
        """
        categorized = self.get_vendor_dashboard_stats(db, vendor_id, platform_id, context)
        return self._flatten_metrics(categorized)

    def get_admin_stats_flat(
        self,
        db: Session,
        platform_id: int,
        context: MetricsContext | None = None,
    ) -> dict[str, Any]:
        """
        Get platform metrics as a flat dictionary.

        This is a convenience method that flattens the category-grouped metrics
        into a single dictionary with metric keys as keys. Useful for backward
        compatibility with existing dashboard code.

        Args:
            db: Database session
            platform_id: Platform ID
            context: Optional filtering/scoping context

        Returns:
            Flat dict mapping metric keys to values
        """
        categorized = self.get_admin_dashboard_stats(db, platform_id, context)
        return self._flatten_metrics(categorized)

    def _flatten_metrics(
        self, categorized: dict[str, list[MetricValue]]
    ) -> dict[str, Any]:
        """Flatten categorized metrics into a single dictionary."""
        flat: dict[str, Any] = {}
        for metrics in categorized.values():
            for metric in metrics:
                flat[metric.key] = metric.value
        return flat

    def get_available_categories(
        self, db: Session, platform_id: int
    ) -> list[str]:
        """
        Get list of available metric categories for a platform.

        Args:
            db: Database session
            platform_id: Platform ID (for module enablement check)

        Returns:
            List of category names from enabled providers
        """
        providers = self._get_enabled_providers(db, platform_id)
        return [provider.metrics_category for _, provider in providers]


# Singleton instance
stats_aggregator = StatsAggregatorService()

__all__ = ["StatsAggregatorService", "stats_aggregator"]
