"""Seed the manager / staff / visitor system roles + ensure all menu permissions.

Idempotent (get_or_create + permissions.set). Safe to re-run. Complements the
0002_seed_rbac data migration: that one seeds the granular per-area roles
(ProjectManager, HRManager, ...); this command seeds the three broad access
tiers the app exposes to every user — manager, staff, visitor — and backfills
any newly-added domains (e.g. `meetings`) so the permission catalog stays whole.
"""

from django.core.management.base import BaseCommand
from django.db import models

from apps.core.authorization import registry
from apps.core.models import Permission, Role

# All menu domains, including ones added after 0002 (meetings).
DOMAINS = [
    ("project", "Project"),
    ("events", "Events"),
    ("meetings", "Meetings"),
    ("hr", "HR"),
    ("finance", "Finance"),
    ("procurements", "Procurements"),
    ("asset_management", "Asset Management"),
    ("administrations", "Administrations"),
    ("publications", "Publications"),
    ("disseminations", "Disseminations"),
    ("phonebook", "Phonebook"),
    ("tasks", "Tasks"),
    ("analytics", "Analytics"),
    ("donors", "Donors"),
]
BASE_ACTIONS = ["view", "create", "update", "delete"]
APPROVE_DOMAINS = {"project", "tasks", "finance", "procurements", "hr", "administrations"}

# The three broad access tiers. Patterns: "domain.*" = all actions, "domain.action".
ROLE_MATRIX = {
    "Manager": {
        "description": "Full access to every operational domain. Manages RBAC.",
        "patterns": ["*"],
    },
    "Staff": {
        "description": "Create + edit own records across operational domains; "
        "read-only on HR/finance/analytics.",
        "patterns": [
            "tasks.view", "tasks.create", "tasks.update",
            "project.view", "project.create", "project.update",
            "events.view", "events.create", "events.update",
            "meetings.view", "meetings.create", "meetings.update",
            "phonebook.view", "phonebook.create", "phonebook.update",
            "publications.view", "publications.create", "publications.update",
            "disseminations.view", "disseminations.create", "disseminations.update",
            "asset_management.view", "asset_management.create",
            "procurements.view", "procurements.create",
            "administrations.view", "administrations.create",
            "finance.view", "hr.view", "analytics.view",
        ],
    },
    "Visitor": {
        "description": "Read-only on collaboration domains + self-service "
        "administrations requests. No HR/finance/procurements/assets.",
        "patterns": [
            "tasks.view", "project.view", "events.view", "meetings.view",
            "phonebook.view", "publications.view", "disseminations.view",
            "administrations.view", "administrations.create",
        ],
    },
}


def _legacy_permissions():
    """Legacy v1 rows only. Wildcard tier roles must never absorb v2
    capability rows (plan: no wildcard capabilities, Manager != all caps)."""
    return Permission.objects.filter(
        models.Q(code__isnull=True) | models.Q(code__startswith="legacy.")
    )


def _resolve_permissions(patterns):
    if patterns == ["*"]:
        # Everything except rbac.manage — only Manager gets RBAC management,
        # and "*" already includes it, so keep it for Manager.
        return list(_legacy_permissions())
    ids = set()
    for p in patterns:
        domain, _, action = p.partition(".")
        if action == "*":
            ids.update(_legacy_permissions().filter(domain=domain).values_list("id", flat=True))
        else:
            ids.update(
                _legacy_permissions().filter(domain=domain, action=action).values_list("id", flat=True)
            )
    return list(Permission.objects.filter(id__in=ids))


class Command(BaseCommand):
    help = "Seed manager/staff/visitor system roles and ensure all menu permissions exist."

    def handle(self, *args, **options):
        # 1. Ensure permissions for every domain/action.
        created_perms = 0
        for slug, label in DOMAINS:
            actions = BASE_ACTIONS + (["approve"] if slug in APPROVE_DOMAINS else [])
            for action in actions:
                _, made = Permission.objects.get_or_create(
                    domain=slug, action=action,
                    defaults={"label": f"{label}: {action.title()}"},
                )
                created_perms += int(made)
        _, made = Permission.objects.get_or_create(
            domain="rbac", action="manage",
            defaults={"label": "RBAC: Manage roles and assignments"},
        )
        created_perms += int(made)

        # 2. RBAC v2 expand: give every legacy row a compatibility-only code
        # (hidden from the v2 role editor, never a final capability) and seed
        # the canonical capability registry as Permission rows keyed by code.
        backfilled = 0
        for perm in Permission.objects.filter(code__isnull=True):
            perm.code = f"legacy.{perm.domain}.{perm.action}"
            perm.save(update_fields=["code"])
            backfilled += 1

        seeded_caps = 0
        for definition in registry.all_capabilities():
            _, made = Permission.objects.update_or_create(
                code=definition.code,
                defaults={
                    "domain": definition.module,
                    "action": definition.verb,
                    "module": definition.module,
                    "resource": definition.resource,
                    "label": definition.label,
                    "description": definition.description,
                    "risk_level": definition.risk,
                },
            )
            seeded_caps += int(made)
        self.stdout.write(
            f"  v2 capabilities: {seeded_caps} new, "
            f"{backfilled} legacy rows backfilled with compatibility codes"
        )

        # 3. Upsert the three tier roles.
        for name, cfg in ROLE_MATRIX.items():
            role, _ = Role.objects.get_or_create(
                slug=name.lower(),
                defaults={"name": name, "description": cfg["description"], "is_system": True},
            )
            role.name = name
            role.description = cfg["description"]
            role.is_system = True
            role.save(update_fields=["name", "description", "is_system"])
            role.permissions.set(_resolve_permissions(cfg["patterns"]))
            self.stdout.write(f"  {name}: {role.permissions.count()} permissions")

        self.stdout.write(self.style.SUCCESS(
            f"RBAC seeded. {created_perms} new permissions; manager/staff/visitor roles ready."
        ))
