"""Unit tests for MenuDiscoveryService."""

import pytest

from app.modules.core.services.menu_discovery_service import MenuDiscoveryService
from app.modules.enums import FrontendType


@pytest.mark.unit
@pytest.mark.core
class TestMenuDiscoveryService:
    """Test suite for MenuDiscoveryService."""

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

    def test_service_instantiation(self):
        """Service can be instantiated."""
        assert self.service is not None

    def test_discover_all_menus_includes_merchant(self):
        """discover_all_menus() includes FrontendType.MERCHANT entries."""
        menus = self.service.discover_all_menus()
        assert FrontendType.MERCHANT in menus
        merchant_sections = menus[FrontendType.MERCHANT]
        assert len(merchant_sections) > 0, "Expected at least one MERCHANT menu section"

    def test_merchant_menu_has_expected_section_ids(self):
        """MERCHANT menus contain main, billing, account, and loyalty sections."""
        menus = self.service.discover_all_menus()
        section_ids = {s.id for s in menus[FrontendType.MERCHANT]}
        assert "main" in section_ids
        assert "billing" in section_ids
        assert "account" in section_ids
        assert "loyalty" in section_ids

    def test_merchant_main_section_has_dashboard(self):
        """Main section contains the dashboard item."""
        menus = self.service.discover_all_menus()
        main_sections = [s for s in menus[FrontendType.MERCHANT] if s.id == "main"]
        assert len(main_sections) == 1
        item_ids = [i.id for i in main_sections[0].items]
        assert "dashboard" in item_ids

    def test_merchant_billing_section_items(self):
        """Billing section contains subscriptions and invoices."""
        menus = self.service.discover_all_menus()
        billing_sections = [s for s in menus[FrontendType.MERCHANT] if s.id == "billing"]
        assert len(billing_sections) == 1
        item_ids = [i.id for i in billing_sections[0].items]
        assert "subscriptions" in item_ids
        assert "invoices" in item_ids

    def test_merchant_account_section_items(self):
        """Account section contains stores and profile."""
        menus = self.service.discover_all_menus()
        account_sections = [s for s in menus[FrontendType.MERCHANT] if s.id == "account"]
        assert len(account_sections) == 1
        item_ids = [i.id for i in account_sections[0].items]
        assert "stores" in item_ids
        assert "profile" in item_ids

    def test_merchant_loyalty_section_items(self):
        """Loyalty section contains program."""
        menus = self.service.discover_all_menus()
        loyalty_sections = [s for s in menus[FrontendType.MERCHANT] if s.id == "loyalty"]
        assert len(loyalty_sections) == 1
        item_ids = [i.id for i in loyalty_sections[0].items]
        assert "program" in item_ids

    def test_get_all_menu_items_merchant(self):
        """get_all_menu_items returns items for MERCHANT frontend type."""
        items = self.service.get_all_menu_items(FrontendType.MERCHANT)
        assert len(items) > 0
        item_ids = {i.id for i in items}
        assert "dashboard" in item_ids
        assert "subscriptions" in item_ids
        assert "program" in item_ids

    def test_get_all_menu_item_ids_merchant(self):
        """get_all_menu_item_ids returns IDs for MERCHANT frontend type."""
        item_ids = self.service.get_all_menu_item_ids(FrontendType.MERCHANT)
        assert "dashboard" in item_ids
        assert "subscriptions" in item_ids
        assert "invoices" in item_ids
        assert "stores" in item_ids
        assert "profile" in item_ids
        assert "program" in item_ids

    def test_get_mandatory_item_ids_merchant(self):
        """Mandatory items for MERCHANT include dashboard and subscriptions."""
        mandatory = self.service.get_mandatory_item_ids(FrontendType.MERCHANT)
        assert "dashboard" in mandatory
        assert "subscriptions" in mandatory

    def test_merchant_sections_have_expected_orders(self):
        """MERCHANT menu sections have the expected order values."""
        menus = self.service.discover_all_menus()
        sections = menus[FrontendType.MERCHANT]
        order_map = {s.id: s.order for s in sections}
        # main is lowest, account is highest
        assert order_map["main"] < order_map["billing"]
        assert order_map["billing"] < order_map["loyalty"]
        assert order_map["loyalty"] < order_map["account"]


@pytest.mark.unit
@pytest.mark.core
class TestMenuDiscoveryServiceEnabledModuleCodes:
    """Test enabled_module_codes parameter on get_menu_sections_for_frontend."""

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

    def test_all_modules_enabled_shows_all_sections(self, db):
        """When all module codes are provided, all sections appear."""
        enabled = {"core", "billing", "tenancy", "loyalty"}
        sections = self.service.get_menu_sections_for_frontend(
            db, FrontendType.MERCHANT, enabled_module_codes=enabled,
        )
        section_ids = {s.id for s in sections}
        assert "main" in section_ids
        assert "billing" in section_ids
        assert "account" in section_ids
        assert "loyalty" in section_ids

    def test_loyalty_disabled_hides_loyalty_section(self, db):
        """When loyalty is not in enabled_module_codes, its section still appears
        but items are marked as disabled (filtered out by get_menu_for_frontend)."""
        enabled = {"core", "billing", "tenancy"}
        sections = self.service.get_menu_sections_for_frontend(
            db, FrontendType.MERCHANT, enabled_module_codes=enabled,
        )
        # Sections still exist, but loyalty items are marked disabled
        loyalty_sections = [s for s in sections if s.id == "loyalty"]
        if loyalty_sections:
            for item in loyalty_sections[0].items:
                assert item.is_module_enabled is False

    def test_get_menu_for_frontend_filters_disabled_modules(self, db):
        """get_menu_for_frontend removes items from disabled modules."""
        enabled = {"core", "billing", "tenancy"}  # No loyalty
        sections = self.service.get_menu_for_frontend(
            db, FrontendType.MERCHANT, enabled_module_codes=enabled,
        )
        section_ids = {s.id for s in sections}
        assert "main" in section_ids
        assert "billing" in section_ids
        assert "account" in section_ids
        assert "loyalty" not in section_ids, "Loyalty section should be filtered out"

    def test_get_menu_for_frontend_includes_loyalty_when_enabled(self, db):
        """get_menu_for_frontend includes loyalty when its module is enabled."""
        enabled = {"core", "billing", "tenancy", "loyalty"}
        sections = self.service.get_menu_for_frontend(
            db, FrontendType.MERCHANT, enabled_module_codes=enabled,
        )
        section_ids = {s.id for s in sections}
        assert "loyalty" in section_ids

    def test_core_only_shows_minimal_sections(self, db):
        """With only core modules, only main section appears."""
        enabled = {"core"}
        sections = self.service.get_menu_for_frontend(
            db, FrontendType.MERCHANT, enabled_module_codes=enabled,
        )
        section_ids = {s.id for s in sections}
        assert "main" in section_ids
        assert "billing" not in section_ids
        assert "loyalty" not in section_ids

    def test_enabled_module_codes_none_shows_all(self, db):
        """When enabled_module_codes is None and no platform_id, all modules shown."""
        sections = self.service.get_menu_for_frontend(
            db, FrontendType.MERCHANT, enabled_module_codes=None,
        )
        section_ids = {s.id for s in sections}
        assert "main" in section_ids
        assert "billing" in section_ids
        assert "loyalty" in section_ids
        assert "account" in section_ids


@pytest.mark.unit
@pytest.mark.core
class TestMenuDiscoveryPermissionFiltering:
    """Test user_permissions parameter on get_menu_for_frontend."""

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

    def test_no_permission_filter_shows_all_items(self, db):
        """When user_permissions is None, no permission filtering occurs."""
        enabled = {"core", "catalog", "orders", "tenancy", "billing", "loyalty"}
        sections = self.service.get_menu_for_frontend(
            db, FrontendType.STORE, enabled_module_codes=enabled,
            user_permissions=None,
        )
        # Should have items regardless of requires_permission
        all_items = [item for s in sections for item in s.items]
        assert len(all_items) > 0

    def test_permission_filter_hides_unpermitted_items(self, db):
        """Items with requires_permission not in user_permissions are hidden."""
        enabled = {"core", "catalog", "orders", "tenancy", "billing", "loyalty"}
        # Only allow dashboard.view — should hide products, orders, etc.
        sections = self.service.get_menu_for_frontend(
            db, FrontendType.STORE, enabled_module_codes=enabled,
            user_permissions=["dashboard.view"],
        )
        all_item_ids = {item.id for s in sections for item in s.items}
        # Dashboard should be visible (has requires_permission="dashboard.view")
        assert "dashboard" in all_item_ids
        # Products requires "products.view" — should be hidden
        assert "products" not in all_item_ids

    def test_permission_filter_shows_permitted_items(self, db):
        """Items with matching permission are shown."""
        enabled = {"core", "catalog", "orders", "tenancy"}
        sections = self.service.get_menu_for_frontend(
            db, FrontendType.STORE, enabled_module_codes=enabled,
            user_permissions=["dashboard.view", "products.view", "orders.view", "team.view"],
        )
        all_item_ids = {item.id for s in sections for item in s.items}
        assert "dashboard" in all_item_ids
        assert "products" in all_item_ids
        assert "orders" in all_item_ids

    def test_empty_permissions_list_hides_permission_required_items(self, db):
        """Empty permissions list hides all items that require a permission."""
        enabled = {"core", "catalog"}
        sections = self.service.get_menu_for_frontend(
            db, FrontendType.STORE, enabled_module_codes=enabled,
            user_permissions=[],
        )
        all_item_ids = {item.id for s in sections for item in s.items}
        # Items without requires_permission should still show
        # Items with requires_permission should be hidden
        assert "products" not in all_item_ids
        assert "dashboard" not in all_item_ids

    def test_items_without_requires_permission_always_visible(self, db):
        """Items that have no requires_permission are shown regardless of user_permissions."""
        enabled = {"core", "catalog", "tenancy"}
        # Get all store items to check which have no requires_permission
        all_items_raw = self.service.get_all_menu_items(FrontendType.STORE)
        items_without_perm = [
            item for item in all_items_raw if item.requires_permission is None
        ]

        if items_without_perm:
            sections = self.service.get_menu_for_frontend(
                db, FrontendType.STORE, enabled_module_codes=enabled,
                user_permissions=["some.random.permission"],
            )
            all_item_ids = {item.id for s in sections for item in s.items}
            # Items without requires_permission should still be visible
            for item in items_without_perm:
                if item.module_code in enabled:
                    assert item.id in all_item_ids
