# tests/unit/modules/test_module_definitions.py
"""
Unit tests for module definitions.

Tests cover:
- Module definition completeness (MOD-020)
- Menu/feature consistency (MOD-021)
- Permission coverage (MOD-022)
- Router pattern consistency (MOD-023)
"""

import pytest

from app.modules.base import PermissionDefinition
from app.modules.registry import MODULES


@pytest.mark.unit
class TestModuleDefinitionCompleteness:
    """Test MOD-020: Module definitions have required attributes."""

    def test_all_modules_have_code(self):
        """Test that all modules have a code defined."""
        for code, module in MODULES.items():
            assert module.code, f"Module {code} missing 'code' attribute"
            assert module.code == code, f"Module code mismatch: {module.code} != {code}"

    def test_all_modules_have_name(self):
        """Test that all modules have a name defined."""
        for code, module in MODULES.items():
            assert module.name, f"Module {code} missing 'name' attribute"

    def test_all_modules_have_description(self):
        """Test that all modules have a description defined."""
        for code, module in MODULES.items():
            assert module.description, f"Module {code} missing 'description' attribute"

    def test_all_modules_have_version(self):
        """Test that all modules have a version defined."""
        for code, module in MODULES.items():
            assert module.version, f"Module {code} missing 'version' attribute"
            # Version should be semantic versioning format
            parts = module.version.split(".")
            assert len(parts) >= 2, f"Module {code} has invalid version format: {module.version}"

    def test_all_modules_have_features(self):
        """Test that all modules have features defined."""
        for code, module in MODULES.items():
            assert module.features is not None, f"Module {code} missing 'features' attribute"
            assert isinstance(module.features, list), f"Module {code} features should be a list"
            # All modules should have at least one feature
            assert len(module.features) > 0, f"Module {code} has empty features list"


@pytest.mark.unit
class TestModulePermissions:
    """Test MOD-022: Feature modules should have permissions."""

    # Modules exempt from permission requirements
    EXEMPT_MODULES = {
        "contracts",  # Infrastructure module
        "dev-tools",  # Internal module (is_internal=True)
        "monitoring",  # Internal module (is_internal=True)
    }

    def test_feature_modules_have_permissions(self):
        """Test that modules with features have permissions (unless exempt)."""
        for code, module in MODULES.items():
            if code in self.EXEMPT_MODULES:
                continue

            # If module has features, it should have permissions
            if module.features:
                assert module.permissions is not None, (
                    f"Module {code} has features but no permissions"
                )
                assert len(module.permissions) > 0, (
                    f"Module {code} has features but empty permissions list"
                )

    def test_internal_modules_exempt_from_permissions(self):
        """Test that internal modules don't require permissions."""
        internal_modules = [m for m in MODULES.values() if getattr(m, "is_internal", False)]
        # Internal modules exist
        assert len(internal_modules) > 0, "No internal modules found"
        # They may or may not have permissions (exempt from requirement)

    def test_permission_structure(self):
        """Test that permissions have required fields."""
        for code, module in MODULES.items():
            if not module.permissions:
                continue

            for perm in module.permissions:
                assert isinstance(perm, PermissionDefinition), (
                    f"Module {code} permission is not a PermissionDefinition"
                )
                assert perm.id, f"Module {code} has permission without id"
                assert perm.label_key, f"Module {code} permission {perm.id} missing label_key"
                assert perm.description_key, (
                    f"Module {code} permission {perm.id} missing description_key"
                )
                assert perm.category, f"Module {code} permission {perm.id} missing category"

    def test_permission_ids_follow_convention(self):
        """Test that permission IDs follow the module.action convention."""
        for code, module in MODULES.items():
            if not module.permissions:
                continue

            for perm in module.permissions:
                # Permission ID should contain a dot
                assert "." in perm.id, (
                    f"Module {code} permission {perm.id} should follow 'module.action' format"
                )

    def test_permission_counts(self):
        """Test that modules have expected permission counts."""
        # Modules that should have permissions
        modules_with_permissions = {
            code: module
            for code, module in MODULES.items()
            if module.permissions
        }

        # At least 15 modules should have permissions
        assert len(modules_with_permissions) >= 15, (
            f"Expected at least 15 modules with permissions, got {len(modules_with_permissions)}"
        )


@pytest.mark.unit
class TestModuleMenuFeatureConsistency:
    """Test MOD-021: Modules with menus should have features."""

    def test_modules_with_menus_have_features(self):
        """Test that modules with menu definitions have features."""
        for code, module in MODULES.items():
            has_menus = getattr(module, "menus", None) and len(module.menus) > 0
            has_menu_items = (
                getattr(module, "menu_items", None) and
                any(items for items in module.menu_items.values())
            )

            if has_menus or has_menu_items:
                assert module.features and len(module.features) > 0, (
                    f"Module {code} has menus but no features defined"
                )


@pytest.mark.unit
class TestModuleRouterPattern:
    """Test MOD-023: Router pattern consistency."""

    def test_modules_with_routers_attribute(self):
        """Test that modules can have router attributes attached."""
        # After calling get_*_with_routers(), modules should have router attrs
        # This test just verifies the attribute exists (may be None)
        for _code, module in MODULES.items():
            # These are optional attributes set by get_*_with_routers()
            # Just verify they can be accessed without error
            _ = getattr(module, "admin_router", None)
            _ = getattr(module, "store_router", None)
            _ = getattr(module, "platform_router", None)


@pytest.mark.unit
class TestSpecificModulePermissions:
    """Test specific modules have their expected permissions."""

    def test_cart_module_permissions(self):
        """Test cart module has required permissions."""
        module = MODULES.get("cart")
        assert module is not None
        assert module.permissions is not None

        perm_ids = {p.id for p in module.permissions}
        assert "cart.view" in perm_ids
        assert "cart.manage" in perm_ids

    def test_checkout_module_permissions(self):
        """Test checkout module has required permissions."""
        module = MODULES.get("checkout")
        assert module is not None
        assert module.permissions is not None

        perm_ids = {p.id for p in module.permissions}
        assert "checkout.view_settings" in perm_ids
        assert "checkout.manage_settings" in perm_ids

    def test_analytics_module_permissions(self):
        """Test analytics module has required permissions."""
        module = MODULES.get("analytics")
        assert module is not None
        assert module.permissions is not None

        perm_ids = {p.id for p in module.permissions}
        assert "analytics.view" in perm_ids
        assert "analytics.export" in perm_ids
        assert "analytics.manage_dashboards" in perm_ids

    def test_billing_module_permissions(self):
        """Test billing module has required permissions."""
        module = MODULES.get("billing")
        assert module is not None
        assert module.permissions is not None

        perm_ids = {p.id for p in module.permissions}
        assert "billing.view_tiers" in perm_ids
        assert "billing.manage_tiers" in perm_ids
        assert "billing.view_subscriptions" in perm_ids
        assert "billing.manage_subscriptions" in perm_ids
        assert "billing.view_invoices" in perm_ids

    def test_cms_module_permissions(self):
        """Test cms module has required permissions."""
        module = MODULES.get("cms")
        assert module is not None
        assert module.permissions is not None

        perm_ids = {p.id for p in module.permissions}
        assert "cms.view_pages" in perm_ids
        assert "cms.manage_pages" in perm_ids
        assert "cms.view_media" in perm_ids
        assert "cms.manage_media" in perm_ids
        assert "cms.manage_themes" in perm_ids

    def test_loyalty_module_permissions(self):
        """Test loyalty module has required permissions."""
        module = MODULES.get("loyalty")
        assert module is not None
        assert module.permissions is not None

        perm_ids = {p.id for p in module.permissions}
        assert "loyalty.view_programs" in perm_ids
        assert "loyalty.manage_programs" in perm_ids
        assert "loyalty.view_rewards" in perm_ids
        assert "loyalty.manage_rewards" in perm_ids

    def test_marketplace_module_permissions(self):
        """Test marketplace module has required permissions."""
        module = MODULES.get("marketplace")
        assert module is not None
        assert module.permissions is not None

        perm_ids = {p.id for p in module.permissions}
        assert "marketplace.view_integration" in perm_ids
        assert "marketplace.manage_integration" in perm_ids
        assert "marketplace.sync_products" in perm_ids

    def test_messaging_module_permissions(self):
        """Test messaging module has required permissions."""
        module = MODULES.get("messaging")
        assert module is not None
        assert module.permissions is not None

        perm_ids = {p.id for p in module.permissions}
        assert "messaging.view_messages" in perm_ids
        assert "messaging.send_messages" in perm_ids
        assert "messaging.manage_templates" in perm_ids

    def test_payments_module_permissions(self):
        """Test payments module has required permissions."""
        module = MODULES.get("payments")
        assert module is not None
        assert module.permissions is not None

        perm_ids = {p.id for p in module.permissions}
        assert "payments.view_gateways" in perm_ids
        assert "payments.manage_gateways" in perm_ids
        assert "payments.view_transactions" in perm_ids


@pytest.mark.unit
class TestModuleFeatures:
    """Test module feature definitions."""

    def test_cart_module_features(self):
        """Test cart module has required features."""
        module = MODULES.get("cart")
        assert module is not None

        expected_features = {
            "cart_management",
            "cart_persistence",
            "cart_item_operations",
            "shipping_calculation",
            "promotion_application",
        }
        assert expected_features.issubset(set(module.features))

    def test_checkout_module_features(self):
        """Test checkout module has required features."""
        module = MODULES.get("checkout")
        assert module is not None

        expected_features = {
            "checkout_flow",
            "order_creation",
            "payment_processing",
            "checkout_validation",
            "guest_checkout",
        }
        assert expected_features.issubset(set(module.features))

    def test_catalog_module_features(self):
        """Test catalog module has required features."""
        module = MODULES.get("catalog")
        assert module is not None

        expected_features = {
            "product_catalog",
            "product_search",
        }
        assert expected_features.issubset(set(module.features))
