"""RoleFormAdapter — fetch/save Form.io trees from BPA roles."""

from __future__ import annotations

import json
from copy import deepcopy
from typing import Any

from mcp_eregistrations_bpa.patch.adapters.base import FormAdapter


class RoleFormAdapter(FormAdapter):
    """Adapter for role forms accessed via GET/PUT /role endpoints.

    BPA API quirk: PUT goes to ``/role`` (no ID in URL).  The role ``id``
    must be present in the JSON body.
    """

    def __init__(self, client: Any) -> None:
        self._client = client
        self._envelope: dict[str, Any] | None = None
        self._target_id: str | None = None

    @property
    def envelope(self) -> dict[str, Any] | None:
        """Full role object stored after fetch(), used for round-trip save."""
        return self._envelope

    @property
    def label(self) -> str:
        if self._envelope is None:
            return "Role (unfetched)"
        name = self._envelope.get("name")
        if name:
            return f'Role "{name}"'
        return f"Role {self._target_id}"

    async def fetch(self, target_id: str) -> list[dict[str, Any]]:
        """Fetch role form components via GET /role/{role_id}.

        Handles formSchema returned as either a dict or a JSON string.

        Args:
            target_id: The role ID.

        Returns:
            List of Form.io components from the role's formSchema.
        """
        self._target_id = target_id
        data = await self._client.get(
            "/role/{role_id}",
            path_params={"role_id": target_id},
            resource_type="role",
            resource_id=target_id,
        )
        self._envelope = deepcopy(data)

        form_schema = data.get("formSchema")
        if form_schema is None:
            return []

        # BPA sometimes returns formSchema as a JSON string
        if isinstance(form_schema, str):
            form_schema = json.loads(form_schema)

        if not isinstance(form_schema, dict):
            return []

        components: list[dict[str, Any]] = form_schema.get("components", [])
        return components

    async def save(self, components: list[dict[str, Any]]) -> None:
        """Save modified components back via PUT /role.

        Re-embeds the components into the stored envelope and PUTs
        the full role object.  The role ID is in the JSON body, not
        in the URL (BPA API convention).

        Args:
            components: Modified Form.io component list.

        Raises:
            RuntimeError: If fetch() was not called first.
        """
        if self._envelope is None:
            raise RuntimeError(
                "Must call fetch() before save() — the adapter needs the "
                "role envelope for a round-trip PUT."
            )

        payload = deepcopy(self._envelope)

        # Ensure formSchema is a dict (it may have been stored as string)
        form_schema = payload.get("formSchema")
        if isinstance(form_schema, str):
            form_schema = json.loads(form_schema)
        if form_schema is None:
            form_schema = {}

        form_schema["components"] = components
        payload["formSchema"] = form_schema

        await self._client.put(
            "/role",
            json=payload,
            resource_type="role",
            resource_id=payload["id"],
        )
