# app/modules/contracts/metrics.py
"""
Metrics provider protocol for cross-module statistics aggregation.

This module defines the protocol that modules implement to expose their own metrics.
The core module's StatsAggregator discovers and aggregates all providers.

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

Usage:
    # 1. Implement the protocol in your module
    class OrderMetricsProvider:
        @property
        def metrics_category(self) -> str:
            return "orders"

        def get_store_metrics(self, db, store_id, **kwargs) -> list[MetricValue]:
            return [
                MetricValue(key="orders.total", value=42, label="Total Orders", category="orders")
            ]

    # 2. Register in module definition
    def _get_metrics_provider():
        from app.modules.orders.services.order_metrics import order_metrics_provider
        return order_metrics_provider

    orders_module = ModuleDefinition(
        code="orders",
        metrics_provider=_get_metrics_provider,
        # ...
    )

    # 3. Metrics appear automatically in dashboards when module is enabled
"""

from dataclasses import dataclass
from datetime import datetime
from typing import TYPE_CHECKING, Protocol, runtime_checkable

if TYPE_CHECKING:
    from sqlalchemy.orm import Session


@dataclass
class MetricValue:
    """
    Standard metric value with metadata.

    This is the unit of data returned by metrics providers.
    It contains both the value and metadata for display.

    Attributes:
        key: Unique identifier for this metric (e.g., "orders.total_count")
             Format: "{category}.{metric_name}" for consistency
        value: The actual metric value (int, float, or str)
        label: Human-readable label for display (e.g., "Total Orders")
        category: Grouping category (should match metrics_category of provider)
        icon: Optional Lucide icon name for UI display (e.g., "shopping-cart")
        description: Optional longer description of what this metric represents
        unit: Optional unit suffix (e.g., "EUR", "%", "items")
        trend: Optional trend indicator ("up", "down", "stable")
        trend_value: Optional numeric trend value (e.g., percentage change)

    Example:
        MetricValue(
            key="orders.total_count",
            value=1234,
            label="Total Orders",
            category="orders",
            icon="shopping-cart",
            unit="orders",
        )
    """

    key: str
    value: int | float | str
    label: str
    category: str
    icon: str | None = None
    description: str | None = None
    unit: str | None = None
    trend: str | None = None  # "up", "down", "stable"
    trend_value: float | None = None


@dataclass
class MetricsContext:
    """
    Context for metrics collection.

    Provides filtering and scoping options for metrics providers.

    Attributes:
        date_from: Start of date range filter
        date_to: End of date range filter
        include_trends: Whether to calculate trends (may be expensive)
        period: Time period for analytics (e.g., "7d", "30d", "90d", "1y")
    """

    date_from: datetime | None = None
    date_to: datetime | None = None
    include_trends: bool = False
    period: str = "30d"


@runtime_checkable
class MetricsProviderProtocol(Protocol):
    """
    Protocol for modules that provide metrics/statistics.

    Each module implements this to expose its own metrics.
    The core module's StatsAggregator discovers and aggregates all providers.

    Implementation Notes:
    - Providers should be stateless (all data via db session)
    - Return empty list if no metrics available (don't raise)
    - Use consistent key format: "{category}.{metric_name}"
    - Include icon hints for UI rendering
    - Be mindful of query performance (use efficient aggregations)

    Example Implementation:
        class OrderMetricsProvider:
            @property
            def metrics_category(self) -> str:
                return "orders"

            def get_store_metrics(
                self, db: Session, store_id: int, context: MetricsContext | None = None
            ) -> list[MetricValue]:
                from app.modules.orders.models import Order
                total = db.query(Order).filter(Order.store_id == store_id).count()
                return [
                    MetricValue(
                        key="orders.total",
                        value=total,
                        label="Total Orders",
                        category="orders",
                        icon="shopping-cart"
                    )
                ]

            def get_platform_metrics(
                self, db: Session, platform_id: int, context: MetricsContext | None = None
            ) -> list[MetricValue]:
                # Aggregate across all stores in platform
                ...
    """

    @property
    def metrics_category(self) -> str:
        """
        Category name for this provider's metrics.

        Should be a short, lowercase identifier matching the module's domain.
        Examples: "orders", "inventory", "customers", "billing"

        Returns:
            Category string used for grouping metrics
        """
        ...

    def get_store_metrics(
        self,
        db: "Session",
        store_id: int,
        context: MetricsContext | None = None,
    ) -> list[MetricValue]:
        """
        Get metrics for a specific store.

        Called by the store dashboard to display store-scoped statistics.
        Should only include data belonging to the specified store.

        Args:
            db: Database session for queries
            store_id: ID of the store to get metrics for
            context: Optional filtering/scoping context

        Returns:
            List of MetricValue objects for this store
        """
        ...

    def get_platform_metrics(
        self,
        db: "Session",
        platform_id: int,
        context: MetricsContext | None = None,
    ) -> list[MetricValue]:
        """
        Get metrics aggregated for a platform.

        Called by the admin dashboard to display platform-wide statistics.
        Should aggregate data across all stores in the platform.

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

        Returns:
            List of MetricValue objects aggregated for the platform
        """
        ...

    def get_merchant_metrics(
        self,
        db: "Session",
        merchant_id: int,
        context: MetricsContext | None = None,
    ) -> list[MetricValue]:
        """
        Get metrics scoped to a specific merchant.

        Called by the merchant dashboard to display merchant-scoped statistics.
        Should only include data belonging to the specified merchant.

        Args:
            db: Database session for queries
            merchant_id: ID of the merchant to get metrics for
            context: Optional filtering/scoping context

        Returns:
            List of MetricValue objects for this merchant
        """
        ...


__all__ = [
    "MetricValue",
    "MetricsContext",
    "MetricsProviderProtocol",
]
