# app/modules/core/tests/unit/test_stats_aggregator.py
"""Unit tests for StatsAggregatorService merchant methods."""

from unittest.mock import MagicMock, patch

import pytest

from app.modules.contracts.metrics import MetricValue
from app.modules.core.services.stats_aggregator import StatsAggregatorService


def _make_metric(key: str, value: int, category: str = "test") -> MetricValue:
    return MetricValue(key=key, value=value, label=key, category=category)


@pytest.mark.unit
@pytest.mark.core
class TestStatsAggregatorMerchant:
    """Tests for StatsAggregatorService merchant aggregation methods."""

    def setup_method(self):
        self.service = StatsAggregatorService()

    def test_get_merchant_dashboard_stats_aggregates_providers(self, db):
        """Aggregates metrics from providers that implement get_merchant_metrics."""
        provider_a = MagicMock()
        provider_a.metrics_category = "billing"
        provider_a.get_merchant_metrics.return_value = [
            _make_metric("billing.active_subscriptions", 2, "billing"),
        ]

        provider_b = MagicMock()
        provider_b.metrics_category = "tenancy"
        provider_b.get_merchant_metrics.return_value = [
            _make_metric("tenancy.total_stores", 3, "tenancy"),
        ]

        module_a = MagicMock()
        module_a.code = "billing"
        module_b = MagicMock()
        module_b.code = "tenancy"

        with patch.object(
            self.service, "_get_enabled_providers", return_value=[(module_a, provider_a), (module_b, provider_b)]
        ):
            result = self.service.get_merchant_dashboard_stats(db, merchant_id=1, platform_id=1)

        assert "billing" in result
        assert "tenancy" in result
        assert result["billing"][0].value == 2
        assert result["tenancy"][0].value == 3

    def test_get_merchant_dashboard_stats_skips_providers_without_method(self, db):
        """Skips providers that don't have get_merchant_metrics."""
        provider = MagicMock(spec=[])  # No attributes at all
        provider.metrics_category = "legacy"
        module = MagicMock()
        module.code = "legacy"

        with patch.object(
            self.service, "_get_enabled_providers", return_value=[(module, provider)]
        ):
            result = self.service.get_merchant_dashboard_stats(db, merchant_id=1, platform_id=1)

        assert result == {}

    def test_get_merchant_dashboard_stats_handles_provider_error(self, db):
        """Gracefully handles a provider raising an exception."""
        provider = MagicMock()
        provider.metrics_category = "broken"
        provider.get_merchant_metrics.side_effect = RuntimeError("DB error")
        module = MagicMock()
        module.code = "broken"

        with patch.object(
            self.service, "_get_enabled_providers", return_value=[(module, provider)]
        ):
            result = self.service.get_merchant_dashboard_stats(db, merchant_id=1, platform_id=1)

        assert result == {}

    def test_get_merchant_stats_flat(self, db):
        """Flattens categorized metrics into a single dict."""
        provider = MagicMock()
        provider.metrics_category = "tenancy"
        provider.get_merchant_metrics.return_value = [
            _make_metric("tenancy.total_stores", 5, "tenancy"),
            _make_metric("tenancy.team_members", 12, "tenancy"),
        ]
        module = MagicMock()
        module.code = "tenancy"

        with patch.object(
            self.service, "_get_enabled_providers", return_value=[(module, provider)]
        ):
            flat = self.service.get_merchant_stats_flat(db, merchant_id=1, platform_id=1)

        assert flat["tenancy.total_stores"] == 5
        assert flat["tenancy.team_members"] == 12

    def test_get_merchant_stats_flat_empty(self, db):
        """Returns empty dict when no providers have merchant metrics."""
        with patch.object(self.service, "_get_enabled_providers", return_value=[]):
            flat = self.service.get_merchant_stats_flat(db, merchant_id=1, platform_id=1)

        assert flat == {}
