"""One-off backfill for ``LibraryMembership`` rows.

Phase 2 introduces ``LibraryMembership`` as the Postgres-side gate on
who may scope a Neo4j-resident Library into a token's
``allowed_libraries``. Any Library that existed *before* this migration
has no membership rows, so nobody can grant it.

Running this command assigns **owner** membership on every global
Library (``workspace_id IS NULL``) to the first active superuser. It
skips:

* Workspace-scoped Libraries (``workspace_id`` is not null). Those
  belong to a Daedalus workspace and will be reachable via team JWTs
  once Phase 4 wires up ``TeamWorkspaceAssignment``. Granting them to
  a superuser UserToken would silently widen the blast radius of a
  leaked token.
* Libraries that already have any :class:`LibraryMembership` row. We
  do not stack roles for idempotency.

Idempotent. Safe to run on every deploy.
"""

from __future__ import annotations

import logging

from django.contrib.auth import get_user_model
from django.core.management.base import BaseCommand, CommandError

from mcp_server.models import LibraryMembership

logger = logging.getLogger(__name__)


class Command(BaseCommand):
    help = (
        "Backfill LibraryMembership(role=owner) for every global Neo4j Library, "
        "assigning it to the first active superuser. Skips workspace-scoped "
        "Libraries and Libraries that already have any membership row."
    )

    def add_arguments(self, parser):
        parser.add_argument(
            "--user",
            dest="username",
            default=None,
            help=(
                "Username to grant ownership to. Defaults to the first active "
                "superuser, ordered by id ascending."
            ),
        )
        parser.add_argument(
            "--dry-run",
            action="store_true",
            help="Report what would be inserted, don't persist.",
        )

    def handle(self, *args, **options):
        User = get_user_model()

        username = options.get("username")
        if username:
            try:
                target = User.objects.get(username=username, is_active=True)
            except User.DoesNotExist as exc:
                raise CommandError(
                    f"No active user {username!r} found."
                ) from exc
        else:
            target = (
                User.objects
                .filter(is_superuser=True, is_active=True)
                .order_by("id")
                .first()
            )
            if target is None:
                raise CommandError(
                    "No active superuser to own libraries. "
                    "Pass --user=<username> or create a superuser first."
                )

        try:
            from library.models import Library
        except Exception as exc:  # pragma: no cover
            raise CommandError(
                f"Could not import library.models.Library (Neo4j unreachable?): {exc}"
            ) from exc

        libraries = list(
            Library.nodes.filter(workspace_id__isnull=True)
        )

        already_scoped = set(
            LibraryMembership.objects
            .values_list("library_uid", flat=True)
        )

        to_create = []
        skipped_workspace = 0
        skipped_existing = 0
        for lib in libraries:
            if not getattr(lib, "uid", None):
                continue
            if getattr(lib, "workspace_id", None):
                # Shouldn't happen — the .filter above should eliminate
                # them — but belt-and-braces against a misconfigured query.
                skipped_workspace += 1
                continue
            if lib.uid in already_scoped:
                skipped_existing += 1
                continue
            to_create.append(
                LibraryMembership(
                    user=target,
                    library_uid=lib.uid,
                    role=LibraryMembership.Role.OWNER,
                )
            )

        self.stdout.write(
            self.style.HTTP_INFO(
                f"Target user: {target.username} (id={target.id})"
            )
        )
        self.stdout.write(
            f"Global libraries found: {len(libraries)}"
        )
        self.stdout.write(
            f"  already have a membership: {skipped_existing}"
        )
        self.stdout.write(
            f"  workspace-scoped (skipped):  {skipped_workspace}"
        )
        self.stdout.write(
            self.style.SUCCESS(f"Will insert: {len(to_create)}")
        )

        if options["dry_run"]:
            self.stdout.write(self.style.WARNING("--dry-run: no rows inserted."))
            for m in to_create:
                self.stdout.write(f"  would insert: {m.library_uid} → {target.username}")
            return

        if to_create:
            LibraryMembership.objects.bulk_create(to_create, ignore_conflicts=True)
            logger.info(
                "backfill_library_memberships inserted=%d user=%s",
                len(to_create), target.username,
            )
        self.stdout.write(self.style.SUCCESS("Done."))
