"""Forms for the per-user API token self-service dashboard.

The dashboard is where humans mint their own opaque :class:`UserToken`
rows — used by MCP tool clients (Claude Desktop, Cline) on ``/mcp/``
and by the Daedalus REST integration on ``/library/api/*`` /
``/mcp_server/api/teams/*``. The plaintext is surfaced exactly once on
the "created" page and never retrievable again — see ``views.py``.

Two optional pickers ("Restrictions"; collapsed in the template) apply
only to the MCP surface:

* ``allowed_tools`` — multi-select over the FastMCP tool registry.
  Empty = all tools permitted. Backs ``UserToken.allowed_tools``.
* ``allowed_libraries`` — multi-select over Neo4j Libraries the
  current request user has ``owner`` or ``manager`` membership on.
  Empty = **zero** libraries (fail-closed) *for MCP callers*, matching
  the semantics in §4.1 of the integration design doc. On the REST
  surface these fields are ignored; ``Team.owner`` and
  ``Library.owner_username`` enforce access there.
"""

from __future__ import annotations

import asyncio
import functools

from django import forms

from .admin import _library_choices_for_user
from .models import UserToken


@functools.lru_cache(maxsize=1)
def _registered_tool_names() -> list[str]:
    """Pull the list of registered MCP tool names from the FastMCP instance.

    Cached at module level — the tool registry is fixed at server startup.
    Importing here (rather than at module import) avoids circulars when the
    server module imports the dashboard pieces transitively.
    """
    from .server import mcp

    tools = asyncio.run(mcp.get_tools())
    return sorted(tools.keys())


def _tool_choices() -> list[tuple[str, str]]:
    return [(name, name) for name in _registered_tool_names()]


class UserTokenCreateForm(forms.Form):
    """Generate a new bearer token. The token value itself is server-generated."""

    name = forms.CharField(
        max_length=100,
        widget=forms.TextInput(attrs={
            "class": "input input-bordered w-full",
            "placeholder": "e.g. Claude Desktop, CI script",
        }),
        help_text=(
            "A friendly label so you can identify this token later. It also "
            "labels any library the token creates (shown as “Managed by "
            "<name>”) — for an app integration, use the app's name, e.g. "
            "Daedalus, Kairos, Spelunker."
        ),
    )
    expires_at = forms.DateTimeField(
        required=False,
        widget=forms.DateTimeInput(attrs={
            "class": "input input-bordered w-full",
            "type": "datetime-local",
        }),
        help_text="Leave blank for no expiry.",
    )
    allowed_tools = forms.MultipleChoiceField(
        required=False,
        widget=forms.CheckboxSelectMultiple(attrs={"class": "checkbox checkbox-sm"}),
        help_text="Leave all unchecked to permit every tool.",
    )
    allowed_libraries = forms.MultipleChoiceField(
        required=False,
        widget=forms.CheckboxSelectMultiple(attrs={"class": "checkbox checkbox-sm"}),
        help_text=(
            "Libraries this token may read. Empty = zero libraries (fail-closed). "
            "You only see libraries where you hold owner/manager membership."
        ),
    )

    def __init__(self, *args, user=None, **kwargs):
        super().__init__(*args, **kwargs)
        self.fields["allowed_tools"].choices = _tool_choices()
        self.fields["allowed_libraries"].choices = _library_choices_for_user(user)


class UserTokenEditForm(forms.ModelForm):
    """Edit token metadata. The hashed token itself cannot be edited."""

    allowed_tools = forms.MultipleChoiceField(
        required=False,
        widget=forms.CheckboxSelectMultiple(attrs={"class": "checkbox checkbox-sm"}),
        help_text="Leave all unchecked to permit every tool.",
    )
    allowed_libraries = forms.MultipleChoiceField(
        required=False,
        widget=forms.CheckboxSelectMultiple(attrs={"class": "checkbox checkbox-sm"}),
        help_text=(
            "Libraries this token may read. Empty = zero libraries (fail-closed). "
            "You only see libraries where you hold owner/manager membership."
        ),
    )

    class Meta:
        model = UserToken
        fields = ["name", "is_active", "expires_at", "allowed_tools", "allowed_libraries"]
        widgets = {
            "name": forms.TextInput(attrs={"class": "input input-bordered w-full"}),
            "is_active": forms.CheckboxInput(attrs={"class": "toggle toggle-primary"}),
            "expires_at": forms.DateTimeInput(attrs={
                "class": "input input-bordered w-full",
                "type": "datetime-local",
            }),
        }

    def __init__(self, *args, user=None, **kwargs):
        super().__init__(*args, **kwargs)
        self.fields["allowed_tools"].choices = _tool_choices()
        self.fields["allowed_libraries"].choices = _library_choices_for_user(
            user or (self.instance.user if self.instance and self.instance.pk else None)
        )
        if self.instance and self.instance.pk:
            self.fields["allowed_tools"].initial = self.instance.allowed_tools or []
            self.fields["allowed_libraries"].initial = (
                list(self.instance.allowed_libraries or [])
            )

    def clean_allowed_libraries(self):
        value = self.cleaned_data.get("allowed_libraries") or []
        return list(value)
