# app/modules/tenancy/services/permission_discovery_service.py
"""
Permission Discovery Service - Discovers and aggregates permissions from all modules.

This service implements the module-driven permission system where each module
defines its own permissions through PermissionDefinition in its definition.py file.

Key Features:
- Discovers permission definitions from all loaded modules
- Groups permissions by category for UI organization
- Provides role preset mappings (owner, manager, staff, etc.)
- Supports permission checking and validation

Usage:
    from app.modules.tenancy.services.permission_discovery_service import (
        permission_discovery_service
    )

    # Get all permissions
    all_perms = permission_discovery_service.get_all_permissions()

    # Get permissions grouped by category
    grouped = permission_discovery_service.get_permissions_by_category()

    # Get permission IDs for a role preset
    staff_perms = permission_discovery_service.get_preset_permissions("staff")

    # Check if a permission ID is valid
    is_valid = permission_discovery_service.is_valid_permission("products.view")
"""

import logging
from dataclasses import dataclass, field

logger = logging.getLogger(__name__)


@dataclass
class DiscoveredPermission:
    """
    A permission discovered from a module, enriched with module info.

    Extends PermissionDefinition with module context for tracking
    which module provides each permission.
    """

    id: str
    label_key: str
    description_key: str
    category: str
    is_owner_only: bool
    module_code: str


@dataclass
class PermissionCategory:
    """
    A category grouping related permissions for UI organization.
    """

    id: str
    label_key: str
    permissions: list[DiscoveredPermission] = field(default_factory=list)


class PermissionDiscoveryService:
    """
    Service to discover and aggregate permissions from all modules.

    This service:
    1. Collects permission definitions from all module definition.py files
    2. Groups permissions by category for UI display
    3. Provides role preset definitions (owner, manager, staff, etc.)
    4. Validates permission IDs
    """

    # =========================================================================
    # Role Presets
    # =========================================================================
    # Role presets define default permission sets. These reference permission IDs
    # that should be defined by modules. The service validates these at runtime.

    ROLE_PRESETS = {
        "owner": None,  # Special: owners get ALL permissions
        "manager": {
            # Dashboard
            "dashboard.view",
            # Products
            "products.view",
            "products.create",
            "products.edit",
            "products.delete",
            "products.import",
            "products.export",
            # Stock/Inventory
            "stock.view",
            "stock.edit",
            "stock.transfer",
            # Orders
            "orders.view",
            "orders.edit",
            "orders.cancel",
            "orders.refund",
            # Customers
            "customers.view",
            "customers.edit",
            "customers.export",
            # Marketing
            "marketing.view",
            "marketing.create",
            "marketing.send",
            # Reports
            "reports.view",
            "reports.financial",
            "reports.export",
            # Settings (limited)
            "settings.view",
            "settings.theme",
            # Team (view only)
            "team.view",
            # Imports
            "imports.view",
            "imports.create",
            "imports.cancel",
        },
        "staff": {
            # Dashboard
            "dashboard.view",
            # Products
            "products.view",
            "products.create",
            "products.edit",
            # Stock
            "stock.view",
            "stock.edit",
            # Orders
            "orders.view",
            "orders.edit",
            # Customers
            "customers.view",
            "customers.edit",
        },
        "support": {
            # Dashboard
            "dashboard.view",
            # Products (view only)
            "products.view",
            # Orders
            "orders.view",
            "orders.edit",
            # Customers
            "customers.view",
            "customers.edit",
        },
        "viewer": {
            # Read-only access
            "dashboard.view",
            "products.view",
            "stock.view",
            "orders.view",
            "customers.view",
            "reports.view",
        },
        "marketing": {
            # Marketing-focused role
            "dashboard.view",
            "customers.view",
            "customers.export",
            "marketing.view",
            "marketing.create",
            "marketing.send",
            "reports.view",
        },
    }

    # Category labels for UI grouping
    CATEGORY_LABELS = {
        "dashboard": "tenancy.permissions.category.dashboard",
        "products": "tenancy.permissions.category.products",
        "stock": "tenancy.permissions.category.stock",
        "orders": "tenancy.permissions.category.orders",
        "customers": "tenancy.permissions.category.customers",
        "marketing": "tenancy.permissions.category.marketing",
        "reports": "tenancy.permissions.category.reports",
        "settings": "tenancy.permissions.category.settings",
        "team": "tenancy.permissions.category.team",
        "imports": "tenancy.permissions.category.imports",
        "general": "tenancy.permissions.category.general",
    }

    def get_all_permissions(self) -> list[DiscoveredPermission]:
        """
        Discover all permissions from all loaded modules.

        Returns:
            List of DiscoveredPermission objects from all modules
        """
        from app.modules.registry import MODULES

        permissions = []

        for module_code, module_def in MODULES.items():
            for perm in module_def.permissions:
                discovered = DiscoveredPermission(
                    id=perm.id,
                    label_key=perm.label_key,
                    description_key=perm.description_key,
                    category=perm.category,
                    is_owner_only=perm.is_owner_only,
                    module_code=module_code,
                )
                permissions.append(discovered)

        return sorted(permissions, key=lambda p: (p.category, p.id))

    def get_all_permission_ids(self) -> set[str]:
        """
        Get set of all permission IDs from all modules.

        Returns:
            Set of permission ID strings
        """
        from app.modules.registry import MODULES

        ids = set()
        for module_def in MODULES.values():
            ids.update(module_def.get_permission_ids())

        return ids

    def get_permission(self, permission_id: str) -> DiscoveredPermission | None:
        """
        Get a specific permission by ID.

        Args:
            permission_id: Permission ID to look up

        Returns:
            DiscoveredPermission if found, None otherwise
        """
        from app.modules.registry import MODULES

        for module_code, module_def in MODULES.items():
            for perm in module_def.permissions:
                if perm.id == permission_id:
                    return DiscoveredPermission(
                        id=perm.id,
                        label_key=perm.label_key,
                        description_key=perm.description_key,
                        category=perm.category,
                        is_owner_only=perm.is_owner_only,
                        module_code=module_code,
                    )

        return None

    def get_permissions_by_category(self) -> list[PermissionCategory]:
        """
        Get all permissions grouped by category.

        Returns:
            List of PermissionCategory objects with permissions
        """
        all_perms = self.get_all_permissions()

        # Group by category
        categories_map: dict[str, list[DiscoveredPermission]] = {}
        for perm in all_perms:
            if perm.category not in categories_map:
                categories_map[perm.category] = []
            categories_map[perm.category].append(perm)

        # Build category objects
        categories = []
        for cat_id, perms in sorted(categories_map.items()):
            label_key = self.CATEGORY_LABELS.get(
                cat_id, f"tenancy.permissions.category.{cat_id}"
            )
            categories.append(
                PermissionCategory(
                    id=cat_id,
                    label_key=label_key,
                    permissions=perms,
                )
            )

        return categories

    def get_permissions_for_module(
        self, module_code: str
    ) -> list[DiscoveredPermission]:
        """
        Get all permissions defined by a specific module.

        Args:
            module_code: Module code to get permissions for

        Returns:
            List of DiscoveredPermission from the specified module
        """
        from app.modules.registry import MODULES

        module_def = MODULES.get(module_code)
        if not module_def:
            return []

        return [
            DiscoveredPermission(
                id=perm.id,
                label_key=perm.label_key,
                description_key=perm.description_key,
                category=perm.category,
                is_owner_only=perm.is_owner_only,
                module_code=module_code,
            )
            for perm in module_def.permissions
        ]

    def is_valid_permission(self, permission_id: str) -> bool:
        """
        Check if a permission ID is valid (defined by some module).

        Args:
            permission_id: Permission ID to validate

        Returns:
            True if permission exists, False otherwise
        """
        return permission_id in self.get_all_permission_ids()

    def get_preset_permissions(self, preset_name: str) -> set[str]:
        """
        Get permission IDs for a role preset.

        Args:
            preset_name: Name of preset (owner, manager, staff, support, viewer, marketing)

        Returns:
            Set of permission IDs for the preset, or empty set if not found.
            For "owner", returns all permission IDs.
        """
        preset = self.ROLE_PRESETS.get(preset_name.lower())

        if preset_name.lower() == "owner":
            # Owners get all permissions
            return self.get_all_permission_ids()

        if preset is None:
            logger.warning(f"Unknown role preset: {preset_name}")
            return set()

        # Filter to only permissions that actually exist
        all_perms = self.get_all_permission_ids()
        return preset & all_perms

    def get_available_presets(self) -> list[str]:
        """
        Get list of available role preset names.

        Returns:
            List of preset names
        """
        return list(self.ROLE_PRESETS.keys())

    def validate_permissions(self, permission_ids: list[str]) -> list[str]:
        """
        Validate a list of permission IDs.

        Args:
            permission_ids: List of permission IDs to validate

        Returns:
            List of invalid permission IDs (empty if all valid)
        """
        all_perms = self.get_all_permission_ids()
        return [pid for pid in permission_ids if pid not in all_perms]

    def get_owner_only_permissions(self) -> set[str]:
        """
        Get permission IDs that are owner-only.

        These permissions cannot be assigned to team members.

        Returns:
            Set of owner-only permission IDs
        """
        from app.modules.registry import MODULES

        owner_only = set()
        for module_def in MODULES.values():
            for perm in module_def.permissions:
                if perm.is_owner_only:
                    owner_only.add(perm.id)

        return owner_only


# Singleton instance
permission_discovery_service = PermissionDiscoveryService()


__all__ = [
    "permission_discovery_service",
    "PermissionDiscoveryService",
    "DiscoveredPermission",
    "PermissionCategory",
]
