"""
Idempotently upsert the shared permission catalog (Modules, Features,
Permissions) from ``access_control.catalog``.

Usage::

    python manage.py seed_permissions          # create/update catalog
    python manage.py seed_permissions --prune   # also deactivate stale entries

Safe to run repeatedly and after editing ``catalog.py``.
"""
from django.core.management.base import BaseCommand
from django.db import transaction

from access_control.catalog import MODULES
from access_control.models import Module, Feature, Permission


class Command(BaseCommand):
    help = 'Seed/refresh the access-control permission catalog from catalog.py'

    def add_arguments(self, parser):
        parser.add_argument(
            '--prune', action='store_true',
            help='Deactivate catalog entries no longer present in catalog.py',
        )

    @transaction.atomic
    def handle(self, *args, **options):
        created = {'modules': 0, 'features': 0, 'permissions': 0}
        seen_modules, seen_features, seen_perms = set(), set(), set()

        for m_order, m in enumerate(MODULES):
            module, m_new = Module.objects.update_or_create(
                code=m['code'],
                defaults={
                    'name': m['name'],
                    'description': m.get('description', ''),
                    'casl_subject': m.get('casl_subject', ''),
                    'icon': m.get('icon', ''),
                    'order': m_order,
                    'is_active': True,
                },
            )
            created['modules'] += int(m_new)
            seen_modules.add(module.id)

            for f_order, f in enumerate(m['features']):
                feature, f_new = Feature.objects.update_or_create(
                    module=module, code=f['code'],
                    defaults={
                        'name': f['name'],
                        'description': f.get('description', ''),
                        'order': f_order,
                        'is_active': True,
                    },
                )
                created['features'] += int(f_new)
                seen_features.add(feature.id)

                for action in f['actions']:
                    codename = f"{m['code']}.{f['code']}.{action}"
                    perm, p_new = Permission.objects.update_or_create(
                        feature=feature, action=action,
                        defaults={
                            'codename': codename,
                            'name': f"{f['name']} - {action.title()}",
                            'is_active': True,
                        },
                    )
                    created['permissions'] += int(p_new)
                    seen_perms.add(perm.id)

        if options['prune']:
            stale_p = Permission.objects.exclude(id__in=seen_perms).update(is_active=False)
            stale_f = Feature.objects.exclude(id__in=seen_features).update(is_active=False)
            stale_m = Module.objects.exclude(id__in=seen_modules).update(is_active=False)
            self.stdout.write(self.style.WARNING(
                f'Deactivated stale: {stale_m} modules, {stale_f} features, {stale_p} permissions'))

        self.stdout.write(self.style.SUCCESS(
            'Catalog synced. New: '
            f"{created['modules']} modules, {created['features']} features, "
            f"{created['permissions']} permissions. "
            f"Totals: {Module.objects.count()} modules, "
            f"{Feature.objects.count()} features, "
            f"{Permission.objects.count()} permissions."))
