"""Mechanical profiles for decoded 1C form payloads (root marker 4)."""

from __future__ import annotations

import re
from typing import Any

from .child_records import collect_evidence, declared_child_records
from .payload import GUID_RE, collect_strings, root_signature, scalar
from .structured_metadata import get_by_path


BSL_ROUTINE_RE = re.compile(r"(?im)^\s*(?:&[^\r\n]+\s*)*(?:Асинх\s+)?(Процедура|Функция)\s+([A-Za-zА-Яа-яЁё_][\wА-Яа-яЁё]*)")
BSL_IDENTIFIER_RE = re.compile(r"^[A-Za-zА-Яа-яЁё_][\wА-Яа-яЁё]*$")
BSL_PATH_RE = re.compile(r"^[A-Za-zА-Яа-яЁё_][\wА-Яа-яЁё]*(?:\.[A-Za-zА-Яа-яЁё_][\wА-Яа-яЁё]*)*$")

FORM_EVENT_NAMES = {
    "01d80ddd-dce5-4db3-beb5-f63c97cb05b9": "OnEditEnd",
    "047d4d09-961c-4bdc-8519-eef10674c35b": "AfterWrite",
    "11707a99-4eb9-4373-bc8c-84891483a034": "Click",
    "1282f000-23b6-4887-87f4-9e8e79db3d32": "Selection",
    "14256303-d2b7-4a58-bfab-e77493d10a59": "EditTextChange",
    "178a97c4-0ffe-4fcc-93e6-505369939da5": "AutoComplete",
    "1960479b-4d89-4eba-8b39-0aa802020558": "StartChoice",
    "213d1900-dcad-4616-9f20-3f077156a40f": "AfterWriteAtServer",
    "2391e7b8-7235-45d7-ab7e-6ff3dc086396": "BeforeAddRow",
    "2988b2a5-c887-4928-94ae-5d0c9c31e999": "DetailProcessing",
    "2ccfdec5-583d-4eca-8319-e55de492665a": "BeforeDeleteRow",
    "3699f6a3-9a2a-4c82-a775-6ff4824a08ca": "NotificationProcessing",
    "390d5e4b-e732-4c88-8748-9e211a416984": "OnReadAtServer",
    "1952a54f-35ad-4928-902f-df212ab38ca3": "OnSaveDataInSettingsAtServer",
    "3c3da18f-fc18-4f77-8c2d-96c25bec40a5": "Selection",
    "3ccc650e-f631-4cae-8e33-3eaac610b5f9": "OnOpen",
    "526c501f-ed3f-4db4-8731-fd0324707501": "OnCurrentPageChange",
    "509eca20-d6e4-4fef-a0f8-3a6b44c64178": "URLProcessing",
    "60edb81d-887b-478e-94ee-7fef2b13393d": "OnActivateRow",
    "650da4af-3233-4ce0-a1ae-23f87a226eee": "DetailProcessing",
    "8a5894c9-d2ff-4c1d-b433-89cc352bbfbc": "BeforeWrite",
    "8f42e083-be92-4102-b1f0-fa58452c1a63": "BeforeWriteAtServer",
    "93dfba16-26db-46f8-acb5-4f92f50c855f": "NavigationProcessing",
    "9f2e5ddb-3492-4f5d-8f0d-416b8d1d5c5b": "OnCreateAtServer",
    "97365900-eadf-4dfd-a9aa-fbb9ecabd079": "OnGetDataAtServer",
    "9874537f-454c-40ae-83e9-3b9cefbc6d08": "Click",
    "ab930362-ff94-4dcb-ad16-188805d23e3c": "BeforeRowChange",
    "aeba313d-c467-44b3-b4a2-956340932c8f": "Creating",
    "ac5a9c5a-5f1d-4fc5-b88c-a187038c16d1": "Opening",
    "b3c10170-c5ff-4cba-b537-679e1c872b45": "OnStartEdit",
    "bf0ac0e1-bcbb-4dfe-8fc4-0b1923b461a6": "BeforeWriteAtServer",
    "b50dc41b-c15a-4ebe-a17f-d01e51c47de6": "Clearing",
    "c331eb1b-d32b-4533-844c-1276600b64e3": "TextEditEnd",
    "ca21cd18-35b2-4281-b5c8-016ecc8da8ac": "OnClose",
    "d710ea07-5c96-4c43-ab6e-e138d3653780": "URLProcessing",
    "de65638d-a806-4a76-bc10-f62bbc86e0e7": "AfterDeleteRow",
    "eba5f295-c611-4dd9-84b5-22911ad60c53": "Click",
    "e773807c-0c0c-4689-a093-231ddcd6409f": "BeforeLoadDataFromSettingsAtServer",
    "e73d6384-49d2-4885-a752-a674d6ff7742": "FillCheckProcessingAtServer",
    "70636369-514c-4662-977e-1c3976c9756c": "Tuning",
    "f228b12f-d892-4925-b338-695617357b32": "OnActivateCell",
    "f72043b8-2d79-414e-bc4e-3972fe9dbca1": "ChoiceProcessing",
    "fe115cc8-9e33-4684-a166-bd5136fe7a9f": "OnChange",
}

MARKER_NAMES = {
    "9": "AttributeOrCommand",
    "12": "ExtendedTooltip",
    "22": "ContainerItem",
    "31": "CommandBarButton",
    "34": "Button",
    "35": "InputField",
    "37": "InputField",
    "55": "DynamicListTable",
}

FORM_ITEM_TYPE_NAMES = {
    "0": "Командная панель",
    "1": "Подменю",
    "2": "Группа колонок",
    "3": "Страницы",
    "4": "Страница",
    "5": "Группа",
    "6": "Группа кнопок",
    "8": "Контекстное меню",
    "9": "Командная панель",
    "12": "Расширенная подсказка",
    "31": "Кнопка командной панели",
    "34": "Кнопка",
    "48": "Поле формы",
    "55": "Динамический список",
    "73": "Таблица формы",
}

FORM_TABLE_ADDITION_TYPE_NAMES = {
    "0": "SearchStringAddition",
    "1": "ViewStatusAddition",
    "2": "SearchControlAddition",
}

FORM_FIELD_SUBTYPE_NAMES = {
    "1": "Поле надписи",
    "2": "Поле ввода",
    "3": "Поле флажка",
    "4": "PictureField",
    "5": "Поле переключателя",
    "6": "Поле табличного документа",
    "11": "ChartField",
    "14": "GraphicalSchemaField",
}

FORM_DECORATION_TYPE_NAMES = {
    "0": "Декорация надписи",
    "1": "Декорация картинки",
}

FORM_LOCAL_COMMAND_GROUP_GUID = "409b9a53-7f7e-4178-86c1-33176c7c7a7a"
FORM_STANDARD_COMMANDS = {
    ("198ea630-fda2-4cda-8a23-f999f4c67ee6", "0"): "Form.StandardCommand.CustomizeForm",
    ("39bb0fe9-771d-4dd5-8a6e-2d16984523af", "0"): "Form.StandardCommand.Help",
    ("fe558fde-99b3-45d0-a060-9fc2905309f6", "0"): "Form.StandardCommand.Write",
    ("1f317795-c420-4a30-b594-c492abc55f7a", "0"): "Form.StandardCommand.Reread",
    ("68baa1bc-edd1-4d9b-ad80-1d53fb8a7988", "0"): "Form.StandardCommand.Copy",
    ("827b541d-30c1-4f06-aecf-92aa496a0835", "0"): "Form.StandardCommand.SetDeletionMark",
    ("3a17e914-ec6a-4280-b4df-78914f40522b", "0"): "Form.StandardCommand.ShowInList",
    ("174e58ce-82ad-4787-b956-9367937f7971", "0"): "Form.StandardCommand.ChangeHistory",
    ("6886601d-276c-4d3f-af0a-05c586025608", "0"): "Form.StandardCommand.Change",
    ("bdefa701-6685-453e-a02a-3683d0cc16d3", "0"): "Form.StandardCommand.Find",
    ("96e0bc70-f8ff-4732-8119-060923203629", "0"): "Form.StandardCommand.CancelSearch",
}

FORM_GRAPHICAL_SCHEMA_STANDARD_COMMANDS = {
    ("e2d6f793-b786-4640-a91b-8d77f73860f1", "3"): "Print",
    ("1d13f9a3-402a-46cb-9c68-1709356840f2", "3"): "Preview",
    ("01db2225-b62d-4112-a4b6-d39d627bf79f", "3"): "PageSetup",
}

FORM_TABLE_STANDARD_COMMANDS = {
    "b0016a68-ec64-4e6d-b905-c71fd62efc4c": "Add",
    "0ae4bea5-23be-42a7-b69e-97b11b29c453": "Copy",
    "b41f5bbc-ba5d-4888-8cd1-db246a371418": "Change",
    "8d772f97-c0ef-47c0-9cb0-efea28c61341": "Delete",
    "9ef79140-3de6-436a-8dda-610bb963f5db": "EndEdit",
    "daa306cd-a78a-4e74-a14c-739daba624cb": "SetDateInterval",
    "c0519548-2a9a-44de-a25e-faf01e089d4d": "Find",
    "44ad3ec9-f3c2-4913-9224-5f9fb6418743": "CancelSearch",
    "88078230-1f6b-415f-99e4-ad2ff73810cf": "CopyToClipboard",
    "37740564-9e86-44a0-bea9-3f485a5a3f91": "MoveUp",
    "fa51b106-eae6-44c7-8054-76cbb3100603": "MoveDown",
    "2bbe4e12-06d2-409b-a972-eea585125d83": "SortListAsc",
    "58b2a785-23f6-4b0e-a324-9a1323285595": "SortListDesc",
    "49602716-fea6-497f-8047-726404038857": "OutputList",
}

FORM_OBJECT_COMMANDS = {
    ("0fa77ef7-a836-4459-9bca-6010d0bdfc7f", "0"): "Выполнено",
    ("dcff004c-1b61-4a19-b977-ea21bf688614", "0"): "Перенаправить",
}

FORM_STANDARD_DATA_FIELDS = {
    "-2": "Code",
    "-3": "Description",
    "-4": "Parent",
    "-5": "Ref",
}

FORM_PUBLIC_DATA_FIELD_NAMES = {
    "Код": "Code",
    "Номер": "Number",
    "Наименование": "Description",
    "Родитель": "Parent",
    "НомерСтроки": "LineNumber",
}

FORM_ITEM_MARKERS = {"6", "12", "22", "31", "34", "35", "37", "48", "55", "73"}

FORM_ITEM_PARAMETER_ROLES = {
    "6": {
        0: "Маркер дополнения таблицы",
        1: "Идентификатор",
        2: "Использование",
        3: "Подчинение",
        4: "Группа",
        5: "Вид дополнения",
        6: "Имя",
    },
    "12": {
        0: "Маркер расширенной подсказки",
        1: "Идентификатор",
        2: "Использование",
        3: "Подчинение",
        4: "Группа",
        6: "Имя",
    },
    "22": {
        0: "Маркер элемента",
        1: "Идентификатор",
        2: "Использование",
        3: "Подчинение",
        4: "Группа",
        5: "Вид элемента",
        6: "Имя",
        7: "Заголовок",
    },
    "31": {
        0: "Маркер кнопки командной панели",
        1: "Идентификатор",
        2: "Использование",
        3: "Подчинение",
        4: "Группа",
        5: "Имя",
        6: "Заголовок",
    },
    "34": {
        0: "Маркер кнопки",
        1: "Идентификатор",
        2: "Использование",
        3: "Подчинение",
        4: "Группа",
        5: "Вид кнопки",
        6: "Имя",
    },
    "35": {
        0: "Маркер поля ввода",
        1: "Идентификатор",
        2: "Использование",
        3: "Подчинение",
        4: "Группа",
        5: "Вид элемента",
        6: "Имя",
        9: "Заголовок",
        11: "Путь к данным",
    },
    "37": {
        0: "Маркер поля ввода",
        1: "Идентификатор",
        2: "Использование",
        3: "Подчинение",
        4: "Группа",
        5: "Вид элемента",
        6: "Имя",
        9: "Заголовок",
        11: "Путь к данным",
    },
    "48": {
        0: "Маркер поля формы",
        1: "Идентификатор",
        2: "Использование",
        3: "Подчинение",
        4: "Группа",
        5: "Вид элемента",
        6: "Имя",
        12: "Путь к данным",
    },
    "55": {
        0: "Маркер динамического списка",
        1: "Идентификатор",
        2: "Использование",
        3: "Подчинение",
        4: "Группа",
        5: "Имя",
        11: "Путь к данным",
    },
    "73": {
        0: "Маркер таблицы формы",
        1: "Идентификатор",
        2: "Использование",
        3: "Подчинение",
        4: "Группа",
        5: "Имя",
        12: "Путь к данным",
    },
}

SECTION_RECORD_PARAMETER_ROLES = {
    0: "Маркер записи",
    1: "Идентификатор",
    2: "Имя",
    3: "Заголовок",
    6: "Имя",
}

FORM_ITEM_SEMANTIC_PROPERTIES = {
    "6": {
        1: ("Основные", "Идентификатор"),
        2: ("Иерархия", "Использование"),
        3: ("Иерархия", "Подчинение"),
        4: ("Иерархия", "Группа"),
        5: ("Основные", "Вид"),
        6: ("Основные", "Имя"),
    },
    "12": {
        1: ("Основные", "Идентификатор"),
        2: ("Иерархия", "Использование"),
        3: ("Иерархия", "Подчинение"),
        4: ("Иерархия", "Группа"),
        6: ("Основные", "Имя"),
    },
    "22": {
        1: ("Основные", "Идентификатор"),
        2: ("Иерархия", "Использование"),
        3: ("Иерархия", "Подчинение"),
        4: ("Иерархия", "Группа"),
        5: ("Основные", "Вид"),
        6: ("Основные", "Имя"),
        7: ("Основные", "Заголовок"),
    },
    "31": {
        1: ("Основные", "Идентификатор"),
        2: ("Иерархия", "Использование"),
        3: ("Иерархия", "Подчинение"),
        4: ("Иерархия", "Группа"),
        5: ("Основные", "Имя"),
        6: ("Основные", "Заголовок"),
    },
    "34": {
        1: ("Основные", "Идентификатор"),
        2: ("Иерархия", "Использование"),
        3: ("Иерархия", "Подчинение"),
        4: ("Иерархия", "Группа"),
        5: ("Основные", "Вид"),
        6: ("Основные", "Имя"),
    },
    "35": {
        1: ("Основные", "Идентификатор"),
        2: ("Иерархия", "Использование"),
        3: ("Иерархия", "Подчинение"),
        4: ("Иерархия", "Группа"),
        5: ("Основные", "Вид"),
        6: ("Основные", "Имя"),
        9: ("Основные", "Заголовок"),
    },
    "37": {
        1: ("Основные", "Идентификатор"),
        2: ("Иерархия", "Использование"),
        3: ("Иерархия", "Подчинение"),
        4: ("Иерархия", "Группа"),
        5: ("Основные", "Вид"),
        6: ("Основные", "Имя"),
        9: ("Основные", "Заголовок"),
    },
    "48": {
        1: ("Основные", "Идентификатор"),
        2: ("Иерархия", "Использование"),
        3: ("Иерархия", "Подчинение"),
        4: ("Иерархия", "Группа"),
        5: ("Основные", "Вид"),
        6: ("Основные", "Имя"),
    },
    "55": {
        1: ("Основные", "Идентификатор"),
        2: ("Иерархия", "Использование"),
        3: ("Иерархия", "Подчинение"),
        4: ("Иерархия", "Группа"),
        5: ("Основные", "Имя"),
    },
    "73": {
        1: ("Основные", "Идентификатор"),
        2: ("Иерархия", "Использование"),
        3: ("Иерархия", "Подчинение"),
        4: ("Иерархия", "Группа"),
        5: ("Основные", "Имя"),
    },
}

SECTION_RECORD_SEMANTIC_PROPERTIES = {
    1: ("Основные", "Идентификатор"),
    2: ("Основные", "Имя"),
    3: ("Основные", "Заголовок"),
    6: ("Основные", "Имя"),
}


def children(node: Any) -> list[Any]:
    if isinstance(node, dict) and node.get("type") in {"list", "sequence"}:
        return node.get("items") or []
    return []


def child_at(node: Any, index: int) -> Any:
    items = children(node)
    return items[index] if 0 <= index < len(items) else None


def atoms(node: Any, *, limit: int = 100) -> list[str]:
    result: list[str] = []

    def walk(value: Any) -> None:
        if len(result) >= limit:
            return
        text = scalar(value)
        if text:
            result.append(text)
            return
        for child in children(value):
            walk(child)

    walk(node)
    return result


def guids(node: Any, *, limit: int = 100) -> list[str]:
    return [value.lower() for value in atoms(node, limit=limit * 4) if GUID_RE.fullmatch(value)][:limit]


def child_scalar(node: Any, index: int) -> str | None:
    items = children(node)
    if index < 0 or index >= len(items):
        return None
    return scalar(items[index])


def direct_scalar(node: Any) -> str | None:
    if isinstance(node, dict) and node.get("type") in {"atom", "string", "number", "guid", "base64"}:
        value = node.get("value")
        return "" if value is None else str(value)
    return None


def child_direct_scalar(node: Any, index: int) -> str | None:
    items = children(node)
    if index < 0 or index >= len(items):
        return None
    return direct_scalar(items[index])


def child_direct_scalar_from_end(node: Any, offset: int) -> tuple[str | None, int | None]:
    items = children(node)
    index = len(items) + offset
    if offset >= 0 or index < 0 or index >= len(items):
        return None, None
    return direct_scalar(items[index]), index


def scalar_kind(value: str | None) -> str:
    if value is None:
        return "empty"
    if GUID_RE.fullmatch(value):
        return "guid"
    if value in {"0", "1"}:
        return "boolean_or_number"
    if re.fullmatch(r"-?\d+", value):
        return "number"
    if re.fullmatch(r"-?\d+(?:\.\d+)?", value):
        return "number"
    return "string"


def path_join(path: str, index: int) -> str:
    return f"{path}.{index}" if path else str(index)


def node_scalar_values(node: Any, *, limit: int = 40) -> list[dict[str, Any]]:
    values: list[dict[str, Any]] = []

    def walk(value: Any, path: str) -> None:
        if len(values) >= limit:
            return
        text = scalar(value)
        if text is not None:
            values.append({"value": text, "kind": scalar_kind(text), "position": {"indices": [int(part) for part in path.split(".") if part.isdigit()]}})
            return
        for index, child in enumerate(children(value)):
            walk(child, path_join(path, index))

    walk(node, "")
    return values


def node_scalar_entries(node: Any, base_path: str = "", *, limit: int = 20000) -> list[dict[str, Any]]:
    result: list[dict[str, Any]] = []

    def walk(value: Any, path: str) -> None:
        if len(result) >= limit:
            return
        text = direct_scalar(value)
        if text is not None:
            result.append({"value": text, "path": path, "kind": scalar_kind(text)})
            return
        for index, child in enumerate(children(value)):
            walk(child, path_join(path, index))

    walk(node, base_path)
    return result


def direct_parameters(
    node: Any,
    base_path: str,
    *,
    roles: dict[int, str] | None = None,
    limit: int = 200,
) -> list[dict[str, Any]]:
    result: list[dict[str, Any]] = []
    for index, child in enumerate(children(node)[:limit]):
        item_path = path_join(base_path, index)
        text = scalar(child)
        entry: dict[str, Any] = {
            "index": index,
            "presentation": (roles or {}).get(index) or f"Параметр {index}",
            "position": {"indices": [int(part) for part in item_path.split(".") if part.isdigit()]},
        }
        if text is not None:
            entry["value"] = text
            entry["value_kind"] = scalar_kind(text)
        else:
            child_items = children(child)
            evidence = collect_evidence(child)
            localized = localized_text(child, item_path, max_depth=3)
            entry.update(
                {
                    "kind": "group",
                    "items": len(child_items),
                    "strings": sorted(evidence["strings"])[:20],
                    "guids": sorted(evidence["guids"])[:20],
                    "values_sample": node_scalar_values(child, limit=20),
                }
            )
            if localized:
                entry["localized_text"] = {"lang": localized.get("lang"), "text": localized.get("value")}
        result.append(entry)
    return result


def parameter_value(parameters: list[dict[str, Any]], index: int) -> Any:
    for parameter in parameters:
        if parameter.get("index") == index:
            return parameter.get("value")
    return None


def semantic_property(name: str, value: Any, *, index: int | None = None, source: str = "form_payload") -> dict[str, Any]:
    result = {
        "name": name,
        "value": value,
        "source": source,
        "status": "ok" if value is not None and value != "" else "empty",
    }
    if index is not None:
        result["parameter_index"] = index
    return result


def item_type_name(marker: str | None, type_code: str | None) -> str | None:
    marker_text = str(marker or "")
    type_text = str(type_code or "")
    if marker_text == "6":
        return FORM_TABLE_ADDITION_TYPE_NAMES.get(type_text, type_text or None)
    if marker_text in {"35", "37", "48"}:
        return FORM_FIELD_SUBTYPE_NAMES.get(type_text, "Поле")
    if marker_text == "22":
        return FORM_ITEM_TYPE_NAMES.get(type_text, type_text or None)
    return FORM_ITEM_TYPE_NAMES.get(marker_text, type_text or marker_text or None)


def form_item_public_type_name(marker: str | None, type_code: str | None, name: str | None) -> str | None:
    """Resolve form element type using payload shape plus stable XML-confirmed naming rules."""
    marker_text = str(marker or "")
    name_text = str(name or "")
    if marker_text == "12":
        if name_text.endswith(("РасширеннаяПодсказка", "ExtendedTooltip")):
            return "Расширенная подсказка"
        return FORM_DECORATION_TYPE_NAMES.get(str(type_code or ""), "Декорация")
    return item_type_name(marker, type_code)


def bool_presentation(value: str | None) -> bool | None:
    if value == "1":
        return True
    if value == "0":
        return False
    return None


def auto_bool_presentation(value: str | None) -> str | bool | None:
    if value == "1":
        return True
    if value == "0":
        return False
    if value == "2":
        return "Авто"
    return None


def title_location_presentation(value: str | None) -> str | None:
    return {
        "0": "None",
        "1": "Auto",
        "2": "Left",
        "3": "Top",
        "4": "Right",
    }.get(str(value) if value is not None else "")


def horizontal_align_presentation(value: str | None) -> str | None:
    return {
        "0": "Left",
        "1": "Center",
        "2": "Right",
        "3": "Auto",
    }.get(str(value) if value is not None else "")


def vertical_align_presentation(value: str | None) -> str | None:
    return {
        "0": "Top",
        "1": "Center",
        "2": "Bottom",
        "3": "Auto",
    }.get(str(value) if value is not None else "")


def button_importance_presentation(value: str | None) -> str | None:
    return {
        "0": "Supplementary",
        "1": "Main",
    }.get(str(value) if value is not None else "")


def input_edit_mode_presentation(value: str | None) -> str | None:
    return {
        "0": "Directly",
        "1": "Auto",
        "2": "EnterOnInput",
    }.get(str(value) if value is not None else "")


def tooltip_representation_presentation(value: str | None) -> str | None:
    return {
        "0": "Auto",
        "3": "Button",
    }.get(str(value) if value is not None else "")


def choice_folders_and_items_presentation(value: str | None) -> str | None:
    return {
        "0": "Items",
        "3": "Auto",
    }.get(str(value) if value is not None else "")


def edit_text_update_presentation(value: str | None) -> str | None:
    return {
        "0": "Auto",
        "2": "OnValueChange",
    }.get(str(value) if value is not None else "")


def choice_button_representation_presentation(value: str | None) -> str | None:
    return {
        "0": "Auto",
        "2": "ShowInDropListAndInInputField",
        "3": "ShowInInputField",
    }.get(str(value) if value is not None else "")


def choice_history_on_input_presentation(value: str | None) -> str | None:
    return {
        "0": "Auto",
        "1": "DontUse",
    }.get(str(value) if value is not None else "")


def shortcut_presentation(node: Any) -> str | None:
    """Decode the confirmed managed-form shortcut tuple ``{0,key,modifiers}``."""
    if child_direct_scalar(node, 0) != "0":
        return None
    key_code = child_direct_scalar(node, 1)
    modifiers = child_direct_scalar(node, 2)
    if not key_code or modifiers is None:
        return None
    try:
        code = int(key_code)
        modifier_mask = int(modifiers)
    except ValueError:
        return None
    if 112 <= code <= 123:
        key_name = f"F{code - 111}"
    elif 32 <= code <= 126:
        key_name = chr(code).upper()
    else:
        return None
    modifier_names = []
    if modifier_mask & 8:
        modifier_names.append("Ctrl")
    if modifier_mask & 16:
        modifier_names.append("Alt")
    if modifier_mask & 4:
        modifier_names.append("Shift")
    return "+".join([*modifier_names, key_name])


def choice_parameter_links_presentation(node: Any) -> dict[str, Any] | None:
    """Decode the stable public part of an input field ChoiceParameterLinks node.

    Marker 5007 is followed by the declared link count. Link data paths use
    internal form attribute references, so only the public link names are
    exposed here until those references are resolved independently.
    """
    if child_direct_scalar(node, 0) != "5007":
        return None
    try:
        count = int(child_direct_scalar(node, 1) or "0")
    except ValueError:
        return None
    if count <= 0:
        return None
    names: list[str] = []
    for item in children(node)[2:]:
        value = direct_scalar(item)
        if not value or not BSL_PATH_RE.fullmatch(value):
            continue
        names.append(value)
        if len(names) == count:
            break
    if len(names) != count:
        return None
    return {
        "count": count,
        "links": [{"name": name} for name in names],
    }


def choice_list_presentation(node: Any) -> dict[str, Any] | None:
    """Decode confirmed numeric ChoiceList items from input-field payloads."""
    if child_direct_scalar(node, 0) != "3":
        return None
    try:
        count = int(child_direct_scalar(node, 1) or "0")
    except ValueError:
        return None
    if count <= 0:
        return None
    result: list[dict[str, Any]] = []
    node_items = children(node)
    for item_index in range(count):
        encoded_item = node_items[3 + item_index * 2] if 3 + item_index * 2 < len(node_items) else None
        if child_direct_scalar(encoded_item, 0) != "#":
            return None
        payload = child_at(encoded_item, 2)
        encoded_value = child_at(payload, 2)
        value_kind = child_direct_scalar(encoded_value, 0)
        localized = localized_text(encoded_item, "", max_depth=8)
        presentation = str((localized or {}).get("value") or "")
        if value_kind == "N":
            scalar_value = child_direct_scalar(encoded_value, 1)
            if scalar_value is None:
                return None
            value: dict[str, Any] = {"kind": "Number", "value": scalar_value}
        elif value_kind == "U":
            type_guid = child_direct_scalar(payload, 3)
            value_guid = child_direct_scalar(payload, 4)
            if not type_guid or not value_guid or not GUID_RE.fullmatch(type_guid) or not GUID_RE.fullmatch(value_guid):
                return None
            value = {
                "kind": "EnumValue",
                "type_guid": type_guid.lower(),
                "value_guid": value_guid.lower(),
                "status": "identity_pending",
            }
        else:
            return None
        result.append(
            {
                "presentation": presentation,
                "value": value,
            }
        )
    return {"count": count, "items": result}


def type_link_reference(node: Any) -> dict[str, Any] | None:
    if child_direct_scalar(node, 0) != "3" or child_direct_scalar(node, 1) != "2":
        return None
    field_reference = children(node)[3] if len(children(node)) > 3 else None
    field_ids = [value for value in atoms(field_reference, limit=8) if re.fullmatch(r"\d+", value or "")]
    if not field_ids:
        return None
    link_item = child_direct_scalar(node, 4)
    return {
        "field_id": field_ids[-1],
        "link_item": int(link_item) if re.fullmatch(r"\d+", str(link_item or "")) else 0,
    }


def style_value_presentation(node: Any) -> str | dict[str, Any] | None:
    if child_direct_scalar(node, 0) != "3":
        return None
    variant = child_direct_scalar(node, 1)
    payload = children(node)[2] if len(children(node)) > 2 else None
    code = child_direct_scalar(payload, 0)
    if variant == "4" and code == "0":
        return "Авто"
    if variant == "1" and code == "18":
        return "win:ButtonText"
    if variant == "2" and code == "27":
        return "web:DarkGreen"
    if variant != "3":
        return None
    standard = {
        "-21": "style:ButtonTextColor",
        "-22": "style:BorderColor",
        "-23": "style:ToolTipBackColor",
        "-35": "style:TableHeaderBackColor",
        "-1": "style:FormBackColor",
    }.get(str(code or ""))
    if standard:
        return standard
    guid = child_direct_scalar(payload, 1)
    if str(guid or "").lower() == "ad87bd29-0ad1-4da4-ac62-38e714e0cb9f":
        return "style:ПоясняющийТекст"
    if guid and GUID_RE.fullmatch(guid):
        return {
            "kind": "StyleItem",
            "guid": guid.lower(),
            "status": "identity_pending",
        }
    return None


def font_value_presentation(node: Any) -> str | dict[str, Any] | None:
    if child_direct_scalar(node, 0) != "7":
        return None
    variant = child_direct_scalar(node, 1)
    if variant == "3":
        return "Авто"
    if variant != "2":
        return None
    reference = children(node)[3] if len(children(node)) > 3 else None
    code = child_direct_scalar(reference, 0)
    standard = {
        "-31": "style:NormalTextFont",
        "-32": "style:LargeTextFont",
    }.get(str(code or ""))
    if standard:
        return standard
    guid = child_direct_scalar(reference, 1)
    if code == "0" and guid and GUID_RE.fullmatch(guid):
        return {
            "kind": "StyleItem",
            "guid": guid.lower(),
            "status": "identity_pending",
        }
    return None


def auto_enum_presentation(value: str | None) -> str | None:
    return {"3": "Авто"}.get(str(value) if value is not None else "")


def button_representation_presentation(value: str | None) -> str | None:
    return {
        "0": "Text",
        "1": "Picture",
        "2": "PictureAndText",
        "3": "Авто",
    }.get(str(value) if value is not None else "")


def command_bar_location_presentation(value: str | None) -> str | None:
    return {
        "0": "Авто",
        "1": "В командной панели",
        "2": "В дополнительном подменю",
    }.get(str(value) if value is not None else "")


def add_grouped_property(groups: dict[str, list[dict[str, Any]]], group: str, prop: dict[str, Any]) -> None:
    groups.setdefault(group, []).append(prop)


def mark_semantic_parameter_mapped(semantic: dict[str, Any], index: int) -> None:
    unmapped = semantic.get("unmapped_parameters")
    coverage = semantic.get("coverage")
    if not isinstance(unmapped, list) or not isinstance(coverage, dict):
        return
    before = len(unmapped)
    semantic["unmapped_parameters"] = [item for item in unmapped if item.get("index") != index]
    if len(semantic["unmapped_parameters"]) == before:
        return
    coverage["mapped"] = int(coverage.get("mapped") or 0) + (before - len(semantic["unmapped_parameters"]))
    coverage["unmapped"] = max(0, int(coverage.get("unmapped") or 0) - (before - len(semantic["unmapped_parameters"])))
    coverage["status"] = "partial" if coverage["unmapped"] else "ok"


def semantic_coverage(parameters: list[dict[str, Any]], mapped_indexes: set[int]) -> dict[str, Any]:
    available = [item for item in parameters if item.get("index") is not None]
    unmapped = [item for item in available if item.get("index") not in mapped_indexes]
    return {
        "mapped": len(available) - len(unmapped),
        "unmapped": len(unmapped),
        "total": len(available),
        "status": "partial" if unmapped else "ok",
    }


def semantic_unmapped_parameters(parameters: list[dict[str, Any]], mapped_indexes: set[int]) -> list[dict[str, Any]]:
    result = []
    for parameter in parameters:
        if parameter.get("index") in mapped_indexes:
            continue
        public = {
            "index": parameter.get("index"),
            "presentation": parameter.get("presentation"),
            "value": parameter.get("value"),
            "value_kind": parameter.get("value_kind"),
            "kind": parameter.get("kind"),
            "items": parameter.get("items"),
            "strings": parameter.get("strings"),
            "guids": parameter.get("guids"),
            "localized_text": parameter.get("localized_text"),
        }
        result.append({key: value for key, value in public.items() if value is not None and value != [] and value != {}})
    return result


def public_semantic(semantic: dict[str, Any], *, include_diagnostics: bool) -> dict[str, Any]:
    if include_diagnostics:
        return semantic
    groups: dict[str, list[dict[str, Any]]] = {}
    for group, properties in (semantic.get("groups") or {}).items():
        groups[group] = [
            {key: prop.get(key) for key in ("name", "value", "status") if key in prop}
            for prop in properties
            if isinstance(prop, dict)
        ]
    return {"groups": groups}


def public_rows_semantics(rows: list[dict[str, Any]], *, include_diagnostics: bool) -> None:
    if include_diagnostics:
        return
    for row in rows:
        semantic = row.get("semantic")
        if isinstance(semantic, dict):
            row["semantic"] = public_semantic(semantic, include_diagnostics=False)


def add_derived_semantic_property(row: dict[str, Any], group: str, name: str, value: Any, *, source: str) -> None:
    if value is None or value == "":
        return
    semantic = row.setdefault("semantic", {})
    groups = semantic.setdefault("groups", {})
    for prop in groups.get(group, []):
        if isinstance(prop, dict) and prop.get("name") == name:
            return
    add_grouped_property(groups, group, semantic_property(name, value, source=source))


def form_item_semantic_properties(row: dict[str, Any], parameters: list[dict[str, Any]]) -> dict[str, Any]:
    marker = str(row.get("marker") or "")
    type_code = str(row.get("type_code") or "")
    role_map = dict(FORM_ITEM_SEMANTIC_PROPERTIES.get(marker, {}))
    if marker == "34":
        name_path = str(row.get("name_path") or "")
        if name_path.endswith(".5"):
            role_map.pop(6, None)
            role_map[4] = ("Основные", "Вид")
            role_map[5] = ("Основные", "Имя")
    elif marker == "48":
        name_path = str(row.get("name_path") or "")
        if name_path.endswith(".6"):
            role_map.pop(7, None)
            role_map[5] = ("Основные", "Вид")
            role_map[6] = ("Основные", "Имя")
    elif marker in {"35", "37"} and str(row.get("name_path") or "").endswith(".7"):
        role_map = {
            (index + 1 if index >= 5 else index): value
            for index, value in role_map.items()
        }
    elif marker == "73":
        name_path = str(row.get("name_path") or "")
        if name_path.endswith(".5"):
            role_map.pop(6, None)
            role_map[5] = ("Основные", "Имя")
    groups: dict[str, list[dict[str, Any]]] = {}
    mapped: set[int] = set()
    for index, (group, name) in role_map.items():
        mapped.add(index)
        value = parameter_value(parameters, index)
        if name == "Вид":
            value = item_type_name(marker, str(value) if value is not None else type_code)
        add_grouped_property(groups, group, semantic_property(name, value, index=index))
    if marker == "31":
        add_grouped_property(groups, "Основные", semantic_property("Вид", "Кнопка командной панели", source="marker"))
    elif type_code and (6 if marker in {"35", "37"} and str(row.get("name_path") or "").endswith(".7") else 5) not in mapped:
        type_index = 6 if marker in {"35", "37"} and str(row.get("name_path") or "").endswith(".7") else 5
        add_grouped_property(groups, "Основные", semantic_property("Вид", row.get("type_name") or item_type_name(marker, type_code), index=type_index))
        mapped.add(type_index)
    return {
        "groups": groups,
        "unmapped_parameters": semantic_unmapped_parameters(parameters, mapped),
        "coverage": semantic_coverage(parameters, mapped),
    }


def enrich_item_reference_semantics(records: list[dict[str, Any]]) -> None:
    by_name = {str(row.get("name") or ""): row for row in records if row.get("name")}
    by_id = {str(row.get("id") or ""): row for row in records if row.get("id") not in {None, ""}}
    for row in records:
        owner = str(row.get("name") or "")
        owner_path = str(row.get("path") or "")
        if not owner or not owner_path:
            continue
        descendants = [
            candidate
            for candidate in records
            if str(candidate.get("path") or "").startswith(owner_path + ".") and candidate.get("name") != owner
        ]
        for suffix, prop_name in (
            ("КонтекстноеМеню", "КонтекстноеМеню"),
            ("РасширеннаяПодсказка", "РасширеннаяПодсказка"),
            ("ExtendedTooltip", "РасширеннаяПодсказка"),
            ("КоманднаяПанель", "AutoCommandBar"),
        ):
            child = by_name.get(owner + suffix)
            if child is not None and child in descendants:
                add_derived_semantic_property(row, "Прочее", prop_name, child.get("name"), source="item_reference:name_path")
        for child in descendants:
            child_type = str(child.get("type_name") or "")
            if child_type in {"SearchStringAddition", "ViewStatusAddition", "SearchControlAddition"} and str(child.get("name") or "").startswith(owner):
                add_derived_semantic_property(row, "Прочее", child_type, child.get("name"), source="item_reference:child_type")
        user_settings_group_ids = [
            *([str(row.pop("_user_settings_group_id"))] if row.get("_user_settings_group_id") not in {None, ""} else []),
            *[str(value) for value in row.pop("_user_settings_group_ids", [])],
        ]
        referenced_groups = [
            by_id[value]
            for value in user_settings_group_ids
            if value in by_id
            and (
                str(by_id[value].get("type_name") or "") in {"Группа", "Группа колонок", "Группа кнопок"}
                or "настрой" in str(by_id[value].get("name") or "").casefold()
            )
        ]
        named_settings_groups = [
            candidate
            for candidate in records
            if "настро" in str(candidate.get("name") or "").casefold()
            and "пользователь" in str(candidate.get("name") or "").casefold()
            and str(candidate.get("type_name") or "") == "Группа"
        ]
        user_settings_group = referenced_groups[-1] if referenced_groups else (
            named_settings_groups[0] if str(row.get("marker") or "") == "55" and len(named_settings_groups) == 1 else None
        )
        if user_settings_group is not None:
            add_derived_semantic_property(
                row,
                "Использование",
                "UserSettingsGroup",
                user_settings_group.get("name"),
                source="item_reference:id",
            )

    addition_representations = {
        "SearchStringAddition": "SearchStringRepresentation",
        "ViewStatusAddition": "ViewStatusRepresentation",
        "SearchControlAddition": "SearchControl",
    }
    table_rows = [row for row in records if str(row.get("marker") or "") in {"55", "73"}]
    for row in records:
        representation = addition_representations.get(str(row.get("type_name") or ""))
        row_path = str(row.get("path") or "")
        if not representation or not row_path:
            continue
        owners = [
            candidate
            for candidate in table_rows
            if str(candidate.get("path") or "") and row_path.startswith(str(candidate.get("path")) + ".")
        ]
        if not owners:
            continue
        owner = max(owners, key=lambda candidate: len(str(candidate.get("path") or "").split(".")))
        source = {
            "owner_element": owner.get("name"),
            "representation": representation,
        }
        row["addition_source"] = source
        add_derived_semantic_property(row, "Основные", "AdditionSource", source, source="item_hierarchy:table_addition")


def enrich_input_field_specific_semantics(row: dict[str, Any], node: Any) -> None:
    marker = str(row.get("marker") or "")
    if marker not in {"35", "37"}:
        return
    semantic = row.setdefault("semantic", {})
    groups = semantic.setdefault("groups", {})
    legacy_offset = 1 if str(row.get("name_path") or "").endswith(".7") else 0

    def actual_index(index: int) -> int:
        return index + legacy_offset if index >= 5 else index

    def field_scalar(index: int) -> str | None:
        return child_direct_scalar(node, actual_index(index))

    title_location = title_location_presentation(field_scalar(7))
    if title_location is not None:
        add_grouped_property(groups, "Основные", semantic_property("ПоложениеЗаголовка", title_location, index=actual_index(7), source="form_payload_input_field"))
        mark_semantic_parameter_mapped(semantic, actual_index(7))
    visible = bool_presentation(field_scalar(43))
    if visible is not None:
        add_grouped_property(groups, "Основные", semantic_property("Видимость", visible, index=actual_index(43), source="form_payload_input_field"))
        mark_semantic_parameter_mapped(semantic, actual_index(43))
    enabled = bool_presentation(field_scalar(13))
    if enabled is not None:
        add_grouped_property(groups, "Основные", semantic_property("Доступность", enabled, index=actual_index(13), source="form_payload_input_field"))
        mark_semantic_parameter_mapped(semantic, actual_index(13))
    read_only = bool_presentation(field_scalar(14))
    if read_only is not None:
        add_grouped_property(groups, "Основные", semantic_property("ТолькоПросмотр", read_only, index=actual_index(14), source="form_payload_input_field"))
        mark_semantic_parameter_mapped(semantic, actual_index(14))
    skip_on_input = {"1": True, "2": False}.get(str(field_scalar(15) or ""))
    if skip_on_input is not None:
        add_grouped_property(
            groups,
            "Использование",
            semantic_property("ПропускатьПриВводе", skip_on_input, index=actual_index(15), source="form_payload_input_field"),
        )
        mark_semantic_parameter_mapped(semantic, actual_index(15))
    default_item = bool_presentation(field_scalar(16))
    if default_item is not None:
        add_grouped_property(
            groups,
            "Основные",
            semantic_property(
                "АктивизироватьПоУмолчанию",
                default_item,
                index=actual_index(16),
                source="form_payload_input_field",
            ),
        )
        mark_semantic_parameter_mapped(semantic, actual_index(16))
    show_in_header = bool_presentation(field_scalar(20))
    if show_in_header is not None:
        add_grouped_property(
            groups,
            "Расположение",
            semantic_property("ShowInHeader", show_in_header, index=actual_index(20), source="form_payload_input_field"),
        )
        mark_semantic_parameter_mapped(semantic, actual_index(20))
    show_in_footer = bool_presentation(field_scalar(21))
    if show_in_footer is not None:
        add_grouped_property(
            groups,
            "Расположение",
            semantic_property("ShowInFooter", show_in_footer, index=actual_index(21), source="form_payload_input_field"),
        )
        mark_semantic_parameter_mapped(semantic, actual_index(21))
    fixing_in_table = "Left" if field_scalar(49) == "1" else None
    if fixing_in_table is not None:
        add_grouped_property(
            groups,
            "Расположение",
            semantic_property("FixingInTable", fixing_in_table, index=actual_index(49), source="form_payload_input_field"),
        )
        mark_semantic_parameter_mapped(semantic, actual_index(49))
    auto_cell_height = bool_presentation(field_scalar(28))
    if auto_cell_height is True:
        add_grouped_property(
            groups,
            "Расположение",
            semantic_property("AutoCellHeight", True, index=actual_index(28), source="form_payload_input_field"),
        )
        mark_semantic_parameter_mapped(semantic, actual_index(28))
    cell_hyperlink = bool_presentation(field_scalar(22))
    if cell_hyperlink is not None:
        add_grouped_property(
            groups,
            "Использование",
            semantic_property("CellHyperlink", cell_hyperlink, index=actual_index(22), source="form_payload_input_field"),
        )
        mark_semantic_parameter_mapped(semantic, actual_index(22))
    direct_properties = [
        ("Расположение", "TitleHeight", field_scalar(8), actual_index(8)),
        ("Расположение", "HorizontalAlign", horizontal_align_presentation(field_scalar(23)), actual_index(23)),
        ("Использование", "РежимРедактирования", input_edit_mode_presentation(field_scalar(26)), actual_index(26)),
        ("Использование", "AutoEditMode", True if field_scalar(26) == "2" else None, actual_index(26)),
        ("Расположение", "GroupHorizontalAlign", horizontal_align_presentation(field_scalar(53)), actual_index(53)),
        ("Расположение", "GroupVerticalAlign", vertical_align_presentation(field_scalar(54)), actual_index(54)),
        ("Оформление", "ToolTipRepresentation", tooltip_representation_presentation(field_scalar(50)), actual_index(50)),
    ]
    for group, name, value, index in direct_properties:
        if value is not None:
            add_grouped_property(groups, group, semantic_property(name, value, index=index, source="form_payload_input_field"))
            mark_semantic_parameter_mapped(semantic, index)

    specific_index = actual_index(39)
    specific = children(node)[specific_index] if len(children(node)) > specific_index else None
    if specific is None:
        return
    choice_list = choice_list_presentation(children(specific)[1] if len(children(specific)) > 1 else None)
    if choice_list is not None:
        add_grouped_property(
            groups,
            "Использование",
            semantic_property("ChoiceList", choice_list, index=1, source="form_payload_field"),
        )
        mark_semantic_parameter_mapped(semantic, 1)
    specific_marker = child_direct_scalar(specific, 0)
    if marker == "37" and str(row.get("type_code") or "") == "1" and specific_marker == "11":
        hyperlink = bool_presentation(child_direct_scalar(specific, 7))
        if hyperlink is not None:
            add_grouped_property(
                groups,
                "Использование",
                semantic_property("Hiperlink", hyperlink, index=7, source="form_payload_label_field"),
            )
        label_properties = [
            ("Ширина", child_direct_scalar(specific, 1)),
            ("Высота", child_direct_scalar(specific, 2)),
            ("ВертикальноеПоложениеВГруппе", {"2": "Top"}.get(str(child_direct_scalar(specific, 3) or ""))),
            ("РастягиватьПоВертикали", bool_presentation(child_direct_scalar(specific, 4))),
            ("РастягиватьПоГоризонтали", bool_presentation(child_direct_scalar(specific, 17))),
        ]
        for name, value in label_properties:
            if value not in {None, "", "0"} or isinstance(value, bool):
                add_grouped_property(
                    groups,
                    "Расположение",
                    semantic_property(name, value, index=39, source="form_payload_label_field"),
                )
                mark_semantic_parameter_mapped(semantic, 39)
        text_color = style_value_presentation(child_at(specific, 8))
        if text_color is not None:
            add_grouped_property(
                groups,
                "Оформление",
                semantic_property("TextColor", text_color, index=39, source="form_payload_label_field"),
            )
            mark_semantic_parameter_mapped(semantic, 39)
    expected_specific_marker = "32" if marker == "35" else "36"
    if marker == "35" and specific_marker == "11":
        auto_max_width = bool_presentation(child_direct_scalar(specific, 15))
        max_width = child_direct_scalar(specific, 16)
        if auto_max_width is not None:
            add_grouped_property(
                groups,
                "Расположение",
                semantic_property("АвтоМаксимальнаяШирина", auto_max_width, index=39, source="form_payload_label_field"),
            )
            mark_semantic_parameter_mapped(semantic, 39)
        if max_width not in {None, ""}:
            add_grouped_property(
                groups,
                "Расположение",
                semantic_property("МаксимальнаяШирина", max_width, index=39, source="form_payload_label_field"),
            )
            mark_semantic_parameter_mapped(semantic, 39)
        return
    if specific_marker != expected_specific_marker:
        if marker == "37" and str(row.get("type_code") or "") == "11" and specific_marker == "1":
            chart_height = child_direct_scalar(specific, 2)
            if chart_height not in {None, "", "0"}:
                add_grouped_property(
                    groups,
                    "Расположение",
                    semantic_property("Высота", chart_height, index=39, source="form_payload_chart_field"),
                )
                mark_semantic_parameter_mapped(semantic, 39)
        if marker == "37" and str(row.get("type_code") or "") == "14" and specific_marker == "3":
            graphical_properties = [
                ("Расположение", "Ширина", child_direct_scalar(specific, 1)),
                ("Расположение", "Высота", child_direct_scalar(specific, 2)),
                ("Использование", "Edit", bool_presentation(child_direct_scalar(specific, 3))),
            ]
            for group, name, value in graphical_properties:
                if value not in {None, ""}:
                    add_grouped_property(
                        groups,
                        group,
                        semantic_property(name, value, index=39, source="form_payload_graphical_schema_field"),
                    )
                    mark_semantic_parameter_mapped(semantic, 39)
        if marker == "37" and str(row.get("type_code") or "") == "4" and specific_marker == "10":
            picture_properties = [
                ("Расположение", "Ширина", child_direct_scalar(specific, 1)),
                ("Расположение", "РастягиватьПоГоризонтали", bool_presentation(child_direct_scalar(specific, 2))),
                ("Использование", "РежимПеретаскиванияФайлов", {"1": "AsFile"}.get(str(child_direct_scalar(specific, 17) or ""))),
            ]
            for group, name, value in picture_properties:
                if value not in {None, ""}:
                    add_grouped_property(
                        groups,
                        group,
                        semantic_property(name, value, index=39, source="form_payload_picture_field"),
                    )
                    mark_semantic_parameter_mapped(semantic, 39)
        return
    open_button = auto_bool_presentation(child_direct_scalar(specific, 15))
    quick_choice = auto_bool_presentation(child_direct_scalar(specific, 23))
    choose_type = bool_presentation(child_direct_scalar(specific, 32))
    min_value = child_direct_scalar(child_at(specific, 16), 1) if child_direct_scalar(child_at(specific, 16), 0) == "N" else None
    max_value = child_direct_scalar(child_at(specific, 17), 1) if child_direct_scalar(child_at(specific, 17), 0) == "N" else None
    properties = [
        ("Расположение", "Width", child_direct_scalar(specific, 2), 2),
        ("Расположение", "Height", child_direct_scalar(specific, 3), 3),
        ("Расположение", "HorizontalStretch", auto_bool_presentation(child_direct_scalar(specific, 4)), 4),
        ("Расположение", "VerticalStretch", auto_bool_presentation(child_direct_scalar(specific, 5)), 5),
        ("Расположение", "Wrap", bool_presentation(child_direct_scalar(specific, 6)), 6),
        ("Использование", "PasswordMode", {"1": True, "2": False}.get(str(child_direct_scalar(specific, 7) or "")), 7),
        ("Использование", "MultiLine", multiline_presentation(child_direct_scalar(specific, 8)), 8),
        ("Использование", "ChoiceListButton", bool_presentation(child_direct_scalar(specific, 11)), 11),
        ("Использование", "КнопкаВыпадающегоСписка", auto_bool_presentation(child_direct_scalar(specific, 47)), 47),
        ("Использование", "КнопкаВыбора", bool_presentation(child_direct_scalar(specific, 12)), 12),
        ("Использование", "SpinButton", auto_bool_presentation(child_direct_scalar(specific, 14)), 14),
        ("Использование", "OpenButton", open_button, 15),
        ("Использование", "КнопкаОчистки", auto_bool_presentation(child_direct_scalar(specific, 13)), 13),
        ("Использование", "CreateButton", bool_presentation(child_direct_scalar(specific, 45)), 45),
        ("Использование", "ListChoiceMode", bool_presentation(child_direct_scalar(specific, 19)), 19),
        ("Расположение", "ChoiceListHeight", child_direct_scalar(specific, 21), 21),
        ("Использование", "ChoiceFoldersAndItems", choice_folders_and_items_presentation(child_direct_scalar(specific, 24)), 24),
        ("Использование", "БыстрыйВыбор", quick_choice, 23),
        ("Использование", "AutoChoiceIncomplete", auto_bool_presentation(child_direct_scalar(specific, 28)), 28),
        ("Использование", "MarkRequiredComplete", auto_bool_presentation(child_direct_scalar(specific, 31)), 31),
        ("Использование", "AutoMarkIncomplete", auto_bool_presentation(child_direct_scalar(specific, 31)), 31),
        ("Использование", "ВыбиратьТип", choose_type, 32),
        ("Использование", "TypeDomainEnabled", choose_type, 32),
        ("Использование", "ExtendedEdit", bool_presentation(child_direct_scalar(specific, 52)), 52),
        ("Использование", "MinValue", min_value, 16),
        ("Использование", "MaxValue", max_value, 17),
        ("Использование", "РедактированиеТекста", bool_presentation(child_direct_scalar(specific, 41)), 41),
        ("Использование", "EditTextUpdate", edit_text_update_presentation(child_direct_scalar(specific, 43)), 43),
        ("Использование", "ChoiceButtonRepresentation", choice_button_representation_presentation(child_direct_scalar(specific, 46)), 46),
        ("Использование", "ChoiceHistoryOnInput", choice_history_on_input_presentation(child_direct_scalar(specific, 48)), 48),
        ("Использование", "ExtendedEditMultipleValues", bool_presentation(child_direct_scalar(specific, 65)), 65),
        ("Расположение", "AutoMaxHeight", bool_presentation(child_direct_scalar(specific, 52)), 52),
        ("Расположение", "MaxHeight", child_direct_scalar(specific, 53), 53),
        ("Расположение", "АвтоМаксимальнаяШирина", bool_presentation(child_direct_scalar(specific, 49)), 49),
        ("Расположение", "МаксимальнаяШирина", child_direct_scalar(specific, 50), 50),
        ("Расположение", "FooterHorizontalAlign", {"2": "Left"}.get(str(child_direct_scalar(specific, 45) or "")), 45),
    ]
    for group, name, value, index in properties:
        if value is not None:
            add_grouped_property(groups, group, semantic_property(name, value, index=index, source="form_payload_input_field"))
            mark_semantic_parameter_mapped(semantic, index)
    mask = child_direct_scalar(specific, 18)
    if mask:
        add_grouped_property(
            groups,
            "Использование",
            semantic_property("Mask", mask, index=18, source="form_payload_input_field"),
        )
        mark_semantic_parameter_mapped(semantic, 18)
    for name, index in (("Format", 29), ("EditFormat", 30), ("InputHint", 44)):
        value_node = children(specific)[index] if len(children(specific)) > index else None
        localized = localized_text(value_node, "", max_depth=3) if value_node is not None else None
        value = str((localized or {}).get("value") or "")
        if value:
            group = "Форматирование" if name in {"Format", "EditFormat"} else "Оформление"
            add_grouped_property(
                groups,
                group,
                semantic_property(name, value, index=index, source="form_payload_input_field"),
            )
            mark_semantic_parameter_mapped(semantic, index)
    for name, index in (("TextColor", 37), ("BackColor", 38), ("BorderColor", 39)):
        value = style_value_presentation(children(specific)[index] if len(children(specific)) > index else None)
        if value is not None:
            add_grouped_property(
                groups,
                "Оформление",
                semantic_property(name, value, index=index, source="form_payload_input_field"),
            )
            mark_semantic_parameter_mapped(semantic, index)
    font = font_value_presentation(children(specific)[40] if len(children(specific)) > 40 else None)
    if font is not None:
        add_grouped_property(
            groups,
            "Оформление",
            semantic_property("Font", font, index=40, source="form_payload_input_field"),
        )
        mark_semantic_parameter_mapped(semantic, 40)
    type_link = type_link_reference(children(specific)[42] if len(children(specific)) > 42 else None)
    if type_link is not None:
        row["_type_link_reference"] = type_link
    choice_parameter_links = choice_parameter_links_presentation(
        children(specific)[64] if len(children(specific)) > 64 else None
    )
    if choice_parameter_links is not None:
        add_grouped_property(
            groups,
            "Использование",
            semantic_property(
                "ChoiceParameterLinks",
                choice_parameter_links,
                index=64,
                source="form_payload_input_field",
            ),
        )
        mark_semantic_parameter_mapped(semantic, 64)


def enrich_container_specific_semantics(row: dict[str, Any], node: Any) -> None:
    if str(row.get("marker") or "") != "22":
        return
    semantic = row.setdefault("semantic", {})
    groups = semantic.setdefault("groups", {})
    visible = bool_presentation(child_direct_scalar(node, 10))
    visible_index = 10
    if row.get("type_name") == "Группа":
        width = child_direct_scalar(node, 12)
        if width not in {None, "", "0"}:
            add_grouped_property(
                groups,
                "Расположение",
                semantic_property("Ширина", width, index=12, source="form_payload_group_layout"),
            )
            mark_semantic_parameter_mapped(semantic, 12)
        for index in (26, 28):
            if child_direct_scalar(node, index) == "0":
                visible = False
                visible_index = index
                break
    if visible is not None:
        add_grouped_property(groups, "Основные", semantic_property("Видимость", visible, index=visible_index, source="form_payload_container"))
        mark_semantic_parameter_mapped(semantic, visible_index)
    if row.get("type_name") == "Командная панель":
        autofill_index = 28 if len(children(node)) == 29 else len(children(node)) - 1
        autofill = bool_presentation(child_direct_scalar(node, autofill_index))
        if autofill is not None:
            add_grouped_property(
                groups,
                "Использование",
                semantic_property("Автозаполнение", autofill, index=autofill_index, source="form_payload_command_bar"),
            )
            mark_semantic_parameter_mapped(semantic, autofill_index)
        command_source = {
            "a9f3b1ac-f51b-431e-b102-55a69acdecad": "Form",
        }.get(str(child_direct_scalar(node, 22) or "").lower())
        if command_source is not None:
            add_grouped_property(
                groups,
                "Использование",
                semantic_property("CommandSource", command_source, index=22, source="form_payload_command_bar"),
            )
            mark_semantic_parameter_mapped(semantic, 22)
    if row.get("type_name") == "Группа кнопок":
        button_group_options = child_at(node, 20)
        command_source_guid = str(child_direct_scalar(child_at(button_group_options, 1), 1) or "").lower()
        command_source = {
            "02023637-7868-4a5f-8576-835a76e0c9ba": "Form",
            "2ef6d6fa-847a-485e-8684-d37a3ab5efb8": "FormCommandPanelGlobalCommands",
        }.get(command_source_guid)
        if child_direct_scalar(button_group_options, 0) == "2" and command_source is not None:
            add_grouped_property(
                groups,
                "Использование",
                semantic_property("CommandSource", command_source, index=20, source="form_payload_button_group"),
            )
            mark_semantic_parameter_mapped(semantic, 20)
    container_representation = {
        "Группа кнопок": "Compact",
        "Подменю": "Picture",
    }.get(str(row.get("type_name") or ""))
    if container_representation:
        add_grouped_property(
            groups,
            "Оформление",
            semantic_property("Отображение", container_representation, source="form_payload_container_type"),
        )
    if row.get("type_name") == "Страница":
        page_options = child_at(node, 20)
        page_grouping = {
            "1": "Horizontal",
            "2": "HorizontalIfPossible",
        }.get(str(child_direct_scalar(page_options, 16) or ""), "Vertical")
        add_grouped_property(
            groups,
            "Расположение",
            semantic_property("Группировка", page_grouping, index=20, source="form_payload_page_layout"),
        )
        mark_semantic_parameter_mapped(semantic, 20)
        add_grouped_property(groups, "Использование", semantic_property("ScrollOnCompress", False, source="form_payload_container_type"))
        if child_direct_scalar(page_options, 6) == "0":
            add_grouped_property(
                groups,
                "Оформление",
                semantic_property("ПоказыватьЗаголовок", False, index=20, source="form_payload_page_layout"),
            )
        for name, option_index in (("HorizontalSpacing", 10), ("VerticalSpacing", 11)):
            spacing = {"4": "OneAndHalf"}.get(str(child_direct_scalar(page_options, option_index) or ""))
            if spacing is not None:
                add_grouped_property(
                    groups,
                    "Расположение",
                    semantic_property(name, spacing, index=20, source="form_payload_page_layout"),
                )
    if row.get("type_name") == "Страницы":
        pages_options = children(node)[20] if len(children(node)) > 20 else None
        primary_representation = child_direct_scalar(pages_options, 1)
        repeated_representation = child_direct_scalar(pages_options, 5)
        compact_pages_layout = child_direct_scalar(pages_options, 0) == "3" and len(children(pages_options)) == 5
        if primary_representation == repeated_representation or compact_pages_layout:
            pages_representation = {
                "0": "None",
                "1": "TabsOnTop",
            }.get(str(primary_representation or ""))
            if pages_representation is not None:
                add_grouped_property(
                    groups,
                    "Оформление",
                    semantic_property(
                        "PagesRepresentation",
                        pages_representation,
                        index=20,
                        source="form_payload_pages_layout",
                    ),
                )
                mark_semantic_parameter_mapped(semantic, 20)
    if row.get("type_name") in {"Страница", "Страницы"}:
        horizontal_stretch = bool_presentation(child_direct_scalar(node, 14))
        if horizontal_stretch is not None:
            add_grouped_property(
                groups,
                "Расположение",
                semantic_property("РастягиватьПоГоризонтали", horizontal_stretch, index=14, source="form_payload_container_layout"),
            )
            mark_semantic_parameter_mapped(semantic, 14)
    if row.get("type_name") == "Группа колонок":
        column_options = child_at(node, 20)
        column_grouping = {"0": "Horizontal", "2": "InCell"}.get(str(child_direct_scalar(column_options, 1) or ""))
        if column_grouping is not None:
            add_grouped_property(
                groups,
                "Расположение",
                semantic_property("Группировка", column_grouping, index=20, source="form_payload_column_group"),
            )
            mark_semantic_parameter_mapped(semantic, 20)
        if child_direct_scalar(node, 14) == "1":
            add_grouped_property(
                groups,
                "Расположение",
                semantic_property("РастягиватьПоГоризонтали", True, index=14, source="form_payload_column_group"),
            )
            mark_semantic_parameter_mapped(semantic, 14)
        show_in_header = bool_presentation(child_direct_scalar(node, 19))
        if show_in_header is not None:
            add_grouped_property(
                groups,
                "Расположение",
                semantic_property("ShowInHeader", show_in_header, index=19, source="form_payload_column_group"),
            )
            mark_semantic_parameter_mapped(semantic, 19)
        if child_direct_scalar(column_options, 11) == "1":
            add_grouped_property(
                groups,
                "Расположение",
                semantic_property("FixingInTable", "Left", index=20, source="form_payload_column_group"),
            )
            mark_semantic_parameter_mapped(semantic, 20)
    node_items = children(node)
    horizontal_align_index = len(node_items) - 3 if len(node_items) >= 29 else None
    vertical_align_index = len(node_items) - 2 if len(node_items) >= 29 else None
    horizontal_align = (
        horizontal_align_presentation(child_direct_scalar(node, horizontal_align_index))
        if horizontal_align_index is not None
        else None
    )
    vertical_align = (
        vertical_align_presentation(child_direct_scalar(node, vertical_align_index))
        if vertical_align_index is not None
        else None
    )
    if horizontal_align is not None:
        add_grouped_property(
            groups,
            "Расположение",
            semantic_property(
                "GroupHorizontalAlign",
                horizontal_align,
                index=horizontal_align_index,
                source="form_payload_container_layout_tail",
            ),
        )
        mark_semantic_parameter_mapped(semantic, horizontal_align_index)
    if vertical_align is not None:
        add_grouped_property(
            groups,
            "Расположение",
            semantic_property(
                "GroupVerticalAlign",
                vertical_align,
                index=vertical_align_index,
                source="form_payload_container_layout_tail",
            ),
        )
        mark_semantic_parameter_mapped(semantic, vertical_align_index)
    if row.get("type_name") == "Группа":
        horizontal_stretch = bool_presentation(child_direct_scalar(node, 14))
        vertical_stretch = bool_presentation(child_direct_scalar(node, 15))
        if horizontal_stretch is not None:
            add_grouped_property(
                groups,
                "Расположение",
                semantic_property("РастягиватьПоГоризонтали", horizontal_stretch, index=14, source="form_payload_group_layout"),
            )
            mark_semantic_parameter_mapped(semantic, 14)
        if vertical_stretch is not None:
            add_grouped_property(
                groups,
                "Расположение",
                semantic_property("РастягиватьПоВертикали", vertical_stretch, index=15, source="form_payload_group_layout"),
            )
            mark_semantic_parameter_mapped(semantic, 15)
        managed_group_options = child_at(node, 20)
        managed_group_layout = child_direct_scalar(managed_group_options, 0) == "29"
        legacy_group_mode = child_direct_scalar(node, 22)
        compact_vertical_weak = len(node_items) == 30 and child_direct_scalar(node, 21) == "0" and child_direct_scalar(node, 24) == "1"
        if managed_group_layout:
            primary_group = child_direct_scalar(managed_group_options, 1)
            grouping_variant = child_direct_scalar(managed_group_options, 4)
            extended_grouping_variant = child_direct_scalar(managed_group_options, 22)
            representation_group = child_direct_scalar(managed_group_options, 3)
            layout_group = None
            grouping_index = 20
            representation_index = 20
            grouping = (
                "Vertical"
                if primary_group == "0"
                else ("HorizontalIfPossible" if grouping_variant == "1" or extended_grouping_variant == "2" else "AlwaysHorizontal")
            )
            if child_direct_scalar(managed_group_options, 27) == "1":
                grouping = "Horizontal"
            representation = {"0": "None", "2": "WeakSeparation", "3": "NormalSeparation"}.get(str(representation_group or ""), "None")
            group_mode = grouping_variant
            united = bool_presentation(child_direct_scalar(managed_group_options, 21))
            if united is not None:
                add_grouped_property(
                    groups,
                    "Прочее",
                    semantic_property("United", united, index=20, source="form_payload_managed_group_layout"),
                )
            back_color = style_value_presentation(children(managed_group_options)[9] if len(children(managed_group_options)) > 9 else None)
            if back_color is not None:
                add_grouped_property(
                    groups,
                    "Оформление",
                    semantic_property("ЦветФона", back_color, index=20, source="form_payload_managed_group_layout"),
                )
            child_items_width = {"1": "Equal", "5": "LeftNarrowest"}.get(str(child_direct_scalar(managed_group_options, 2) or ""))
            if child_items_width is not None:
                add_grouped_property(
                    groups,
                    "Расположение",
                    semantic_property("ChildItemsWidth", child_items_width, index=20, source="form_payload_managed_group_layout"),
                )
            vertical_spacing = {"2": "Half"}.get(str(child_direct_scalar(managed_group_options, 16) or ""))
            if vertical_spacing is not None:
                add_grouped_property(
                    groups,
                    "Расположение",
                    semantic_property("VerticalSpacing", vertical_spacing, index=20, source="form_payload_managed_group_layout"),
                )
            horizontal_align = {"3": "Center"}.get(str(child_direct_scalar(managed_group_options, 27) or ""))
            if horizontal_align is not None:
                add_grouped_property(
                    groups,
                    "Расположение",
                    semantic_property("ГоризонтальноеПоложениеВГруппе", horizontal_align, index=20, source="form_payload_managed_group_layout"),
                )
            managed_horizontal_align = horizontal_align_presentation(child_direct_scalar(managed_group_options, 17))
            if managed_horizontal_align not in {None, "Auto"}:
                groups["Расположение"] = [
                    prop for prop in groups.get("Расположение", []) if prop.get("name") != "GroupHorizontalAlign"
                ]
                add_grouped_property(
                    groups,
                    "Расположение",
                    semantic_property(
                        "ГоризонтальноеПоложениеВГруппе",
                        managed_horizontal_align,
                        index=20,
                        source="form_payload_managed_group_layout",
                    ),
                )
            tooltip_representation = {"1": "Button", "2": "None"}.get(str(child_direct_scalar(managed_group_options, 22) or ""))
            if tooltip_representation is not None:
                add_grouped_property(
                    groups,
                    "Оформление",
                    semantic_property("ОтображениеПодсказки", tooltip_representation, index=20, source="form_payload_managed_group_layout"),
                )
        elif compact_vertical_weak:
            group_mode = "0"
            primary_group = None
            representation_group = None
            layout_group = child_direct_scalar(node, 26)
            grouping_index = 21
            representation_index = 24
            grouping = "Vertical"
            representation = "WeakSeparation"
        elif legacy_group_mode in {"2", "3"}:
            group_mode = legacy_group_mode
            primary_group = child_direct_scalar(node, 23)
            representation_group = child_direct_scalar(node, 25)
            layout_group = child_direct_scalar(node, 27)
            grouping_index = 22
            representation_index = 25
        else:
            group_mode = child_direct_scalar(node, 21)
            primary_group = child_direct_scalar(node, 22)
            representation_group = child_direct_scalar(node, 24)
            layout_group = child_direct_scalar(node, 26)
            grouping_index = 21
            representation_index = 24
        if not managed_group_layout and not compact_vertical_weak:
            weak_representation = bool(primary_group and representation_group and primary_group != representation_group)
            typed_primary_group = child_scalar(node, 22)
            typed_representation_group = child_scalar(node, 24)
            compact_horizontal_if_possible = (
                group_mode == "2"
                and child_direct_scalar(node, 12) == "0"
                and typed_primary_group == "3d3cb80c-508b-41fa-8a18-680cdf5f1712"
                and typed_representation_group == "77ffcc29-7f2d-4223-b22f-19666e7250ba"
            )
            grouping = (
                "HorizontalIfPossible"
                if compact_horizontal_if_possible
                else ("Vertical" if weak_representation else ("HorizontalIfPossible" if group_mode == "3" else "AlwaysHorizontal"))
            )
            representation = "WeakSeparation" if weak_representation or compact_horizontal_if_possible else "None"
        else:
            weak_representation = True
        add_grouped_property(groups, "Расположение", semantic_property("Группировка", grouping, index=grouping_index, source="form_payload_group_layout"))
        add_grouped_property(groups, "Поведение", semantic_property("Поведение", "Usual", source="form_payload_group_layout"))
        add_grouped_property(groups, "Оформление", semantic_property("Отображение", representation, index=representation_index, source="form_payload_group_layout"))
        if managed_group_layout:
            add_grouped_property(groups, "Оформление", semantic_property("ПоказыватьЗаголовок", False, index=20, source="form_payload_managed_group_layout"))
            mark_semantic_parameter_mapped(semantic, 20)
        elif compact_vertical_weak:
            add_grouped_property(groups, "Оформление", semantic_property("ПоказыватьЗаголовок", False, source="live_sql_compact_group_layout"))
        elif not weak_representation and layout_group:
            add_grouped_property(groups, "Оформление", semantic_property("ПоказыватьЗаголовок", False, source="form_payload_group_layout"))


def enrich_table_addition_specific_semantics(row: dict[str, Any], node: Any) -> None:
    if str(row.get("marker") or "") != "6":
        return
    semantic = row.setdefault("semantic", {})
    groups = semantic.setdefault("groups", {})
    properties = [
        ("Основные", "Видимость", bool_presentation(child_direct_scalar(node, 9)), 9),
        ("Основные", "Доступность", bool_presentation(child_direct_scalar(node, 10)), 10),
    ]
    for group, name, value, index in properties:
        if value is not None:
            add_grouped_property(groups, group, semantic_property(name, value, index=index, source="form_payload_table_addition"))
            mark_semantic_parameter_mapped(semantic, index)


def enrich_dynamic_list_specific_semantics(row: dict[str, Any], node: Any) -> None:
    if str(row.get("marker") or "") != "55":
        return
    semantic = row.setdefault("semantic", {})
    groups = semantic.setdefault("groups", {})
    add_grouped_property(groups, "Основные", semantic_property("Вид", "Динамический список", source="form_payload_dynamic_list"))
    visible = bool_presentation(child_direct_scalar(node, 13))
    if visible is not None:
        add_grouped_property(groups, "Основные", semantic_property("Видимость", visible, index=13, source="form_payload_dynamic_list"))
        mark_semantic_parameter_mapped(semantic, 13)
    properties = [
        ("Основные", "ПоложениеКоманднойПанели", {"0": "None", "1": "Top", "2": "Bottom"}.get(str(child_direct_scalar(node, 6) or "")), 6),
        ("Основные", "АктивизироватьПоУмолчанию", bool_presentation(child_direct_scalar(node, 16)), 16),
        ("Основные", "ТолькоПросмотр", bool_presentation(child_direct_scalar(node, 14)), 14),
        ("Использование", "ПропускатьПриВводе", bool_presentation(child_direct_scalar(node, 15)), 15),
        ("Использование", "ChangeRowSet", bool_presentation(child_direct_scalar(node, 17)), 17),
        ("Использование", "ChangeRowOrder", bool_presentation(child_direct_scalar(node, 18)), 18),
        ("Оформление", "Отображение", "List", None),
        ("Оформление", "ЦветРамки", "style:BorderColor", None),
        ("Расположение", "HeightInTableRows", child_direct_scalar(node, 21), 21),
        ("Оформление", "Footer", bool_presentation(child_direct_scalar(node, 28)), 28),
        ("Использование", "RowSelectionMode", row_selection_mode_presentation(child_direct_scalar(node, 31)), 31),
        ("Оформление", "HorizontalLinesBWA", bool_presentation(child_direct_scalar(node, 33)), 33),
        ("Оформление", "VerticalLinesBWA", bool_presentation(child_direct_scalar(node, 34)), 34),
        ("Оформление", "UseAlternationRowColorBWA", bool_presentation(child_direct_scalar(node, 35)), 35),
        ("Использование", "AutoInsertNewRow", bool_presentation(child_direct_scalar(node, 42)), 42),
        ("Использование", "EnableStartDrag", bool_presentation(child_direct_scalar(node, 41)), 41),
        ("Использование", "EnableDrag", bool_presentation(child_direct_scalar(node, 53)), 53),
    ]
    for group, name, value, index in properties:
        if value is not None:
            add_grouped_property(groups, group, semantic_property(name, value, index=index, source="form_payload_dynamic_list"))
            if index is not None:
                mark_semantic_parameter_mapped(semantic, index)
    property_count = child_direct_scalar(node, 54)
    if property_count and property_count.isdigit():
        pair_count = int(property_count)
        property_values: dict[str, tuple[Any, int]] = {}
        property_keys: set[str] = set()
        pair_index = 55
        for _ in range(pair_count):
            key = child_direct_scalar(node, pair_index)
            if key:
                property_keys.add(key)
            value_node = child_at(node, pair_index + 1)
            value_kind = child_direct_scalar(value_node, 0)
            if key and value_kind == "B":
                property_values[key] = (bool_presentation(child_direct_scalar(value_node, 1)), pair_index + 1)
            elif key and value_kind == "N":
                property_values[key] = (child_direct_scalar(value_node, 1), pair_index + 1)
            elif key and value_kind == "#":
                encoded_value = child_direct_scalar(value_node, 2)
                if encoded_value is None and key in {"14", "16"}:
                    encoded_value = next((value for value in reversed(atoms(value_node, limit=20)) if value in {"0", "1", "2"}), None)
                property_values[key] = (encoded_value, pair_index + 1)
            elif key == "16":
                value_atoms = atoms(value_node, limit=20)
                encoded_value = next((value for value in reversed(value_atoms) if value in {"0", "1"}), None)
                if encoded_value is None and "U" in value_atoms:
                    encoded_value = "1"
                if encoded_value is None:
                    encoded_value = "1"
                property_values[key] = (encoded_value, pair_index + 1)
            pair_index += 2
        dynamic_properties = [
            ("Использование", "AutoRefresh", property_values.get("5")),
            ("Использование", "AutoRefreshPeriod", property_values.get("6")),
            ("Использование", "ChoiceFoldersAndItems", property_values.get("8")),
            ("Использование", "RestoreCurrentRow", property_values.get("9")),
            ("Использование", "ShowRoot", property_values.get("11")),
            ("Использование", "AllowRootChoice", property_values.get("12")),
            ("Использование", "UpdateOnDataChange", property_values.get("14")),
            ("Использование", "AllowGettingCurrentRowURL", property_values.get("16")),
        ]
        for group, name, value_and_index in dynamic_properties:
            if value_and_index is None:
                continue
            value, index = value_and_index
            if name == "ChoiceFoldersAndItems":
                value = choice_folders_and_items_presentation(value)
            elif name == "UpdateOnDataChange":
                value = {"0": "Auto", "1": "Always", "2": "Never"}.get(str(value or ""))
            elif name == "AllowGettingCurrentRowURL":
                value = bool_presentation(value)
            if value is not None:
                add_grouped_property(groups, group, semantic_property(name, value, index=index, source="form_payload_dynamic_list_property_bag"))
                mark_semantic_parameter_mapped(semantic, index)
        if "16" in property_keys and "AllowGettingCurrentRowURL" not in {
            str(prop.get("name") or "") for prop in groups.get("Использование", [])
        }:
            add_grouped_property(
                groups,
                "Использование",
                semantic_property(
                    "AllowGettingCurrentRowURL",
                    True,
                    source="form_payload_dynamic_list_property_bag_default",
                ),
            )
        user_settings_group = child_at(node, pair_index)
        user_settings_group_ids = [value for value in atoms(user_settings_group, limit=20) if value not in {"", "0"} and value.isdigit()]
        if user_settings_group_ids:
            row["_user_settings_group_ids"] = user_settings_group_ids
            mark_semantic_parameter_mapped(semantic, pair_index)
        initial_tree_view_node = child_at(node, pair_index + 1)
        initial_tree_view = {"0": "ExpandTopLevel", "1": "DoNotExpand", "2": "ExpandAll"}.get(
            str(child_direct_scalar(initial_tree_view_node, 0) or "")
        )
        if initial_tree_view is not None:
            add_grouped_property(
                groups,
                "Использование",
                semantic_property("InitialTreeView", initial_tree_view, index=pair_index + 1, source="form_payload_dynamic_list_tail"),
            )
            mark_semantic_parameter_mapped(semantic, pair_index + 1)
        row_picture_field = child_direct_scalar(node, pair_index + 2)
        if row_picture_field not in {None, "", "0"} and row.get("name"):
            add_grouped_property(
                groups,
                "Использование",
                semantic_property(
                    "RowPictureDataPath",
                    f"{row.get('name')}.DefaultPicture",
                    index=pair_index + 2,
                    source="form_payload_dynamic_list_standard_field_reference",
                ),
            )
            mark_semantic_parameter_mapped(semantic, pair_index + 2)
    items = children(node)
    has_location_tail = (
        len(items) >= 30
        and child_direct_scalar(child_at(node, len(items) - 26), 0) == "12"
        and child_direct_scalar(child_at(node, len(items) - 21), 0) == "5"
        and child_direct_scalar(child_at(node, len(items) - 19), 0) == "5"
        and child_direct_scalar(child_at(node, len(items) - 17), 0) == "5"
    )
    if has_location_tail:
        relative_properties = [
            ("Расположение", "SearchStringLocation", search_string_location_presentation, -25),
            ("Расположение", "ViewStatusLocation", view_status_location_presentation, -24),
            ("Расположение", "SearchControlLocation", search_control_location_presentation, -23),
            ("Использование", "FileDragMode", file_drag_mode_presentation, -2),
        ]
        for group, name, presenter, offset in relative_properties:
            raw_value, index = child_direct_scalar_from_end(node, offset)
            value = presenter(raw_value)
            if value is not None and index is not None:
                add_grouped_property(groups, group, semantic_property(name, value, index=index, source="form_payload_dynamic_list_tail"))
                mark_semantic_parameter_mapped(semantic, index)


def row_selection_mode_presentation(value: str | None) -> str | None:
    return {"2": "Cell"}.get(str(value) if value is not None else "")


def search_string_location_presentation(value: str | None) -> str | None:
    return {
        "0": "Default",
        "1": "None",
        "2": "CommandBar",
        "3": "Top",
        "5": "FormCaption",
        "6": "PullFromTop",
    }.get(str(value) if value is not None else "")


def view_status_location_presentation(value: str | None) -> str | None:
    return {"0": "Default", "1": "None", "2": "Top"}.get(str(value) if value is not None else "")


def search_control_location_presentation(value: str | None) -> str | None:
    return {"0": "Default", "1": "None", "2": "CommandBar"}.get(str(value) if value is not None else "")


def file_drag_mode_presentation(value: str | None) -> str | None:
    return {"0": "AsFile", "1": "Default"}.get(str(value) if value is not None else "")


def table_height_in_rows_parameter(node: Any) -> tuple[str | None, int]:
    live_value = child_direct_scalar(node, 38)
    legacy_value = child_direct_scalar(node, 39)
    if live_value not in {None, "", "0"} and legacy_value in {None, "", "0"}:
        return live_value, 38
    return legacy_value, 39


def enrich_table_specific_semantics(row: dict[str, Any], node: Any) -> None:
    if str(row.get("marker") or "") != "73":
        return
    semantic = row.setdefault("semantic", {})
    groups = semantic.setdefault("groups", {})
    height_value, height_index = table_height_in_rows_parameter(node)
    enable_start_drag_value = bool_presentation(child_direct_scalar(node, 43))
    enable_start_drag_index = 43
    if enable_start_drag_value is None:
        enable_start_drag_value = bool_presentation(child_direct_scalar(node, 41))
        enable_start_drag_index = 41
    properties = [
        ("Основные", "Видимость", False if child_direct_scalar(node, 72) == "0" else None, 72),
        ("Основные", "Доступность", False if child_direct_scalar(node, 13) == "0" else None, 13),
        ("Оформление", "Отображение", "List", None),
        ("Расположение", "HeightInTableRows", height_value, height_index),
        ("Использование", "RowSelectionMode", row_selection_mode_presentation(child_direct_scalar(node, 31)), 31),
        ("Оформление", "HorizontalLinesBWA", bool_presentation(child_direct_scalar(node, 33)), 33),
        ("Оформление", "VerticalLinesBWA", bool_presentation(child_direct_scalar(node, 34)), 34),
        ("Оформление", "UseAlternationRowColorBWA", bool_presentation(child_direct_scalar(node, 35)), 35),
        ("Использование", "AutoInsertNewRow", bool_presentation(child_direct_scalar(node, 42)), 42),
        ("Использование", "EnableStartDrag", enable_start_drag_value, enable_start_drag_index),
        ("Использование", "EnableDrag", bool_presentation(child_direct_scalar(node, 53)), 53),
    ]
    for group, name, value, index in properties:
        if value not in {None, ""}:
            add_grouped_property(groups, group, semantic_property(name, value, index=index, source="form_payload_table"))
            if index is not None:
                mark_semantic_parameter_mapped(semantic, index)


def enrich_command_button_specific_semantics(row: dict[str, Any], node: Any) -> None:
    if str(row.get("marker") or "") != "31":
        return
    semantic = row.setdefault("semantic", {})
    groups = semantic.setdefault("groups", {})
    command_bar_location_raw = child_direct_scalar(node, 14)
    command_bar_location = command_bar_location_presentation(command_bar_location_raw)
    if command_bar_location_raw == "2":
        command_bar_location = "InCommandBar" if child_direct_scalar(node, 15) == "1" else "В дополнительном подменю"
    properties = [
        ("Основные", "Видимость", {"0": False, "1": True, "2": True}.get(str(child_direct_scalar(node, 49) or "")), 49),
        ("Основные", "Отображение", button_representation_presentation(child_direct_scalar(node, 10)), 10),
        ("Основные", "Доступность", bool_presentation(child_direct_scalar(node, 31)), 31),
        ("Основные", "ButtonImportance", button_importance_presentation(child_direct_scalar(node, 11)), 11),
        ("Основные", "КнопкаПоУмолчанию", bool_presentation(child_direct_scalar(node, 11)), 11),
        ("Использование", "ПропускатьПриВводе", {"0": False, "1": True, "2": False}.get(str(child_direct_scalar(node, 29) or "")), 29),
        ("Расположение", "ПоложениеВКоманднойПанели", command_bar_location, 14),
        ("Расположение", "УникальностьКоманды", bool_presentation(child_direct_scalar(node, 26)), 26),
        ("Расположение", "GroupHorizontalAlign", horizontal_align_presentation(child_direct_scalar(node, 41)), 41),
        ("Расположение", "GroupVerticalAlign", vertical_align_presentation(child_direct_scalar(node, 42)), 42),
        ("Оформление", "ЦветРамки", auto_enum_presentation(child_direct_scalar(node, 43)), 43),
        ("Оформление", "ShapeRepresentation", "None" if child_direct_scalar(node, 45) == "3" else None, 45),
        ("Использование", "ToolTipRepresentation", {"1": "Button", "2": "Balloon"}.get(str(child_direct_scalar(node, 50) or "")), 50),
    ]
    for group, name, value, index in properties:
        if value is not None:
            add_grouped_property(groups, group, semantic_property(name, value, index=index, source="form_payload_command_button"))
            mark_semantic_parameter_mapped(semantic, index)


def enrich_decoration_specific_semantics(row: dict[str, Any], node: Any) -> None:
    if str(row.get("marker") or "") != "12":
        return
    semantic = row.setdefault("semantic", {})
    groups = semantic.setdefault("groups", {})
    width = child_direct_scalar(node, 10)
    specific = child_at(node, 18)
    properties = [
        ("Ширина", width if width not in {None, "", "0"} else None, 10, "Расположение"),
        ("Высота", child_direct_scalar(node, 11) if child_direct_scalar(node, 11) not in {None, "", "0"} else None, 11, "Расположение"),
        ("РастягиватьПоГоризонтали", auto_bool_presentation(child_direct_scalar(node, 12)), 12, "Расположение"),
        ("РастягиватьПоВертикали", auto_bool_presentation(child_direct_scalar(node, 13)), 13, "Расположение"),
        ("ЦветТекста", style_value_presentation(child_at(node, 14)), 14, "Оформление"),
        ("ЦветФона", style_value_presentation(child_at(specific, 6)), 18, "Оформление"),
        ("Видимость", bool_presentation(child_direct_scalar(node, 21)), 21, "Основные"),
        ("АвтоМаксимальнаяШирина", bool_presentation(child_direct_scalar(node, 27)), 27, "Расположение"),
        ("МаксимальнаяШирина", child_direct_scalar(node, 28) if child_direct_scalar(node, 28) not in {None, "", "0"} else None, 28, "Расположение"),
        ("АвтоМаксимальнаяВысота", bool_presentation(child_direct_scalar(node, 29)), 29, "Расположение"),
        ("МаксимальнаяВысота", child_direct_scalar(node, 31) if child_direct_scalar(node, 31) not in {None, "", "0"} else None, 31, "Расположение"),
        (
            "ГоризонтальноеПоложениеВГруппе",
            horizontal_align_presentation(child_direct_scalar(specific, 2))
            if child_direct_scalar(specific, 2) not in {None, "", "0"}
            else None,
            18,
            "Расположение",
        ),
        ("ОтображениеПодсказки", {"2": "Balloon"}.get(str(child_direct_scalar(node, 22) or "")), 22, "Оформление"),
        ("Гиперссылка", bool_presentation(child_direct_scalar(specific, 1)), 18, "Использование"),
        ("GroupHorizontalAlign", horizontal_align_presentation(child_direct_scalar(node, 32)), 32),
        ("GroupVerticalAlign", vertical_align_presentation(child_direct_scalar(node, 33)), 33),
    ]
    for item in properties:
        name, value, index = item[:3]
        group = item[3] if len(item) > 3 else "Расположение"
        if value is not None:
            add_grouped_property(
                groups,
                group,
                semantic_property(name, value, index=index, source="form_payload_decoration"),
            )
            mark_semantic_parameter_mapped(semantic, index)


def enrich_radio_button_specific_semantics(row: dict[str, Any], node: Any) -> None:
    if str(row.get("marker") or "") != "37" or str(row.get("type_code") or "") != "5":
        return
    specific = child_at(node, 39)
    if child_direct_scalar(specific, 0) != "8":
        return
    radio_button_type = {"0": "Auto", "2": "Tumbler"}.get(str(child_direct_scalar(specific, 7) or ""))
    if radio_button_type is None:
        return
    semantic = row.setdefault("semantic", {})
    groups = semantic.setdefault("groups", {})
    add_grouped_property(
        groups,
        "Прочее",
        semantic_property("RadioButtonType", radio_button_type, index=39, source="form_payload_radio_button"),
    )
    columns_count = child_direct_scalar(specific, 2)
    if columns_count not in {None, "", "0"}:
        add_grouped_property(
            groups,
            "Расположение",
            semantic_property("ColumnsCount", columns_count, index=39, source="form_payload_radio_button"),
        )
    mark_semantic_parameter_mapped(semantic, 39)


def enrich_form_button_specific_semantics(row: dict[str, Any], node: Any) -> None:
    if str(row.get("marker") or "") != "34":
        return
    semantic = row.setdefault("semantic", {})
    groups = semantic.setdefault("groups", {})
    properties = [
        ("Основные", "Видимость", False if child_direct_scalar(node, 26) == "0" else None, 26),
        ("Основные", "Доступность", False if child_direct_scalar(node, 7) == "0" else None, 7),
    ]
    for group, name, value, index in properties:
        if value is not None:
            add_grouped_property(groups, group, semantic_property(name, value, index=index, source="form_payload_button"))
            mark_semantic_parameter_mapped(semantic, index)


def form_button_command_binding(node: Any, base_path: str) -> dict[str, Any] | None:
    if child_direct_scalar(node, 0) != "34":
        return None
    binding = children(node)[8] if len(children(node)) > 8 else None
    binding_items = children(binding)
    if len(binding_items) < 2:
        return None
    command_id = direct_scalar(binding_items[0])
    group_guid = direct_scalar(binding_items[1])
    if command_id in {None, ""} or group_guid in {None, ""}:
        return None
    group_guid = str(group_guid).lower()
    result: dict[str, Any] = {
        "command_id": str(command_id),
        "group_guid": group_guid,
        "path": path_join(base_path, 8),
        "command_id_path": path_join(path_join(base_path, 8), 0),
        "group_guid_path": path_join(path_join(base_path, 8), 1),
    }
    standard_name = FORM_STANDARD_COMMANDS.get((group_guid, str(command_id)))
    if standard_name:
        result.update({"command_name": standard_name, "scope": "standard", "match_by": "standard_command_guid"})
    elif group_guid == FORM_LOCAL_COMMAND_GROUP_GUID:
        result.update({"scope": "form", "match_by": "form_command_id"})
    else:
        result.update({"scope": "unknown", "match_by": "command_binding"})
    return result


def form_command_reference(node: Any, base_path: str) -> dict[str, Any] | None:
    """Expose metadata-command references used by command-bar buttons."""
    if child_direct_scalar(node, 0) != "31":
        return None
    reference = children(node)[8] if len(children(node)) > 8 else None
    values = atoms(reference, limit=10)
    command_guid = next((value.lower() for value in values if GUID_RE.fullmatch(value or "")), None)
    if not command_guid:
        return None
    command_code = next((value for value in values if re.fullmatch(r"-?\d+", value or "")), None)
    result = {
        "guid": command_guid,
        **({"code": command_code} if command_code is not None else {}),
        "path": path_join(base_path, 8),
        "scope": "metadata",
        "status": "identity_pending",
    }
    standard_name = FORM_STANDARD_COMMANDS.get((command_guid, str(command_code or "")))
    if standard_name:
        result.update({"command_name": standard_name, "scope": "standard", "status": "ok", "match_by": "standard_command_guid"})
    elif command_guid == FORM_LOCAL_COMMAND_GROUP_GUID and command_code is not None:
        result.update({"command_id": command_code, "scope": "form", "status": "ok", "match_by": "form_command_id"})
    return result


def edit_mode_presentation(value: str | None) -> str | None:
    return {
        "3": "EnterOnInput",
    }.get(str(value) if value is not None else "")


def multiline_presentation(value: str | None) -> bool | None:
    if value == "1":
        return True
    if value == "2":
        return False
    return None


def form_field_details_node(node: Any) -> tuple[Any | None, int | None]:
    for index in (39, 40):
        details = children(node)[index] if len(children(node)) > index else None
        if details is not None and child_direct_scalar(details, 0) == "38":
            return details, index
    return None, None


def enrich_form_field_specific_semantics(row: dict[str, Any], node: Any) -> None:
    if str(row.get("marker") or "") != "48":
        return
    semantic = row.setdefault("semantic", {})
    groups = semantic.setdefault("groups", {})
    field_properties = [
        ("Основные", "ПоложениеЗаголовка", "None" if child_direct_scalar(node, 7) == "0" else None, 7),
        ("Основные", "Видимость", False if child_direct_scalar(node, 43) == "0" else None, 43),
        ("Основные", "Доступность", False if child_direct_scalar(node, 13) == "0" else None, 13),
        ("Основные", "ТолькоПросмотр", True if child_direct_scalar(node, 14) == "1" else None, 14),
        ("Основные", "ПропускатьПриВводе", True if child_direct_scalar(node, 15) == "1" else None, 15),
    ]
    for group, name, value, index in field_properties:
        if value is not None:
            add_grouped_property(groups, group, semantic_property(name, value, index=index, source="form_payload_field"))
            mark_semantic_parameter_mapped(semantic, index)
    details, details_index = form_field_details_node(node)
    if details is not None and details_index is not None:
        auto_max_width = bool_presentation(child_direct_scalar(details, 49))
        multiline = multiline_presentation(child_direct_scalar(details, 8))
        properties = [
            ("Расположение", "Ширина", child_direct_scalar(details, 2), details_index),
            ("Расположение", "Высота", child_direct_scalar(details, 3), details_index),
            ("Расположение", "АвтоМаксимальнаяШирина", False if auto_max_width is False else None, details_index),
            ("Использование", "MultiLine", True if multiline is True else None, details_index),
        ]
        for group, name, value, index in properties:
            if value not in {None, "", "0"}:
                add_grouped_property(groups, group, semantic_property(name, value, index=index, source="form_payload_field_details"))
    edit_mode_index = 62
    edit_mode = edit_mode_presentation(child_direct_scalar(node, edit_mode_index))
    if edit_mode is None:
        edit_mode_index = 61
        edit_mode = edit_mode_presentation(child_direct_scalar(node, edit_mode_index))
    if edit_mode is not None:
        add_grouped_property(groups, "Использование", semantic_property("РежимРедактирования", edit_mode, index=edit_mode_index, source="form_payload_field"))
        mark_semantic_parameter_mapped(semantic, edit_mode_index)
    auto_edit_index = edit_mode_index + 1
    auto_edit = bool_presentation(child_direct_scalar(node, auto_edit_index))
    if edit_mode is not None and auto_edit is not None:
        add_grouped_property(groups, "Использование", semantic_property("AutoEditMode", auto_edit, index=auto_edit_index, source="form_payload_field"))
        mark_semantic_parameter_mapped(semantic, auto_edit_index)


def section_record_semantic_properties(row: dict[str, Any], parameters: list[dict[str, Any]], node: Any = None) -> dict[str, Any]:
    groups: dict[str, list[dict[str, Any]]] = {}
    mapped: set[int] = set()
    for index, (group, name) in SECTION_RECORD_SEMANTIC_PROPERTIES.items():
        mapped.add(index)
        value = parameter_value(parameters, index)
        source = "form_payload"
        # A managed-form record can store the localized title outside its
        # direct parameter #3.  The row decoder already resolves that exact
        # title path, so expose it instead of misleading an agent with an
        # empty semantic «Заголовок» beside a non-empty public row.title.
        if index == 3 and value in {None, ""} and row.get("title") not in {None, ""}:
            value = row.get("title")
            source = "form_payload_title_path"
        add_grouped_property(groups, group, semantic_property(name, value, index=index, source=source))
    if row.get("category"):
        add_grouped_property(groups, "Основные", semantic_property("Категория", row.get("category"), source="decoder"))
        add_grouped_property(groups, "Основные", semantic_property("Вид", row.get("category"), source="decoder"))
    if row.get("category") == "Command" and row.get("name"):
        raw_action = parameter_value(parameters, 8)
        explicit_action = str(raw_action or "").strip()
        action = explicit_action if BSL_IDENTIFIER_RE.fullmatch(explicit_action) else str(row.get("name") or "")
        row["action"] = action
        row["action_source"] = "form_payload_parameter_8" if explicit_action == action else "command_name_fallback"
        if explicit_action == action:
            mapped.add(8)
        add_grouped_property(
            groups,
            "Основные",
            semantic_property("Action", action, index=8 if explicit_action == action else None, source=row["action_source"]),
        )
        current_row_code = str(parameter_value(parameters, 9) or "")
        if current_row_code:
            current_row_use = {"1": "DontUse", "2": "DontUse", "3": "DontUse"}.get(current_row_code, current_row_code)
            add_grouped_property(groups, "Использование", semantic_property("CurrentRowUse", current_row_use, index=9, source="form_payload_command"))
            mapped.add(9)
        modifies_saved_data = bool_presentation(str(parameter_value(parameters, 10) or ""))
        if modifies_saved_data is not None:
            add_grouped_property(groups, "Использование", semantic_property("ModifiesSavedData", modifies_saved_data, index=10, source="form_payload_command"))
            mapped.add(10)
        representation = {"1": "Picture", "2": "TextPicture"}.get(str(child_direct_scalar(node, 9) or ""))
        if representation is not None:
            add_grouped_property(groups, "Основные", semantic_property("Отображение", representation, index=9, source="form_payload_command"))
        shortcut = shortcut_presentation(child_at(node, 6))
        if shortcut is not None:
            add_grouped_property(groups, "Использование", semantic_property("СочетаниеКлавиш", shortcut, index=6, source="form_payload_command"))
            mapped.add(6)
    if row.get("category") == "Attribute":
        main_attribute = bool_presentation(str(parameter_value(parameters, 10) or ""))
        if main_attribute is None and row.get("index") == 0 and str(row.get("name") or "") == "Объект":
            main_attribute = True
        if main_attribute is not None:
            add_grouped_property(groups, "Основные", semantic_property("MainAttribute", main_attribute, index=10 if parameter_value(parameters, 10) is not None else None, source="form_payload_attribute"))
            if parameter_value(parameters, 10) is not None:
                mapped.add(10)
        saved_data = bool_presentation(str(parameter_value(parameters, 11) or ""))
        if saved_data is not None:
            add_grouped_property(groups, "Использование", semantic_property("SavedData", saved_data, index=11, source="form_payload_attribute"))
            mapped.add(11)
        fill_check = {"1": "ShowError"}.get(str(parameter_value(parameters, 12) or ""))
        if fill_check is not None:
            add_grouped_property(groups, "Использование", semantic_property("FillCheck", fill_check, index=12, source="form_payload_attribute"))
            mapped.add(12)
    return {
        "groups": groups,
        "unmapped_parameters": semantic_unmapped_parameters(parameters, mapped),
        "coverage": semantic_coverage(parameters, mapped),
    }


def first_scalar_with_path(node: Any, base_path: str, *, skip_guids: bool = False) -> dict[str, str] | None:
    direct = scalar(node)
    if direct not in {None, ""} and (not skip_guids or not GUID_RE.fullmatch(direct)):
        return {"value": direct, "path": base_path}
    for index, child in enumerate(children(node)):
        found = first_scalar_with_path(child, path_join(base_path, index), skip_guids=skip_guids)
        if found:
            return found
    return None


def localized_text(node: Any, base_path: str, *, max_depth: int = 2) -> dict[str, str] | None:
    def walk(value: Any, path: str, depth: int) -> dict[str, str] | None:
        if depth > max_depth:
            return None
        items = children(value)
        for index in range(len(items) - 1):
            lang = scalar(items[index])
            text = scalar(items[index + 1])
            if lang and text and re.fullmatch(r"[a-z]{2}(?:[-_][A-Z]{2})?", lang):
                return {"lang": lang, "value": text, "path": path_join(path, index + 1)}
        for index, child in enumerate(items):
            found = walk(child, path_join(path, index), depth + 1)
            if found:
                return found
        return None

    return walk(node, base_path, 0)


def record_id(node: Any, base_path: str) -> dict[str, str] | None:
    items = children(node)
    if len(items) > 1:
        direct = first_scalar_with_path(items[1], path_join(base_path, 1))
        if direct:
            return direct
    return None


def reference_id_from_node(node: Any) -> str | None:
    values = atoms(node, limit=20)
    numbers = [value for value in values if re.fullmatch(r"-?\d+", value or "")]
    return numbers[-1] if numbers else None


def data_path_reference_from_node(node: Any) -> dict[str, str]:
    items = children(node)
    if len(items) >= 3 and scalar(items[0]) == "2":
        owner_id = reference_id_from_node(items[1])
        field_id = reference_id_from_node(items[2])
        if owner_id and field_id:
            return {"attribute_id": owner_id, "field_id": field_id}
    reference = reference_id_from_node(node)
    return {"attribute_id": reference} if reference else {}


def footer_data_path_reference(primary_node: Any, footer_node: Any) -> dict[str, Any] | None:
    primary_items = children(primary_node)
    primary_candidate = primary_items[3] if len(primary_items) > 3 else None
    primary_aggregate = (
        child_direct_scalar(primary_node, 0) == "3"
        and str(child_direct_scalar(primary_candidate, 0) or "").startswith("101")
    )
    aggregate_owner = primary_node if primary_aggregate else footer_node
    if child_direct_scalar(aggregate_owner, 0) != "3":
        return None
    aggregate_items = children(aggregate_owner)
    aggregate_node = aggregate_items[3] if len(aggregate_items) > 3 else None
    aggregate_code = child_direct_scalar(aggregate_node, 0)
    field_guid = child_direct_scalar(aggregate_node, 1)
    if not aggregate_code or not aggregate_code.startswith("101") or not field_guid or not GUID_RE.fullmatch(field_guid):
        return None
    if not primary_aggregate:
        primary_guids = guids(primary_node, limit=8)
        if not primary_guids or primary_guids[-1] != field_guid.lower():
            return None
    result: dict[str, Any] = {"aggregate": "Total", "field_guid": field_guid.lower()}
    if primary_aggregate:
        result["primary_aggregate"] = True
    return result


def record_name(node: Any, *, category: str | None = None) -> str | None:
    items = children(node)
    marker = child_scalar(node, 0)
    name_indexes = [3, 6] if category == "Attribute" and marker == "9" else [2, 6, 3]
    for index in name_indexes:
        if index >= len(items):
            continue
        name = scalar(items[index])
        if name and not GUID_RE.fullmatch(name) and not re.fullmatch(r"-?\d+(?:\.\d+)?", name):
            return name
    strings = collect_strings(node, limit=20)
    return next((item for item in strings if item and item not in {"#", "Pattern", "B", "U", "S", "N", "D", "ru"}), None)


def form_parameter_rows(tree: Any, *, include_parameters: bool = True, max_parameters: int = 80) -> list[dict[str, Any]]:
    section = child_at(tree, 4)
    if child_direct_scalar(section, 0) != "0":
        return []
    result: list[dict[str, Any]] = []
    for index, node in enumerate(children(section)[2:]):
        if child_direct_scalar(node, 0) != "0":
            continue
        name = str(child_direct_scalar(node, 1) or "")
        if not name or not BSL_IDENTIFIER_RE.fullmatch(name):
            continue
        key_parameter = bool_presentation(child_direct_scalar(node, 3))
        groups: dict[str, list[dict[str, Any]]] = {
            "Основные": [semantic_property("Вид", "Parameter", source="form_payload_parameter")],
        }
        if key_parameter is not None:
            add_grouped_property(
                groups,
                "Использование",
                semantic_property("KeyParameter", key_parameter, index=3, source="form_payload_parameter"),
            )
        row: dict[str, Any] = {
            "category": "Parameter",
            "type_name": "Parameter",
            "name": name,
            "path": f"4.{index + 2}",
            "semantic": {"groups": groups},
        }
        if include_parameters:
            row["parameters"] = direct_parameters(node, row["path"], limit=max_parameters)
        result.append(row)
    return result


def event_handlers(tree: Any) -> list[dict[str, Any]]:
    base_path = "1.19"
    node = get_by_path(tree, base_path)
    if node is None or not children(node):
        for candidate_path in ("1.18", "1.27"):
            candidate = get_by_path(tree, candidate_path)
            candidate_items = children(candidate)
            if any(
                GUID_RE.fullmatch(scalar(candidate_items[index]) or "") and BSL_IDENTIFIER_RE.fullmatch(scalar(candidate_items[index + 1]) or "")
                for index in range(1, max(1, len(candidate_items) - 1))
            ):
                base_path = candidate_path
                node = candidate
                break
    items = children(node)
    if not any(
        GUID_RE.fullmatch(scalar(items[index]) or "") and BSL_IDENTIFIER_RE.fullmatch(scalar(items[index + 1]) or "")
        for index in range(1, max(1, len(items) - 1))
    ):
        form_node = get_by_path(tree, "1")
        for candidate_index, candidate in enumerate(children(form_node)):
            candidate_items = children(candidate)
            if any(
                GUID_RE.fullmatch(scalar(candidate_items[index]) or "")
                and BSL_IDENTIFIER_RE.fullmatch(scalar(candidate_items[index + 1]) or "")
                for index in range(1, max(1, len(candidate_items) - 1))
            ):
                base_path = f"1.{candidate_index}"
                node = candidate
                break
    items = children(node)
    result: list[dict[str, Any]] = []
    index = 1
    while index + 1 < len(items):
        guid = scalar(items[index])
        handler = scalar(items[index + 1])
        if guid and handler and GUID_RE.fullmatch(guid) and BSL_IDENTIFIER_RE.fullmatch(handler):
            event_guid = guid.lower() if GUID_RE.fullmatch(guid) else None
            event_name = FORM_EVENT_NAMES.get(event_guid or "")
            result.append(
                {
                    "name": event_name,
                    "type_name": "Event",
                    "event_name": FORM_EVENT_NAMES.get(event_guid or ""),
                    "handler": handler,
                    "guid": event_guid,
                    "path": f"{base_path}.{index + 1}",
                }
            )
        index += 2
    return result


def event_handlers_from_node(node: Any, base_path: str, *, owner: str | None = None, max_depth: int = 3) -> list[dict[str, Any]]:
    result: list[dict[str, Any]] = []
    seen: set[tuple[str | None, str]] = set()

    def add_event(guid: str | None, handler: str, path: str) -> None:
        event_guid = guid.lower() if guid and GUID_RE.fullmatch(guid) else None
        key = (event_guid, handler.casefold())
        if key in seen:
            return
        seen.add(key)
        result.append(
            {
                "kind": "element_event",
                **({"owner": owner} if owner else {}),
                "event_name": FORM_EVENT_NAMES.get(event_guid or ""),
                "handler": handler,
                "guid": event_guid,
                "path": path,
            }
        )

    def walk(value: Any, path: str, depth: int) -> None:
        if depth > max_depth:
            return
        items = children(value)
        if depth > 0:
            marker = scalar(items[0]) if items else None
            if marker in FORM_ITEM_MARKERS:
                return
        if len(items) >= 3 and scalar(items[0]) and re.fullmatch(r"\d+", scalar(items[0]) or ""):
            index = 1
            while index + 1 < len(items):
                guid = scalar(items[index])
                handler = scalar(items[index + 1])
                if guid and handler and GUID_RE.fullmatch(guid) and BSL_IDENTIFIER_RE.fullmatch(handler):
                    add_event(guid, handler, path_join(path, index + 1))
                    index += 2
                    continue
                index += 1
        for index, child in enumerate(items):
            walk(child, path_join(path, index), depth + 1)

    walk(node, base_path, 0)
    return result


def form_item_name(node: Any, base_path: str) -> dict[str, str] | None:
    marker = child_scalar(node, 0)
    if marker == "22":
        index = 7 if not child_scalar(node, 5) and re.fullmatch(r"\d+", child_scalar(node, 6) or "") else 6
    elif marker == "31":
        index = 5
    elif marker == "34":
        direct = child_scalar(node, 5)
        index = 5 if direct and not re.fullmatch(r"-?\d+(?:\.\d+)?", direct) else 6
    elif marker in {"35", "37"}:
        index = 7 if not child_scalar(node, 5) and re.fullmatch(r"\d+", child_scalar(node, 6) or "") else 6
    elif marker == "48":
        index = 7 if children(child_at(node, 5)) else 6
    elif marker == "55":
        index = 5
    elif marker == "73":
        index = 5 if child_scalar(node, 5) and not re.fullmatch(r"-?\d+(?:\.\d+)?", child_scalar(node, 5) or "") else 6
    else:
        index = 6
    name = child_scalar(node, index)
    if name and not GUID_RE.fullmatch(name):
        return {"value": name, "path": path_join(base_path, index)}
    found = first_scalar_with_path(node, base_path, skip_guids=True)
    if found and found["value"] not in {"#", "Pattern", "B", "U", "S", "N", "D", "ru"}:
        return found
    return None


def form_item_type_code(node: Any, marker: str | None) -> str | None:
    if marker == "6":
        return child_scalar(node, 5)
    if marker == "34":
        direct = child_scalar(node, 5)
        return child_scalar(node, 4) if direct and not re.fullmatch(r"-?\d+(?:\.\d+)?", direct or "") else direct
    if marker == "48":
        return child_scalar(node, 6) if children(child_at(node, 5)) else child_scalar(node, 5)
    if marker == "22":
        direct = child_scalar(node, 5)
        shifted = child_scalar(node, 6)
        if not direct and re.fullmatch(r"\d+", shifted or ""):
            return shifted
        return direct
    if marker in {"35", "37"}:
        direct = child_scalar(node, 5)
        shifted = child_scalar(node, 6)
        if not direct and re.fullmatch(r"\d+", shifted or ""):
            return shifted
        return direct
    if marker == "12":
        return child_scalar(node, 5)
    return child_scalar(node, 0)


def embedded_table_addition_row(
    node: Any,
    path: str,
    depth: int,
    *,
    include_parameters: bool,
    max_parameters: int,
) -> dict[str, Any] | None:
    """Normalize a table addition stored as marker 5 inside a marker 55/73 tail."""
    items = children(node)
    type_code = child_direct_scalar(node, 5)
    name = child_direct_scalar(node, 6)
    identity = children(items[1]) if len(items) > 1 else []
    item_id = direct_scalar(identity[0]) if identity else None
    owner_guid = direct_scalar(identity[1]) if len(identity) > 1 else None
    if (
        len(items) < 20
        or child_direct_scalar(node, 0) != "5"
        or type_code not in FORM_TABLE_ADDITION_TYPE_NAMES
        or not name
        or not item_id
        or not re.fullmatch(r"\d+", item_id)
        or not owner_guid
        or not GUID_RE.fullmatch(owner_guid)
    ):
        return None
    evidence = collect_evidence(node)
    title = localized_text(items[7], path_join(path, 7)) if len(items) > 7 else None
    parameters = direct_parameters(node, path, roles=FORM_ITEM_PARAMETER_ROLES["6"], limit=max_parameters)
    row: dict[str, Any] = {
        "name": name,
        "name_path": path_join(path, 6),
        "id": item_id,
        "id_path": path_join(path_join(path, 1), 0),
        "title": title["value"] if title else None,
        "title_lang": title.get("lang") if title else None,
        "title_path": title["path"] if title else None,
        "path": path,
        "depth": depth,
        "marker": "6",
        "marker_name": "TableAddition",
        "type_code": type_code,
        "type_name": FORM_TABLE_ADDITION_TYPE_NAMES[type_code],
        "strings_sample": sorted(evidence["strings"])[:30],
        "guids_sample": sorted(evidence["guids"])[:20],
    }
    row["semantic"] = form_item_semantic_properties(row, parameters)
    enrich_table_addition_specific_semantics(row, node)
    row["semantic"] = public_semantic(row["semantic"], include_diagnostics=include_parameters)
    if include_parameters:
        row["parameters"] = parameters
    return row


def item_records(
    tree: Any,
    *,
    limit: int = 500,
    include_parameters: bool = True,
    max_parameters: int = 80,
) -> tuple[list[dict[str, Any]], int, bool]:
    records: list[dict[str, Any]] = []
    total = 0

    def walk(node: Any, path: list[int], depth: int) -> None:
        nonlocal total
        items = children(node)
        marker = scalar(items[0]) if items else None
        if len(items) >= 6 and marker in FORM_ITEM_MARKERS:
            total += 1
            current_path = ".".join(str(part) for part in path)
            name = form_item_name(node, current_path)
            if name and len(records) < limit:
                evidence = collect_evidence(node)
                title = localized_text(node, current_path)
                item_id = record_id(node, current_path)
                parameter_roles = FORM_ITEM_PARAMETER_ROLES.get(marker or "", {})
                type_code = form_item_type_code(node, marker)
                public_type_name = form_item_public_type_name(marker, type_code, name["value"])
                row = {
                    "name": name["value"],
                    "name_path": name["path"],
                    "id": item_id["value"] if item_id else None,
                    "id_path": item_id["path"] if item_id else None,
                    "title": title["value"] if title else None,
                    "title_lang": title.get("lang") if title else None,
                    "title_path": title["path"] if title else None,
                    "path": current_path,
                    "depth": depth,
                    "marker": marker,
                    "marker_name": MARKER_NAMES.get(marker),
                    "type_code": type_code,
                    "type_name": public_type_name,
                    "strings_sample": sorted(evidence["strings"])[:30],
                    "guids_sample": sorted(evidence["guids"])[:20],
                }
                command_binding = form_button_command_binding(node, current_path)
                if command_binding:
                    row["command_binding"] = command_binding
                command_reference = form_command_reference(node, current_path)
                if command_reference:
                    row["command_reference"] = command_reference
                    if command_reference.get("scope") == "form" and "command_binding" not in row:
                        row["command_binding"] = command_reference
                data_path_index = 12 if marker in {"48", "73"} else 11
                if marker in {"35", "37"} and str(row.get("name_path") or "").endswith(".7"):
                    data_path_index = 12
                if marker == "31":
                    data_path_index = 9
                if marker in {"31", "35", "37", "48", "55", "73"} and len(items) > data_path_index:
                    data_path_reference = data_path_reference_from_node(items[data_path_index])
                    if data_path_reference.get("attribute_id"):
                        row["data_path_attribute_id"] = data_path_reference["attribute_id"]
                    if data_path_reference.get("field_id"):
                        row["data_path_field_id"] = data_path_reference["field_id"]
                    if data_path_reference:
                        row["data_path_attribute_id_path"] = path_join(current_path, data_path_index)
                if marker in {"35", "37"} and len(items) > 12:
                    footer_reference = footer_data_path_reference(items[11], items[12])
                    if footer_reference is not None:
                        row["_footer_data_path_reference"] = footer_reference
                item_events = event_handlers_from_node(node, current_path, owner=name["value"], max_depth=3)
                if item_events:
                    row["events"] = item_events
                parameters = direct_parameters(node, current_path, roles=parameter_roles, limit=max_parameters)
                row["semantic"] = form_item_semantic_properties(row, parameters)
                enrich_container_specific_semantics(row, node)
                enrich_table_addition_specific_semantics(row, node)
                enrich_input_field_specific_semantics(row, node)
                enrich_form_field_specific_semantics(row, node)
                enrich_dynamic_list_specific_semantics(row, node)
                enrich_table_specific_semantics(row, node)
                enrich_command_button_specific_semantics(row, node)
                enrich_decoration_specific_semantics(row, node)
                enrich_radio_button_specific_semantics(row, node)
                enrich_form_button_specific_semantics(row, node)
                row["semantic"] = public_semantic(row["semantic"], include_diagnostics=include_parameters)
                if include_parameters:
                    row["parameters"] = parameters
                records.append(row)
                supports_embedded_additions = marker in {"55", "73"} or (
                    marker == "22" and str(row.get("type_name") or "") == "Командная панель"
                )
                if supports_embedded_additions:
                    for child_index, child in enumerate(items):
                        addition = embedded_table_addition_row(
                            child,
                            path_join(current_path, child_index),
                            depth + 1,
                            include_parameters=include_parameters,
                            max_parameters=max_parameters,
                        )
                        if addition is None:
                            continue
                        total += 1
                        if len(records) < limit:
                            records.append(addition)
        for index, child in enumerate(items):
            walk(child, [*path, index], depth + 1)

    root1 = get_by_path(tree, "1")
    walk(root1, [1], 0)
    enrich_item_reference_semantics(records)
    public_rows_semantics(records, include_diagnostics=include_parameters)
    return records[:limit], total, total > len(records)


def section_record_total(tree: Any, path: str) -> int:
    node = get_by_path(tree, path)
    return len(declared_child_records(node, path)) if node is not None else 0


def section_records(
    tree: Any,
    path: str,
    category: str,
    *,
    limit: int = 500,
    include_parameters: bool = True,
    max_parameters: int = 80,
) -> list[dict[str, Any]]:
    node = get_by_path(tree, path)
    if node is None:
        return []
    result = []
    for record in declared_child_records(node, path)[:limit]:
        evidence = collect_evidence(record.node)
        name = record_name(record.node, category=category)
        title = localized_text(record.node, record.path)
        item_id = record_id(record.node, record.path)
        row = {
            "category": category,
            "index": record.index,
            "path": record.path,
            "name": name,
            "id": item_id["value"] if item_id else None,
            "id_path": item_id["path"] if item_id else None,
            "title": title["value"] if title else None,
            "title_lang": title.get("lang") if title else None,
            "title_path": title["path"] if title else None,
            "marker": child_scalar(record.node, 0),
            "marker_name": MARKER_NAMES.get(child_scalar(record.node, 0) or ""),
            "strings_sample": sorted(evidence["strings"])[:30],
            "guids_sample": sorted(evidence["guids"])[:20],
        }
        parameters = direct_parameters(record.node, record.path, roles=SECTION_RECORD_PARAMETER_ROLES, limit=max_parameters)
        row["semantic"] = section_record_semantic_properties(row, parameters, record.node)
        row["semantic"] = public_semantic(row["semantic"], include_diagnostics=include_parameters)
        if include_parameters:
            row["parameters"] = parameters
        if category == "Attribute":
            dynamic_fields = dynamic_list_fields(record.node, owner_name=name, owner_path=record.path)
            if dynamic_fields:
                row["dynamic_list_fields"] = dynamic_fields
            settings = dynamic_list_settings(record.node, owner_path=record.path)
            if settings:
                row["dynamic_list_settings"] = settings
        result.append(row)
    return result


def attribute_by_id(attributes: list[dict[str, Any]]) -> dict[str, dict[str, Any]]:
    return {str(item.get("id")): item for item in attributes if item.get("id") is not None}


def dynamic_field_by_id(attributes: list[dict[str, Any]]) -> dict[str, dict[str, Any]]:
    result: dict[str, dict[str, Any]] = {}
    for attribute in attributes:
        for field in attribute.get("dynamic_list_fields") or []:
            field_id = field.get("id")
            if field_id is None:
                continue
            result[str(field_id)] = field
    return result


def dynamic_field_by_owner_and_id(attributes: list[dict[str, Any]]) -> dict[tuple[str, str], dict[str, Any]]:
    result: dict[tuple[str, str], dict[str, Any]] = {}
    for attribute in attributes:
        owner_id = attribute.get("id")
        if owner_id is None:
            continue
        for field in attribute.get("dynamic_list_fields") or []:
            field_id = field.get("id")
            if field_id is None:
                continue
            result[(str(owner_id), str(field_id))] = field
    return result


def parameter_reference_id(parameter: dict[str, Any] | None) -> str | None:
    if not parameter:
        return None
    values = parameter.get("values_sample") or []
    numbers = [str(item.get("value")) for item in values if str(item.get("value") or "").isdigit()]
    if numbers:
        return numbers[-1]
    value = parameter.get("value")
    return str(value) if str(value or "").isdigit() else None


def enrich_item_data_paths(items: list[dict[str, Any]], attributes: list[dict[str, Any]]) -> None:
    by_id = attribute_by_id(attributes)
    by_name = {str(attribute.get("name") or ""): attribute for attribute in attributes if attribute.get("name")}
    dynamic_by_id = dynamic_field_by_id(attributes)
    dynamic_by_owner_and_id = dynamic_field_by_owner_and_id(attributes)
    table_items = [
        item
        for item in items
        if str(item.get("marker") or "") in {"55", "73"} and item.get("name") and item.get("path")
    ]
    table_items.sort(key=lambda item: len(str(item.get("path") or "").split(".")), reverse=True)

    def fallback_data_path(item: dict[str, Any]) -> tuple[str | None, dict[str, Any] | None, str | None]:
        item_name = str(item.get("name") or "")
        if not item_name:
            return None, None, None
        attribute = by_name.get(item_name)
        if attribute is not None:
            return item_name, attribute, "form_attribute_name_match"
        item_path = str(item.get("path") or "")
        for table_item in table_items:
            table_path = str(table_item.get("path") or "")
            table_name = str(table_item.get("name") or "")
            if not table_path or not table_name or not item_path.startswith(table_path + ".") or not item_name.startswith(table_name):
                continue
            table_attribute = by_name.get(table_name)
            field_name = item_name[len(table_name) :]
            if not field_name:
                continue
            if table_attribute is not None:
                for field in table_attribute.get("dynamic_list_fields") or []:
                    if field_name in {str(field.get("name") or ""), str(field.get("data_name") or "")}:
                        return str(field.get("path_to_data") or f"{table_name}.{field_name}"), table_attribute, "tabular_attribute_field_name_match"
            object_attribute = by_name.get("Объект")
            if object_attribute is not None:
                public_field_name = FORM_PUBLIC_DATA_FIELD_NAMES.get(field_name, field_name)
                return f"Объект.{table_name}.{public_field_name}", object_attribute, "object_tabular_item_name_match"
        return None, None, None

    for item in items:
        marker = str(item.get("marker") or "")
        if marker not in {"31", "35", "37", "48", "55", "73"}:
            continue
        parameters = item.get("parameters") or []
        data_path_index = 9 if marker == "31" else (12 if marker in {"48", "73"} else 11)
        reference = item.get("data_path_attribute_id") or parameter_reference_id(next((parameter for parameter in parameters if parameter.get("index") == data_path_index), None))
        if not reference:
            continue
        field_id = item.get("data_path_field_id")
        attribute = by_id.get(reference)
        dynamic_field = dynamic_by_owner_and_id.get((str(reference), str(field_id))) if field_id else None
        fallback_source = None
        standard_field = FORM_STANDARD_DATA_FIELDS.get(str(field_id or ""))
        if standard_field and attribute:
            attribute_name = str(attribute.get("name") or "")
            if str(field_id) == "-2" and str(item.get("name") or "") == "Номер":
                standard_field = "Number"
            if str(field_id) == "-3" and str(item.get("name") or "") in {"Дата", "Date"}:
                standard_field = "Date"
            if attribute_name in {"Запись", "Record"} and str(item.get("name") or "") in {"Период", "Period"}:
                standard_field = "Period"
            is_dynamic_list = bool(attribute.get("dynamic_list_fields") or attribute.get("dynamic_list_settings"))
            path_to_data = (
                f"Items.{attribute_name}.CurrentData.{standard_field}"
                if is_dynamic_list
                else f"{attribute_name}.{standard_field}"
            )
            dynamic_field = None
        elif dynamic_field:
            dynamic_name = dynamic_field.get("data_name") or dynamic_field.get("name")
            if marker == "31" and attribute and attribute.get("dynamic_list_settings"):
                path_to_data = f"Items.{attribute.get('name')}.CurrentData.{dynamic_name}"
            else:
                path_to_data = dynamic_field.get("path_to_data") or dynamic_name
        elif attribute:
            attribute_name = str(attribute.get("name") or "")
            item_name = str(item.get("name") or "")
            item_path = str(item.get("path") or "")
            owner_table = next(
                (
                    table_item
                    for table_item in table_items
                    if table_item is not item
                    and str(table_item.get("path") or "")
                    and item_path.startswith(str(table_item.get("path")) + ".")
                ),
                None,
            )
            if attribute_name != "Объект" and item_name.endswith("ДатаНачала"):
                path_to_data = f"{attribute_name}.StartDate"
            elif attribute_name != "Объект" and item_name.endswith("ДатаОкончания"):
                path_to_data = f"{attribute_name}.EndDate"
            elif attribute_name in {"Запись", "Record"} and item_name and item_name != attribute_name:
                path_to_data = f"{attribute_name}.{FORM_PUBLIC_DATA_FIELD_NAMES.get(item_name, item_name)}"
            elif attribute_name != "Объект":
                path_to_data = attribute_name
            elif owner_table is not None:
                table_name = str(owner_table.get("name") or "")
                field_name = item_name[len(table_name) :] if item_name.startswith(table_name) else item_name
                public_field_name = FORM_PUBLIC_DATA_FIELD_NAMES.get(field_name, field_name)
                path_to_data = f"{attribute_name}.{table_name}.{public_field_name}"
            elif item_name and item_name != attribute_name:
                public_field_name = FORM_PUBLIC_DATA_FIELD_NAMES.get(item_name, item_name)
                path_to_data = f"{attribute_name}.{public_field_name}"
            else:
                path_to_data = attribute_name
        elif not field_id and dynamic_by_id.get(reference):
            dynamic_field = dynamic_by_id.get(reference)
            path_to_data = dynamic_field.get("path_to_data") or dynamic_field.get("data_name") or dynamic_field.get("name")
        else:
            path_to_data, attribute, fallback_source = fallback_data_path(item)
            if not path_to_data:
                continue
            dynamic_field = None
        item["path_to_data"] = path_to_data
        if attribute and attribute.get("dynamic_list_settings"):
            item["dynamic_list_settings"] = attribute.get("dynamic_list_settings")
        semantic = item.setdefault("semantic", {})
        groups = semantic.setdefault("groups", {})
        add_grouped_property(
            groups,
            "Основные",
            semantic_property(
                "ПутьКДанным",
                path_to_data,
                source=fallback_source or ("standard_data_field_reference" if standard_field else ("dynamic_list_field_reference" if dynamic_field and not attribute else "form_attribute_reference")),
            ),
        )
    for item in items:
        pending = item.pop("_type_link_reference", None)
        if not isinstance(pending, dict):
            continue
        owner_id = str(item.get("data_path_attribute_id") or "")
        field_id = str(pending.get("field_id") or "")
        attribute = by_id.get(owner_id)
        target = dynamic_by_owner_and_id.get((owner_id, field_id))
        owner_name = str((attribute or {}).get("name") or "")
        field_name = str((target or {}).get("data_name") or (target or {}).get("name") or "")
        if not owner_name or not field_name:
            continue
        semantic = item.setdefault("semantic", {})
        groups = semantic.setdefault("groups", {})
        add_grouped_property(
            groups,
            "Использование",
            semantic_property(
                "TypeLink",
                {
                    "path_to_data": f"Items.{owner_name}.CurrentData.{field_name}",
                    "link_item": int(pending.get("link_item") or 0),
                },
                index=42,
                source="form_payload_type_link_reference",
            ),
        )
        mark_semantic_parameter_mapped(semantic, 42)
    for item in items:
        pending = item.pop("_footer_data_path_reference", None)
        if not isinstance(pending, dict) or pending.get("aggregate") != "Total":
            continue
        path_to_data = str(item.get("path_to_data") or "")
        if "." not in path_to_data and pending.get("primary_aggregate"):
            field_guid = str(pending.get("field_guid") or "")
            candidates = {
                str(candidate.get("path_to_data") or "")
                for candidate in items
                if candidate is not item
                and field_guid
                and field_guid in {str(value).lower() for value in candidate.get("guids_sample") or []}
                and "." in str(candidate.get("path_to_data") or "")
            }
            if candidates:
                deepest = max(candidate.count(".") for candidate in candidates)
                deepest_candidates = {candidate for candidate in candidates if candidate.count(".") == deepest}
                if len(deepest_candidates) == 1:
                    path_to_data = deepest_candidates.pop()
        if "." not in path_to_data:
            continue
        owner_path, field_name = path_to_data.rsplit(".", 1)
        if not owner_path or not field_name:
            continue
        semantic = item.setdefault("semantic", {})
        groups = semantic.setdefault("groups", {})
        total_path = f"{owner_path}.Total{field_name}"
        property_name = "ПутьКДанным" if pending.get("primary_aggregate") else "FooterDataPath"
        if pending.get("primary_aggregate"):
            item["path_to_data"] = total_path
        add_grouped_property(
            groups,
            "Основные",
            semantic_property(property_name, total_path, index=12, source="form_payload_footer_data_reference"),
        )
        mark_semantic_parameter_mapped(semantic, 12)


def enrich_page_title_data_paths(items: list[dict[str, Any]]) -> None:
    """Resolve a page title counter from its single descendant table.

    Managed-form pages encode this through internal object-field GUIDs.  The
    descendant table has already been resolved to a public data path, so the
    public ``RowsCount`` path can be derived without exposing storage IDs.
    """
    tables = [
        item
        for item in items
        if str(item.get("marker") or "") in {"55", "73"}
        and item.get("path_to_data")
        and item.get("path")
    ]
    for page in items:
        if str(page.get("type_name") or "") != "Страница":
            continue
        page_path = str(page.get("path") or "")
        if not page_path:
            continue
        descendant_tables = [
            table for table in tables if str(table.get("path") or "").startswith(page_path + ".")
        ]
        if len(descendant_tables) != 1:
            continue
        table_path = str(descendant_tables[0].get("path_to_data") or "")
        if not table_path:
            continue
        semantic = page.setdefault("semantic", {})
        groups = semantic.setdefault("groups", {})
        add_grouped_property(
            groups,
            "Основные",
            semantic_property(
                "TitleDataPath",
                f"{table_path}.RowsCount",
                source="page_single_descendant_table",
            ),
        )


def enrich_item_event_links(items: list[dict[str, Any]], module: dict[str, Any] | None) -> None:
    names = routine_names(module)
    for item in items:
        events = item.get("events") or []
        if not events:
            continue
        links = []
        semantic = item.setdefault("semantic", {})
        groups = semantic.setdefault("groups", {})
        for event in events:
            handler = str(event.get("handler") or "")
            resolved = handler.casefold() in names
            event_presentation = str(event.get("event_name") or "")
            item_name = str(item.get("name") or "")
            if not event_presentation and item_name and handler.casefold().startswith(item_name.casefold()):
                event_presentation = handler[len(item_name) :] or "Обработчик"
            add_grouped_property(
                groups,
                "События",
                semantic_property(event_presentation or "Обработчик", handler, source="element_event_handler"),
            )
            links.append(
                {
                    "kind": "element_event",
                    "element": item.get("name"),
                    "event_name": event_presentation or event.get("event_name"),
                    "handler": handler,
                    "status": "resolved" if resolved else "missing",
                }
            )
        item["handler_links"] = links


def dynamic_list_fields(node: Any, *, owner_name: str | None, owner_path: str | None = None) -> list[dict[str, Any]]:
    values = atoms(node, limit=20000)
    fields: dict[str, dict[str, Any]] = {}
    index = 0
    typed_prefixes = {"S", "N", "B", "U", "#"}
    while index < len(values) - 1:
        key = values[index]
        value_offset = 2 if values[index + 1] in typed_prefixes and index + 2 < len(values) else 1
        value = values[index + value_offset]
        match = re.fullmatch(r"FieldsMapItem(Id|Name|SecondaryName)(\d+)", key or "")
        if match:
            field = fields.setdefault(match.group(2), {})
            if match.group(1) == "Id":
                field["id"] = value
            elif match.group(1) == "Name":
                field["data_name"] = value
            elif match.group(1) == "SecondaryName":
                field["name"] = value
        index += 1
    result = []
    seen_ids: set[str] = set()
    for ordinal in sorted(fields, key=lambda item: int(item)):
        field = fields[ordinal]
        name = field.get("name") or field.get("data_name")
        if not name:
            continue
        data_name = field.get("data_name")
        field_id = field.get("id")
        if field_id is not None:
            seen_ids.add(str(field_id))
        result.append(
            {
                "name": name,
                "data_name": data_name,
                "id": field_id,
                "path_to_data": f"{owner_name}.{data_name}" if owner_name and data_name else data_name,
                "ordinal": int(ordinal),
                "source": "dynamic_list_field_map",
            }
        )
    for index, child in enumerate(children(node)):
        if child_scalar(child, 0) != "5":
            continue
        field_id = child_scalar(child, 1)
        name = child_scalar(child, 3)
        if not field_id or not name or str(field_id) in seen_ids:
            continue
        field_path = path_join(owner_path, index) if owner_path else None
        title = localized_text(child, field_path or "")
        result.append(
            {
                "name": name,
                "data_name": name,
                "id": field_id,
                "title": title.get("value") if title else None,
                "title_lang": title.get("lang") if title else None,
                "title_path": title.get("path") if title else None,
                **({"path": field_path} if field_path else {}),
                "path_to_data": f"{owner_name}.{name}" if owner_name and name else name,
                "ordinal": len(result),
                "source": "tabular_attribute_field",
            }
        )
        seen_ids.add(str(field_id))
    return result


DYNAMIC_LIST_TYPED_PREFIXES = {"S", "N", "B", "U", "#"}
DYNAMIC_LIST_SETTING_ALIASES = {
    "main_table": {"основнаятаблица", "maintable", "source_table", "sourcetable"},
    "custom_query": {"произвольныйзапрос", "customquery", "arbitraryquery"},
    "query_text": {"текстзапроса", "querytext", "запрос", "query"},
}


def dynamic_list_bool(value: str | None) -> bool | str | None:
    if value == "1":
        return True
    if value == "0":
        return False
    return value if value not in {None, ""} else None


def next_dynamic_list_value(entries: list[dict[str, Any]], index: int) -> dict[str, Any] | None:
    next_index = index + 1
    if next_index < len(entries) and entries[next_index].get("value") in DYNAMIC_LIST_TYPED_PREFIXES:
        next_index += 1
    if next_index < len(entries):
        return entries[next_index]
    return None


def dynamic_list_settings(node: Any, *, owner_path: str | None = None) -> dict[str, Any] | None:
    entries = node_scalar_entries(node, owner_path or "", limit=30000)
    settings: dict[str, Any] = {}
    paths: dict[str, str] = {}
    sources: dict[str, str] = {}
    for index, entry in enumerate(entries):
        key = normalize_key(entry.get("value"))
        target = next((name for name, aliases in DYNAMIC_LIST_SETTING_ALIASES.items() if key in aliases), None)
        if not target:
            continue
        value_entry = next_dynamic_list_value(entries, index)
        if not value_entry:
            continue
        value = value_entry.get("value")
        settings[target] = dynamic_list_bool(value) if target == "custom_query" else value
        paths[target] = str(value_entry.get("path") or "")
        sources[target] = "key_value"
    if "query_text" not in settings:
        query_entry = next(
            (
                entry
                for entry in entries
                if isinstance(entry.get("value"), str)
                and re.search(r"(?i)\bselect\b|выбрать", str(entry.get("value") or ""))
            ),
            None,
        )
        if query_entry:
            settings["query_text"] = query_entry.get("value")
            paths["query_text"] = str(query_entry.get("path") or "")
            sources["query_text"] = "query_text_heuristic"
    if not settings:
        return None
    result = {
        "main_table": settings.get("main_table"),
        "custom_query": settings.get("custom_query"),
        "query_text": settings.get("query_text"),
        "paths": paths,
        "sources": sources,
        "status": "ok",
    }
    if settings.get("custom_query") is True and not settings.get("query_text"):
        result["status"] = "query_text_missing"
    return {key: value for key, value in result.items() if value is not None and value != "" and value != {} and value != []}


def normalize_key(value: Any) -> str:
    return re.sub(r"[\s._-]+", "", str(value or "")).casefold()


def dynamic_list_column_items(attributes: list[dict[str, Any]], *, limit: int = 5000) -> tuple[list[dict[str, Any]], int, bool]:
    records: list[dict[str, Any]] = []
    total = 0
    for attribute in attributes:
        owner_name = attribute.get("name")
        for field in attribute.get("dynamic_list_fields") or []:
            total += 1
            if len(records) >= limit:
                continue
            name = field.get("name")
            path_to_data = field.get("path_to_data")
            semantic = {
                "groups": {
                    "Основные": [
                        semantic_property("Идентификатор", field.get("id"), source="dynamic_list_field_map"),
                        semantic_property("Имя", name, source="dynamic_list_field_map"),
                        semantic_property("Заголовок", name, source="dynamic_list_field_map"),
                        semantic_property("Вид", "Колонка динамического списка", source="dynamic_list_field_map"),
                        semantic_property("ПутьКДанным", path_to_data, source="dynamic_list_field_map"),
                    ]
                },
                "coverage": {"mapped": 5, "unmapped": 0, "total": 5, "status": "ok"},
            }
            records.append(
                {
                    "name": name,
                    "id": field.get("id"),
                    "title": name,
                    "marker": "dynamic_list_field",
                    "marker_name": "DynamicListField",
                    "type_code": "dynamic_list_field",
                    "type_name": "Колонка динамического списка",
                    "owner": owner_name,
                    "data_name": field.get("data_name"),
                    "path_to_data": path_to_data,
                    "semantic": semantic,
                }
            )
    return records, total, total > len(records)


def additional_column_items(tree: Any, items: list[dict[str, Any]], *, limit: int = 5000) -> tuple[list[dict[str, Any]], int, bool]:
    """Decode managed-form AdditionalColumns stored after declared attributes."""
    section = child_at(tree, 3)
    section_items = children(section)
    declared = child_direct_scalar(section, 1)
    declared_count = int(declared) if str(declared or "").isdigit() else 0
    tail_start = min(len(section_items), 2 + declared_count)
    table_items = [
        item
        for item in items
        if str(item.get("marker") or "") in {"55", "73"} and item.get("path_to_data")
    ]
    records: list[dict[str, Any]] = []
    total = 0
    for group_index, group in enumerate(section_items[tail_start:], start=tail_start):
        if child_direct_scalar(group, 0) != "0":
            continue
        group_items = children(group)
        count_text = child_direct_scalar(group, 2)
        column_count = int(count_text) if str(count_text or "").isdigit() else 0
        owner_guids = guids(child_at(group, 1), limit=4)
        owner_guid = owner_guids[-1] if owner_guids else ""
        owners = [
            item
            for item in table_items
            if owner_guid and owner_guid in {str(value).lower() for value in item.get("guids_sample") or []}
        ]
        if len(owners) != 1:
            continue
        owner = owners[0]
        owner_path = str(owner.get("path_to_data") or "")
        for offset, column in enumerate(group_items[3 : 3 + column_count], start=3):
            if child_direct_scalar(column, 0) != "5":
                continue
            name = child_direct_scalar(column, 3)
            if not name:
                continue
            total += 1
            if len(records) >= limit:
                continue
            column_id = child_direct_scalar(column, 1)
            path_to_data = f"{owner_path}.{name}"
            records.append(
                {
                    "name": name,
                    "id": column_id,
                    "marker": "additional_column",
                    "marker_name": "AdditionalColumn",
                    "type_code": "additional_column",
                    "type_name": "Колонка реквизита",
                    "owner": owner.get("name"),
                    "owner_guid": owner_guid,
                    "path": f"3.{group_index}.{offset}",
                    "path_to_data": path_to_data,
                    "semantic": {
                        "groups": {
                            "Основные": [
                                semantic_property("Идентификатор", column_id, source="form_payload_additional_column"),
                                semantic_property("Имя", name, source="form_payload_additional_column"),
                                semantic_property("Вид", "Колонка реквизита", source="form_payload_additional_column"),
                                semantic_property("ПутьКДанным", path_to_data, source="form_payload_additional_column"),
                            ]
                        },
                        "coverage": {"mapped": 4, "unmapped": 0, "total": 4, "status": "ok"},
                    },
                }
            )
    return records, total, total > len(records)


def form_common_parameters(tree: Any, *, limit: int = 120) -> list[dict[str, Any]]:
    node = get_by_path(tree, "1")
    if node is None:
        return []
    parameters = direct_parameters(
        node,
        "1",
        roles={
            0: "Версия/тип формы",
            1: "Основные свойства формы",
            27: "События формы",
        },
        limit=limit,
    )
    for parameter in parameters:
        index = parameter.get("index")
        typed_node = child_at(node, index) if isinstance(index, int) else None
        typed_marker = child_direct_scalar(typed_node, 0)
        if typed_marker == "#":
            parameter["typed_guid"] = child_direct_scalar(typed_node, 1)
            parameter["typed_value"] = child_direct_scalar(typed_node, 2)
            parameter["typed_kind"] = "enum"
        elif typed_marker == "B":
            parameter["typed_value"] = child_direct_scalar(typed_node, 1)
            parameter["typed_kind"] = "boolean"
    return parameters


def form_common_semantic(parameters: list[dict[str, Any]], *, include_diagnostics: bool = True) -> dict[str, Any]:
    by_index = {int(item["index"]): item for item in parameters if isinstance(item, dict) and isinstance(item.get("index"), int)}
    form_version = str((by_index.get(0) or {}).get("value") or "")
    auto_save_raw = str((by_index.get(7) or {}).get("value") or "")
    auto_save_data_in_settings = {"0": "DontUse", "1": "Use"}.get(auto_save_raw)
    group_indices = [11, 40, 47, 57]
    group_values = [str((by_index.get(index) or {}).get("value") or "") for index in group_indices]
    form_group = None
    if group_values == ["0", "0", "0", "0"]:
        form_group = "Vertical"
    elif group_values == ["1", "1", "1", "1"]:
        form_group = "Horizontal"
    elif group_values == ["1", "1", "3", "3"]:
        form_group = "AlwaysHorizontal"
    elif group_values == ["1", "2", "2", "2"]:
        form_group = "HorizontalIfPossible"
    elif form_version in {"49", "50"} and group_values[0] == "0":
        form_group = "Vertical"
    window_primary = str((by_index.get(2) or {}).get("value") or "")
    window_companion = str((by_index.get(54) or {}).get("value") or "")
    window_opening_mode = None
    if (window_primary, window_companion) == ("0", "0"):
        window_opening_mode = "DontBlock"
    elif (window_primary, window_companion) == ("1", "1"):
        window_opening_mode = "LockOwner"
    elif (window_primary, window_companion) == ("2", "2"):
        window_opening_mode = "LockWholeInterface"
    elif form_version in {"49", "50"}:
        window_opening_mode = {"0": "DontBlock", "1": "LockOwner"}.get(window_primary)
    show_primary = str((by_index.get(17) or {}).get("value") or "")
    show_companion = str((by_index.get(56) or {}).get("value") or "")
    show_command_bar = None
    if (show_primary, show_companion) == ("0", "0"):
        show_command_bar = False
    elif show_companion == "1" and show_primary in {"2", "3"}:
        show_command_bar = True
    command_bar_location = {
        ("0", "0"): "None",
        ("2", "1"): "Top",
        ("3", "1"): "Bottom",
    }.get((show_primary, show_companion))
    if form_version in {"49", "50"} and show_primary == "0":
        show_command_bar = False
        command_bar_location = "None"
    auto_title = bool_presentation(str((by_index.get(9) or {}).get("value") or "")) if form_version in {"49", "50"} else None
    vertical_scroll = "useIfNecessary" if form_version == "49" and str((by_index.get(36) or {}).get("value") or "") == "2" else None
    use_for_raw = str((by_index.get(20) or {}).get("typed_value") or "")
    use_for_guid = str((by_index.get(20) or {}).get("typed_guid") or "")
    use_for_folders_and_items = choice_folders_and_items_presentation(use_for_raw) if use_for_guid == "59ef2b80-c86b-11d5-a3c1-0050bae0a776" else None
    auto_time = "CurrentOrLast" if use_for_guid == "adeb08a0-415c-11d6-b9d1-0050bae0a95d" and use_for_raw == "3" else None
    posting_parameter = by_index.get(22) or {}
    use_posting_mode = (
        "Auto"
        if str(posting_parameter.get("typed_guid") or "") == "20d89b09-bd04-4304-a8c7-4d07fac6338a"
        and str(posting_parameter.get("typed_value") or "") == "3"
        else None
    )
    repost_parameter = by_index.get(24) or {}
    repost_on_write = bool_presentation(str(repost_parameter.get("typed_value") or "")) if repost_parameter.get("typed_kind") == "boolean" else None
    mapped_indexes: set[int] = set()
    groups: dict[str, list[dict[str, Any]]] = {}
    if form_group is not None:
        mapped_indexes.update(group_indices)
        groups.setdefault("Основные", []).append(
            {
                "name": "Группировка",
                "value": form_group,
                "source": "controlled_designer_form_root_group",
                "status": "ok",
                "parameter_indices": group_indices,
                "parameter_values": group_values,
                "write_shape": "composite_scalar",
            }
        )
    if auto_save_data_in_settings is not None:
        mapped_indexes.add(7)
        groups.setdefault("Основные", []).append(
            {
                "name": "АвтоСохранениеДанныхВНастройках",
                "value": auto_save_data_in_settings,
                "source": "controlled_designer_form_auto_save_data_in_settings",
                "status": "ok",
                "parameter_indices": [7],
                "parameter_values": [auto_save_raw],
                "write_shape": "scalar_enum",
            }
        )
    if window_opening_mode is not None:
        mapped_indexes.update({2, 54})
        groups.setdefault("Основные", []).append(
            {
                "name": "РежимОткрытияОкна",
                "value": window_opening_mode,
                "source": "controlled_designer_form_window_opening_mode",
                "status": "ok",
                "parameter_indices": [2, 54],
                "parameter_values": [window_primary, window_companion],
                "write_shape": "paired_scalar",
            }
        )
    if show_command_bar is not None:
        mapped_indexes.update({17, 56})
        groups.setdefault("Основные", []).append(
            {
                "name": "ОтображатьКоманднуюПанель",
                "value": show_command_bar,
                "source": "controlled_designer_form_show_command_bar",
                "status": "ok",
                "parameter_indices": [17, 56],
                "parameter_values": [show_primary, show_companion],
                "write_shape": "paired_scalar",
            }
        )
    if command_bar_location is not None:
        mapped_indexes.update({17, 56})
        groups.setdefault("Основные", []).append(
            {
                "name": "ПоложениеКоманднойПанели",
                "value": command_bar_location,
                "source": "controlled_designer_form_command_bar_location",
                "status": "ok",
                "parameter_indices": [17, 56],
                "parameter_values": [show_primary, show_companion],
                "write_shape": "paired_scalar_shared",
            }
        )
    if use_for_folders_and_items is not None:
        mapped_indexes.add(20)
        groups.setdefault("Основные", []).append(
            {
                "name": "UseForFoldersAndItems",
                "value": use_for_folders_and_items,
                "source": "live_sql_typed_form_enum",
                "status": "ok",
                "parameter_index": 20,
                "parameter_value": use_for_raw,
                "write_shape": "typed_enum_read_only",
            }
        )
    if auto_title is not None:
        mapped_indexes.add(9)
        groups.setdefault("Прочее", []).append(
            {
                "name": "AutoTitle",
                "value": auto_title,
                "source": "form_payload_root_versioned",
                "status": "ok",
                "parameter_index": 9,
                "write_shape": "scalar_boolean_read_only",
            }
        )
    if vertical_scroll is not None:
        mapped_indexes.add(36)
        groups.setdefault("Прочее", []).append(
            {
                "name": "VerticalScroll",
                "value": vertical_scroll,
                "source": "form_payload_root_versioned",
                "status": "ok",
                "parameter_index": 36,
                "write_shape": "scalar_enum_read_only",
            }
        )
    for name, value, index, source in (
        ("AutoTime", auto_time, 20, "live_sql_typed_form_enum"),
        ("UsePostingMode", use_posting_mode, 22, "live_sql_typed_form_enum"),
        ("RepostOnWrite", repost_on_write, 24, "live_sql_typed_form_boolean"),
    ):
        if value is None:
            continue
        mapped_indexes.add(index)
        groups.setdefault("Прочее", []).append(
            {
                "name": name,
                "value": value,
                "source": source,
                "status": "ok",
                "parameter_index": index,
                "write_shape": "typed_read_only",
            }
        )
    semantic = {
        "groups": groups,
        "coverage": semantic_coverage(parameters, mapped_indexes),
        "unmapped_parameters": semantic_unmapped_parameters(parameters, mapped_indexes),
    }
    return public_semantic(semantic, include_diagnostics=include_diagnostics)


def enrich_form_common_semantic(form_semantic: dict[str, Any], items: list[dict[str, Any]]) -> None:
    command_bar = next(
        (
            item
            for item in items
            if isinstance(item, dict)
            and str(item.get("id") or "") == "-1"
            and str(item.get("type_name") or "") == "Командная панель"
            and item.get("name")
        ),
        None,
    )
    if command_bar is None:
        return
    groups = form_semantic.setdefault("groups", {})
    add_grouped_property(
        groups,
        "Основные",
        semantic_property(
            "АвтоКоманднаяПанель",
            command_bar.get("name"),
            source="form_item_reference:id=-1",
        ),
    )


def module_summary(tree: Any, *, include_text: bool = False) -> dict[str, Any] | None:
    text = scalar(get_by_path(tree, "2"))
    if not text:
        return None
    routines = [{"kind": match.group(1), "name": match.group(2)} for match in BSL_ROUTINE_RE.finditer(text)]
    result: dict[str, Any] = {
        "path": "2",
        "bytes_estimate": len(text.encode("utf-8")),
        "chars": len(text),
        "routine_count": len(routines),
        "routine_names": [str(item.get("name") or "") for item in routines if item.get("name")],
        "routines_sample": routines[:80],
        "text_preview": text[:500],
    }
    if include_text:
        result["text"] = text
    return result


def routine_names(module: dict[str, Any] | None) -> set[str]:
    summary = module or {}
    names = {str(value or "").casefold() for value in summary.get("routine_names") or [] if value}
    names.update(
        str(item.get("name") or "").casefold()
        for item in summary.get("routines_sample") or []
        if isinstance(item, dict) and item.get("name")
    )
    return names


def handler_links(events: list[dict[str, Any]], module: dict[str, Any] | None) -> list[dict[str, Any]]:
    names = routine_names(module)
    links = []
    for event in events:
        handler = str(event.get("handler") or "")
        links.append(
            {
                "kind": "form_event",
                "event_name": event.get("event_name"),
                "handler": handler,
                "status": "resolved" if handler.casefold() in names else "missing",
                "event_path": event.get("path"),
            }
        )
    return links


def command_handler_links(commands: list[dict[str, Any]], module: dict[str, Any] | None) -> list[dict[str, Any]]:
    names = routine_names(module)
    links = []
    for command in commands:
        handler = str(command.get("action") or command.get("name") or "")
        if not handler:
            continue
        resolved = handler.casefold() in names
        action_is_explicit = command.get("action_source") == "form_payload_parameter_8"
        status = "resolved" if resolved else "missing" if action_is_explicit else "no_handler_expected"
        links.append(
            {
                "kind": "form_command",
                "command": command.get("name"),
                "handler": handler,
                "status": status,
                "match_by": "action" if action_is_explicit else "command_name",
                **(
                    {}
                    if resolved
                    else {
                        "diagnostics": {
                            "message": (
                                "Явный обработчик Action не найден в декодированном модуле формы."
                                if action_is_explicit
                                else "Явный BSL-обработчик с именем команды не найден; команда может быть платформенной или декларативной."
                            )
                        }
                    }
                ),
                "command_path": command.get("path"),
                "command_guid": (command.get("guids_sample") or [None])[0],
            }
        )
    return links


def button_command_links(items: list[dict[str, Any]], commands: list[dict[str, Any]]) -> list[dict[str, Any]]:
    command_by_guid = {}
    command_by_id = {str(command.get("id") or ""): command for command in commands if command.get("id") is not None}
    for command in commands:
        for guid in command.get("guids_sample") or []:
            command_by_guid[str(guid).lower()] = command
    links = []
    for item in items:
        if item.get("marker") not in {"31", "34"}:
            continue
        binding = item.get("command_binding") if isinstance(item.get("command_binding"), dict) else None
        if binding and binding.get("command_name"):
            links.append(
                {
                    "kind": "command_button",
                    "button": item.get("name"),
                    "command": binding.get("command_name"),
                    "command_name": binding.get("command_name"),
                    "status": "resolved",
                    "match_by": binding.get("match_by"),
                    "button_path": item.get("path"),
                    "command_path": binding.get("path"),
                    "command_guid": binding.get("group_guid"),
                    "command_binding": binding,
                }
            )
            continue
        if binding and binding.get("scope") == "form":
            command = command_by_id.get(str(binding.get("command_id") or ""))
            if command is not None:
                links.append(
                    {
                        "kind": "command_button",
                        "button": item.get("name"),
                        "command": command.get("name"),
                        "command_name": f"Form.Command.{command.get('name')}",
                        "status": "resolved",
                        "match_by": binding.get("match_by"),
                        "button_path": item.get("path"),
                        "command_path": command.get("path"),
                        "command_guid": binding.get("group_guid"),
                        "command_binding": binding,
                    }
                )
                continue
        command_reference = item.get("command_reference") if isinstance(item.get("command_reference"), dict) else None
        if command_reference:
            reference_key = (str(command_reference.get("guid") or "").lower(), str(command_reference.get("code") or ""))
            suffix = FORM_GRAPHICAL_SCHEMA_STANDARD_COMMANDS.get(
                reference_key
            )
            graphical_fields = [candidate for candidate in items if candidate.get("type_name") == "GraphicalSchemaField"]
            if suffix and len(graphical_fields) == 1:
                owner_name = str(graphical_fields[0].get("name") or "")
                command_name = f"Form.Item.{owner_name}.StandardCommand.{suffix}"
                links.append(
                    {
                        "kind": "command_button",
                        "button": item.get("name"),
                        "command": command_name,
                        "command_name": command_name,
                        "status": "resolved",
                        "match_by": "graphical_schema_standard_command_guid",
                        "button_path": item.get("path"),
                        "command_path": command_reference.get("path"),
                        "command_guid": command_reference.get("guid"),
                    }
                )
                continue
            suffix = FORM_TABLE_STANDARD_COMMANDS.get(reference_key[0])
            table_fields = [
                candidate
                for candidate in items
                if candidate.get("type_name") in {"Динамический список", "Таблица формы"}
                and str(candidate.get("id") or "") == reference_key[1]
            ]
            if suffix and len(table_fields) == 1:
                owner_name = str(table_fields[0].get("name") or "")
                command_name = f"Form.Item.{owner_name}.StandardCommand.{suffix}"
                links.append(
                    {
                        "kind": "command_button",
                        "button": item.get("name"),
                        "command": command_name,
                        "command_name": command_name,
                        "status": "resolved",
                        "match_by": "table_standard_command_guid",
                        "button_path": item.get("path"),
                        "command_path": command_reference.get("path"),
                        "command_guid": command_reference.get("guid"),
                    }
                )
                continue
            object_command = FORM_OBJECT_COMMANDS.get(reference_key)
            query_texts = [
                str((candidate.get("dynamic_list_settings") or {}).get("query_text") or "")
                for candidate in items
                if isinstance(candidate.get("dynamic_list_settings"), dict)
            ]
            public_refs = {
                f"Task.{match.group(1)}"
                for query_text in query_texts
                for match in re.finditer(r"(?:Задача|Task)\.([A-Za-zА-Яа-яЁё_][\wА-Яа-яЁё]*)", query_text)
            }
            if object_command and len(public_refs) == 1:
                command_name = f"{next(iter(public_refs))}.Command.{object_command}"
                links.append(
                    {
                        "kind": "command_button",
                        "button": item.get("name"),
                        "command": command_name,
                        "command_name": command_name,
                        "status": "resolved",
                        "match_by": "object_command_guid_and_dynamic_query",
                        "button_path": item.get("path"),
                        "command_path": command_reference.get("path"),
                        "command_guid": command_reference.get("guid"),
                    }
                )
                continue
            if object_command:
                item_name = str(item.get("name") or "")
                name_match = re.search(
                    rf"(?:Задача|Task)([A-Za-zА-Яа-яЁё_][\wА-Яа-яЁё]*?){re.escape(object_command)}$",
                    item_name,
                )
                if name_match:
                    command_name = f"Task.{name_match.group(1)}.Command.{object_command}"
                    links.append(
                        {
                            "kind": "command_button",
                            "button": item.get("name"),
                            "command": command_name,
                            "command_name": command_name,
                            "status": "resolved",
                            "match_by": "object_command_guid_and_button_name",
                            "button_path": item.get("path"),
                            "command_path": command_reference.get("path"),
                            "command_guid": command_reference.get("guid"),
                        }
                    )
                    continue
        matched_guid = next((guid for guid in item.get("guids_sample") or [] if str(guid).lower() in command_by_guid), None)
        button_text = " ".join(str(value or "") for value in [item.get("name"), item.get("title"), *(item.get("strings_sample") or [])]).casefold()
        command = command_by_guid[str(matched_guid).lower()] if matched_guid else None
        match_by = "guid"
        if command is None:
            item_name = str(item.get("name") or "").casefold()
            for candidate in sorted(commands, key=lambda value: len(str(value.get("name") or "")), reverse=True):
                candidate_name = str(candidate.get("name") or "").casefold()
                if candidate_name and (item_name.endswith(candidate_name) or candidate_name in item_name):
                    command = candidate
                    match_by = "name"
                    break
        if command is None:
            continue
        command_names = [str(value or "") for value in [command.get("name"), command.get("title"), command.get("id")]]
        if match_by == "guid" and not any(value and value.casefold() in button_text for value in command_names):
            command = None
            item_name = str(item.get("name") or "").casefold()
            for candidate in sorted(commands, key=lambda value: len(str(value.get("name") or "")), reverse=True):
                candidate_name = str(candidate.get("name") or "").casefold()
                if candidate_name and (item_name.endswith(candidate_name) or candidate_name in item_name):
                    command = candidate
                    match_by = "name"
                    matched_guid = None
                    break
            if command is None:
                continue
        links.append(
            {
                "kind": "command_button",
                "button": item.get("name"),
                "command": command.get("name"),
                "command_name": f"Form.Command.{command.get('name')}",
                "status": "resolved",
                "match_by": match_by,
                "button_path": item.get("path"),
                "command_path": command.get("path"),
                "command_guid": matched_guid,
            }
        )
    return links


def enrich_button_command_semantics(items: list[dict[str, Any]], links: list[dict[str, Any]]) -> None:
    command_by_button = {str(link.get("button") or ""): link for link in links if link.get("button") and (link.get("command") or link.get("command_name"))}
    for item in items:
        link = command_by_button.get(str(item.get("name") or ""))
        if not link:
            continue
        semantic = item.setdefault("semantic", {})
        groups = semantic.setdefault("groups", {})
        add_grouped_property(
            groups,
            "Основные",
            semantic_property("ИмяКоманды", link.get("command_name") or f"Form.Command.{link.get('command')}", source=f"button_command_link:{link.get('match_by') or 'unknown'}"),
        )


FORM_AUXILIARY_ITEM_TYPES = {
    "Контекстное меню", "Расширенная подсказка", "SearchStringAddition",
    "ViewStatusAddition", "SearchControlAddition",
}


def form_item_coverage_summary(items: list[dict[str, Any]]) -> dict[str, Any]:
    """Report semantic coverage without auxiliary form records hiding control quality."""
    buckets = {
        "all_items": {"items": 0, "mapped": 0, "unmapped": 0},
        "interactive_items": {"items": 0, "mapped": 0, "unmapped": 0},
        "auxiliary_items": {"items": 0, "mapped": 0, "unmapped": 0},
    }
    for item in items:
        if not isinstance(item, dict):
            continue
        coverage = (item.get("semantic") or {}).get("coverage") if isinstance(item.get("semantic"), dict) else None
        if not isinstance(coverage, dict):
            continue
        target = "auxiliary_items" if str(item.get("type_name") or "") in FORM_AUXILIARY_ITEM_TYPES else "interactive_items"
        for bucket_name in ("all_items", target):
            bucket = buckets[bucket_name]
            bucket["items"] += 1
            bucket["mapped"] += int(coverage.get("mapped") or 0)
            bucket["unmapped"] += int(coverage.get("unmapped") or 0)
    for bucket in buckets.values():
        bucket["total"] = bucket["mapped"] + bucket["unmapped"]
        bucket["status"] = "partial" if bucket["unmapped"] else "ok"
    return buckets


def decode_form_payload(
    tree: Any,
    *,
    max_items: int = 500,
    include_module_text: bool = False,
    include_parameters: bool = True,
    max_parameters: int = 80,
) -> dict[str, Any]:
    root = root_signature(tree)
    items, items_total, items_truncated = item_records(tree, limit=max_items, include_parameters=include_parameters, max_parameters=max_parameters)
    attributes = section_records(tree, "3", "Attribute", limit=max_items, include_parameters=include_parameters, max_parameters=max_parameters)
    parameters = form_parameter_rows(tree, include_parameters=include_parameters, max_parameters=max_parameters)
    enrich_item_data_paths(items, attributes)
    enrich_page_title_data_paths(items)
    additional_items, additional_items_total, additional_items_truncated = additional_column_items(
        tree,
        items,
        limit=max(0, max_items - len(items)),
    )
    if additional_items:
        items.extend(additional_items)
        items_total += additional_items_total
        items_truncated = items_truncated or additional_items_truncated or items_total > len(items)
    dynamic_items, dynamic_items_total, dynamic_items_truncated = dynamic_list_column_items(attributes, limit=max(0, max_items - len(items)))
    if dynamic_items:
        items.extend(dynamic_items)
        items_total += dynamic_items_total
        items_truncated = items_truncated or dynamic_items_truncated or items_total > len(items)
    commands = section_records(tree, "5", "Command", limit=max_items, include_parameters=include_parameters, max_parameters=max_parameters)
    tables = section_records(tree, "6", "Table", limit=max_items, include_parameters=include_parameters, max_parameters=max_parameters)
    command_bars = section_records(tree, "7", "CommandBar", limit=max_items, include_parameters=include_parameters, max_parameters=max_parameters)
    totals = {
        "items": items_total,
        "attributes": section_record_total(tree, "3"),
        "commands": section_record_total(tree, "5"),
        "tables": section_record_total(tree, "6"),
        "command_bars": section_record_total(tree, "7"),
    }
    module = module_summary(tree, include_text=include_module_text)
    enrich_item_event_links(items, module)
    events = event_handlers(tree)
    event_links = handler_links(events, module)
    link_items, _, _ = item_records(tree, limit=max(items_total, max_items), include_parameters=False)
    link_commands = section_records(tree, "5", "Command", limit=max(totals["commands"], max_items), include_parameters=False)
    command_links = command_handler_links(link_commands, module)
    button_links = button_command_links(link_items, link_commands)
    enrich_button_command_semantics(items, button_links)
    public_rows_semantics(items, include_diagnostics=include_parameters)
    public_rows_semantics(attributes, include_diagnostics=include_parameters)
    public_rows_semantics(parameters, include_diagnostics=include_parameters)
    public_rows_semantics(commands, include_diagnostics=include_parameters)
    public_rows_semantics(tables, include_diagnostics=include_parameters)
    public_rows_semantics(command_bars, include_diagnostics=include_parameters)
    form_parameters = form_common_parameters(tree, limit=max_parameters)
    form_semantic = form_common_semantic(form_parameters, include_diagnostics=include_parameters)
    enrich_form_common_semantic(form_semantic, items)
    coverage_summary = form_item_coverage_summary(items)
    result = {
        "schema": "onec_form_payload_profile.v1",
        "status": "ok" if root.get("root_marker") == "4" else "not_form_payload",
        "root": root,
        "form_semantic": form_semantic,
        "item_coverage": coverage_summary,
        **({"form_parameters": form_parameters} if include_parameters else {}),
        "events": events,
        "items": items,
        "attributes": attributes,
        "parameters": parameters,
        "commands": commands,
        "tables": tables,
        "command_bars": command_bars,
        "module": module,
        "handler_links": event_links,
        "command_links": command_links,
        "button_command_links": button_links,
        "counts": {},
    }
    result["counts"] = {
        "events": len(result["events"]),
        "items": len(result["items"]),
        "items_total": totals["items"],
        "attributes": len(result["attributes"]),
        "parameters": len(result["parameters"]),
        "attributes_total": totals["attributes"],
        "commands": len(result["commands"]),
        "commands_total": totals["commands"],
        "tables": len(result["tables"]),
        "tables_total": totals["tables"],
        "command_bars": len(result["command_bars"]),
        "command_bars_total": totals["command_bars"],
        "module_routines": ((result.get("module") or {}).get("routine_count") or 0),
        "handler_links": len(result["handler_links"]),
        "resolved_handlers": sum(1 for item in result["handler_links"] if item.get("status") == "resolved"),
        "missing_handlers": sum(1 for item in result["handler_links"] if item.get("status") == "missing"),
        "command_links": len(result["command_links"]),
        "resolved_commands": sum(1 for item in result["command_links"] if item.get("status") == "resolved"),
        "missing_commands": sum(1 for item in result["command_links"] if item.get("status") == "missing"),
        "button_command_links": len(result["button_command_links"]),
        "parameters_included": bool(include_parameters),
        "max_parameters": max_parameters if include_parameters else 0,
        "items_truncated": items_truncated,
        "attributes_truncated": totals["attributes"] > len(result["attributes"]),
        "commands_truncated": totals["commands"] > len(result["commands"]),
        "tables_truncated": totals["tables"] > len(result["tables"]),
        "command_bars_truncated": totals["command_bars"] > len(result["command_bars"]),
    }
    return result
