"""ServiceFormAdapter — fetch/save Form.io trees from BPA service forms."""

from __future__ import annotations

import json
from copy import deepcopy
from typing import Any

from mcp_eregistrations_bpa.patch.adapters.base import FormAdapter

_FORM_CONFIGS: dict[str, dict[str, str]] = {
    "applicant": {
        "get": "/service/{id}/applicant-form",
        "put": "/applicant-form/{form_id}",
        "name": "Applicant Form",
    },
    "guide": {
        "get": "/service/{id}/guide-form",
        "put": "/guide-form/{form_id}",
        "name": "Guide Form",
    },
    "send-file": {
        "get": "/service/{id}/send-file-form",
        "put": "/send-file-form/{form_id}",
        "name": "Send File Form",
    },
    "payment": {
        "get": "/service/{id}/payment-form",
        "put": "/payment-form/{form_id}",
        "name": "Payment Form",
    },
}

VALID_FORM_TYPES = frozenset(_FORM_CONFIGS)


class ServiceFormAdapter(FormAdapter):
    """Adapter for service forms (applicant, guide, send-file, payment).

    Each form type has its own GET/PUT endpoints.  The GET path is scoped
    to a service, while the PUT path uses the form's own ID.
    """

    def __init__(
        self,
        client: Any,
        service_id: str | int,
        form_type: str = "applicant",
    ) -> None:
        if form_type not in _FORM_CONFIGS:
            raise ValueError(
                f"Invalid form_type {form_type!r}. "
                f"Must be one of: {', '.join(sorted(VALID_FORM_TYPES))}"
            )
        self._client = client
        self._service_id = str(service_id)
        self._form_type = form_type
        self._config = _FORM_CONFIGS[form_type]
        self._envelope: dict[str, Any] | None = None

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

    @property
    def label(self) -> str:
        return f"{self._config['name']} (service {self._service_id})"

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

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

        Args:
            target_id: Ignored for service forms (service_id is set in
                constructor), kept for interface compatibility.

        Returns:
            List of Form.io components from the form's formSchema.
        """
        data = await self._client.get(
            self._config["get"],
            path_params={"id": self._service_id},
            params={"reusable": "false"},
            resource_type="form",
            resource_id=self._service_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 /{form-type}/{form_id}.

        Re-embeds the components into the stored envelope and PUTs
        the full form object.

        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 "
                "form 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(
            self._config["put"],
            path_params={"form_id": payload["id"]},
            json=payload,
            resource_type="form",
            resource_id=payload["id"],
        )
