# app/modules/core/services/widget_aggregator.py
"""
Widget aggregator service for collecting dashboard widgets from all modules.

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

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

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

    # Get store dashboard widgets
    widgets = widget_aggregator.get_store_dashboard_widgets(
        db=db, store_id=123, platform_id=1
    )

    # Get admin dashboard widgets
    widgets = widget_aggregator.get_admin_dashboard_widgets(
        db=db, platform_id=1
    )
"""

import logging
from typing import TYPE_CHECKING

from sqlalchemy.orm import Session

from app.modules.contracts.widgets import (
    DashboardWidget,
    DashboardWidgetProviderProtocol,
    StorefrontDashboardCard,
    WidgetContext,
)

if TYPE_CHECKING:
    from app.modules.base import ModuleDefinition

logger = logging.getLogger(__name__)


class WidgetAggregatorService:
    """
    Aggregates widgets from all module providers.

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

    def _get_enabled_providers(
        self, db: Session, platform_id: int
    ) -> list[tuple["ModuleDefinition", DashboardWidgetProviderProtocol]]:
        """
        Get widget 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, DashboardWidgetProviderProtocol]] = []

        for module in MODULES.values():
            # Skip modules without widget providers
            if not module.has_widget_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_widget_provider_instance()
                if provider is not None:
                    providers.append((module, provider))
            except Exception as e:
                logger.warning(
                    f"Failed to get widget provider for module {module.code}: {e}"
                )

        return providers

    def get_store_dashboard_widgets(
        self,
        db: Session,
        store_id: int,
        platform_id: int,
        context: WidgetContext | None = None,
    ) -> dict[str, list[DashboardWidget]]:
        """
        Get all widgets for a store, grouped by category.

        Called by the store dashboard to display store-scoped widgets.

        Args:
            db: Database session
            store_id: ID of the store to get widgets for
            platform_id: Platform ID (for module enablement check)
            context: Optional filtering/scoping context

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

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

        return result

    def get_admin_dashboard_widgets(
        self,
        db: Session,
        platform_id: int,
        context: WidgetContext | None = None,
    ) -> dict[str, list[DashboardWidget]]:
        """
        Get all widgets for a platform, grouped by category.

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

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

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

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

        return result

    def get_widgets_flat(
        self,
        db: Session,
        platform_id: int,
        store_id: int | None = None,
        context: WidgetContext | None = None,
    ) -> list[DashboardWidget]:
        """
        Get widgets as a flat list sorted by order.

        Convenience method that returns all widgets in a single list,
        sorted by their order field.

        Args:
            db: Database session
            platform_id: Platform ID
            store_id: If provided, get store widgets; otherwise platform widgets
            context: Optional filtering/scoping context

        Returns:
            Flat list of DashboardWidget objects sorted by order
        """
        if store_id is not None:
            categorized = self.get_store_dashboard_widgets(
                db, store_id, platform_id, context
            )
        else:
            categorized = self.get_admin_dashboard_widgets(db, platform_id, context)

        # Flatten and sort by order
        all_widgets: list[DashboardWidget] = []
        for widgets in categorized.values():
            all_widgets.extend(widgets)

        return sorted(all_widgets, key=lambda w: w.order)

    def get_widget_by_key(
        self,
        db: Session,
        platform_id: int,
        key: str,
        store_id: int | None = None,
        context: WidgetContext | None = None,
    ) -> DashboardWidget | None:
        """
        Get a specific widget by its key.

        Args:
            db: Database session
            platform_id: Platform ID
            key: Widget key (e.g., "marketplace.recent_imports")
            store_id: If provided, get store widget; otherwise platform widget
            context: Optional filtering/scoping context

        Returns:
            The DashboardWidget with the specified key, or None if not found
        """
        widgets = self.get_widgets_flat(db, platform_id, store_id, context)
        for widget in widgets:
            if widget.key == key:
                return widget
        return None

    def get_storefront_dashboard_cards(
        self,
        db: Session,
        store_id: int,
        customer_id: int,
        platform_id: int,
        context: WidgetContext | None = None,
    ) -> list[StorefrontDashboardCard]:
        """
        Get dashboard cards for the storefront customer account page.

        Collects cards from all enabled modules that implement
        get_storefront_dashboard_cards(), sorted by order.

        Args:
            db: Database session
            store_id: ID of the store
            customer_id: ID of the logged-in customer
            platform_id: Platform ID (for module enablement check)
            context: Optional filtering/scoping context

        Returns:
            Flat list of StorefrontDashboardCard sorted by order
        """
        providers = self._get_enabled_providers(db, platform_id)
        cards: list[StorefrontDashboardCard] = []

        for module, provider in providers:
            if not hasattr(provider, "get_storefront_dashboard_cards"):
                continue
            try:
                module_cards = provider.get_storefront_dashboard_cards(
                    db, store_id, customer_id, context
                )
                if module_cards:
                    cards.extend(module_cards)
            except Exception as e:
                logger.warning(
                    f"Failed to get storefront cards from module {module.code}: {e}"
                )

        return sorted(cards, key=lambda c: c.order)

    def get_available_categories(
        self, db: Session, platform_id: int
    ) -> list[str]:
        """
        Get list of available widget 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.widgets_category for _, provider in providers]


# Singleton instance
widget_aggregator = WidgetAggregatorService()

__all__ = ["WidgetAggregatorService", "widget_aggregator"]
