"""Shared helpers for Form.io schema parsing and component simplification.

These helpers are used by both regular forms (forms.py) and print documents
(print_documents.py). They handle the common patterns of parsing formSchema
from BPA API responses and simplifying component trees for display.
"""

from __future__ import annotations

import json
from typing import Any

from mcp_eregistrations_bpa.tools.formio_helpers import CONTAINER_TYPES

__all__ = [
    "parse_form_schema",
    "simplify_components",
    "build_registration_name_map",
    "resolve_registration_uuids",
]


def parse_form_schema(form_data: dict[str, Any]) -> dict[str, Any]:
    """Parse formSchema from form data, handling string JSON.

    BPA stores formSchema as a JSON string in a TEXT column. This function
    handles both string and dict formats.

    Args:
        form_data: Form data from BPA API containing 'formSchema' key.

    Returns:
        Parsed form schema dict.
    """
    form_schema = form_data.get("formSchema", {})
    if isinstance(form_schema, str):
        try:
            parsed = json.loads(form_schema)
            return parsed if isinstance(parsed, dict) else {}
        except json.JSONDecodeError:
            return {}
    return form_schema if isinstance(form_schema, dict) else {}


def build_registration_name_map(
    registrations: list[dict[str, Any]] | None,
) -> dict[str, str | None]:
    """Build UUID to name mapping from registrations list.

    Args:
        registrations: List of registration objects with 'id' and 'name' fields.

    Returns:
        Dict mapping registration UUIDs to their names.
    """
    if not registrations or not isinstance(registrations, list):
        return {}
    return {
        str(reg.get("id")): reg.get("name") for reg in registrations if reg.get("id")
    }


def resolve_registration_uuids(
    registrations: dict[str, Any] | None,
    name_map: dict[str, str | None],
) -> dict[str, str | None] | None:
    """Resolve registration UUIDs to names.

    Transforms registrations from {uuid: true} format to {uuid: "name"} format.
    If a UUID cannot be resolved, the value is set to null.

    Args:
        registrations: Component registrations dict ({uuid: true, ...}).
        name_map: UUID to name mapping.

    Returns:
        Dict with UUIDs as keys and registration names as values.
        Returns None if registrations is None or empty.
    """
    if not registrations or not isinstance(registrations, dict):
        return None

    resolved: dict[str, str | None] = {}
    for uuid in registrations:
        resolved[uuid] = name_map.get(uuid)
    return resolved


def simplify_components(
    components: list[dict[str, Any]],
    path: list[str] | None = None,
    registration_name_map: dict[str, str | None] | None = None,
    *,
    visible_only: bool = False,
    _ancestor_hidden: bool = False,
) -> list[dict[str, Any]]:
    """Simplify components for display.

    Recursively traverses the component tree, extracting key properties and
    flattening the hierarchy into a list with path information.

    Args:
        components: Raw Form.io components.
        path: Current nesting path.
        registration_name_map: Optional UUID-to-name map for registration resolution.

    Returns:
        Simplified component list.
    """
    # Handle non-list components (BPA API may return int or other types)
    if not isinstance(components, list):
        return []
    if path is None:
        path = []
    if registration_name_map is None:
        registration_name_map = {}

    result = []
    for comp in components:
        if not isinstance(comp, dict):
            continue

        key = comp.get("key")
        if not key:
            continue

        comp_type = comp.get("type", "unknown")
        simplified: dict[str, Any] = {
            "key": key,
            "type": comp_type,
        }

        if comp.get("label"):
            simplified["label"] = comp["label"]

        if path:
            simplified["path"] = path

        # Add validation info
        validate = comp.get("validate", {})
        if isinstance(validate, dict) and validate.get("required"):
            simplified["required"] = True

        # Add is_container flag for container types
        if comp_type in CONTAINER_TYPES:
            simplified["is_container"] = True

        # Surface hidden/disabled state (own or inherited from ancestor)
        is_hidden = bool(comp.get("hidden")) or _ancestor_hidden
        if is_hidden:
            simplified["hidden"] = True
        if comp.get("disabled"):
            simplified["disabled"] = True

        # Extract determinant_ids from Form.io component (always include, empty if none)
        determinant_ids = comp.get("determinantIds", [])
        if determinant_ids is None:
            determinant_ids = []
        elif not isinstance(determinant_ids, list):
            determinant_ids = [determinant_ids] if determinant_ids else []
        simplified["determinant_ids"] = determinant_ids

        # Resolve registration UUIDs to names
        raw_registrations = comp.get("registrations")
        if raw_registrations:
            resolved = resolve_registration_uuids(
                raw_registrations, registration_name_map
            )
            if resolved:
                simplified["registrations"] = resolved

        # Include component_action_id if present
        if comp.get("componentActionId"):
            simplified["component_action_id"] = comp["componentActionId"]

        # Expose catalog reference for select/resource components
        data = comp.get("data", {})
        if isinstance(data, dict):
            if data.get("catalog"):
                simplified["catalog"] = data["catalog"]
            if data.get("dataSrc"):
                simplified["data_source"] = data["dataSrc"]

        # Handle nested components (panels, fieldsets, editgrids, datagrids, etc.)
        children_count = 0
        nested = comp.get("components", [])
        if nested and isinstance(nested, list):
            children_count += len(nested)
            result.extend(
                simplify_components(
                    nested,
                    path + [key],
                    registration_name_map,
                    visible_only=visible_only,
                    _ancestor_hidden=is_hidden,
                )
            )

        # Handle columns (2-level: columns > cells > components)
        columns = comp.get("columns", [])
        if columns and isinstance(columns, list):
            for col in columns:
                if isinstance(col, dict):
                    col_comps = col.get("components", [])
                    if isinstance(col_comps, list):
                        children_count += len(col_comps)
                        result.extend(
                            simplify_components(
                                col_comps,
                                path + [key],
                                registration_name_map,
                                visible_only=visible_only,
                                _ancestor_hidden=is_hidden,
                            )
                        )

        # Handle table rows (rows[][] structure)
        rows = comp.get("rows", [])
        if rows and isinstance(rows, list):
            for row in rows:
                if isinstance(row, list):
                    for cell in row:
                        if isinstance(cell, dict):
                            cell_comps = cell.get("components", [])
                            if isinstance(cell_comps, list):
                                children_count += len(cell_comps)
                                result.extend(
                                    simplify_components(
                                        cell_comps,
                                        path + [key],
                                        registration_name_map,
                                        visible_only=visible_only,
                                        _ancestor_hidden=is_hidden,
                                    )
                                )

        # Add children_count for containers
        if children_count > 0:
            simplified["children_count"] = children_count

        # Filter out hidden components when visible_only is set
        if visible_only and is_hidden:
            continue

        result.append(simplified)

    return result
