# meta-info v1.11 — Compact summary of 1C metadata object
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
[CmdletBinding(PositionalBinding=$false)]
param(
	[Parameter(Mandatory=$true, Position=0)][Alias('Path')][string]$ObjectPath,
	[ValidateSet("overview","brief","full")]
	[string]$Mode = "overview",
	[string]$Name,
	[int]$Limit = 150,
	[int]$Offset = 0,
	[string]$OutFile
)

$ErrorActionPreference = 'Stop'
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8

# --- Output helper (always collect, paginate at the end) ---
$script:lines = @()
function Out([string]$text) { $script:lines += $text }

# --- Resolve path ---
if (-not [System.IO.Path]::IsPathRooted($ObjectPath)) {
	$ObjectPath = Join-Path (Get-Location).Path $ObjectPath
}

# Directory -> find XML inside or as sibling
if (Test-Path $ObjectPath -PathType Container) {
	$dirName = Split-Path $ObjectPath -Leaf
	$candidate = Join-Path $ObjectPath "$dirName.xml"
	$sibling = Join-Path (Split-Path $ObjectPath) "$dirName.xml"
	if (Test-Path $candidate) {
		$ObjectPath = $candidate
	} elseif (Test-Path $sibling) {
		$ObjectPath = $sibling
	} else {
		$xmlFiles = @(Get-ChildItem $ObjectPath -Filter "*.xml" -File | Select-Object -First 1)
		if ($xmlFiles.Count -gt 0) {
			$ObjectPath = $xmlFiles[0].FullName
		} else {
			Write-Host "[ERROR] No XML file found in directory: $ObjectPath"
			exit 1
		}
	}
}

# File not found — check Dir/Name/Name.xml → Dir/Name.xml (common LLM mistake)
if (-not (Test-Path $ObjectPath)) {
	$fileName = [System.IO.Path]::GetFileNameWithoutExtension($ObjectPath)
	$parentDir = Split-Path $ObjectPath
	$parentDirName = Split-Path $parentDir -Leaf
	if ($fileName -eq $parentDirName) {
		$candidate = Join-Path (Split-Path $parentDir) "$fileName.xml"
		if (Test-Path $candidate) { $ObjectPath = $candidate }
	}
}
if (-not (Test-Path $ObjectPath)) {
	Write-Host "[ERROR] File not found: $ObjectPath"
	exit 1
}

# --- Load XML ---
[xml]$xmlDoc = Get-Content -Path $ObjectPath -Encoding UTF8
$ns = New-Object System.Xml.XmlNamespaceManager($xmlDoc.NameTable)
$ns.AddNamespace("md", "http://v8.1c.ru/8.3/MDClasses")
$ns.AddNamespace("v8", "http://v8.1c.ru/8.1/data/core")
$ns.AddNamespace("xr", "http://v8.1c.ru/8.3/xcf/readable")
$ns.AddNamespace("xsi", "http://www.w3.org/2001/XMLSchema-instance")
$ns.AddNamespace("xs", "http://www.w3.org/2001/XMLSchema")
$ns.AddNamespace("cfg", "http://v8.1c.ru/8.1/data/enterprise/current-config")
$ns.AddNamespace("app", "http://v8.1c.ru/8.2/managed-application/core")

$mdRoot = $xmlDoc.SelectSingleNode("/md:MetaDataObject", $ns)
if (-not $mdRoot) {
	Write-Host "[ERROR] Not a valid 1C metadata XML file"
	exit 1
}

# --- Detect object type ---
$typeNode = $null
$mdType = ""
foreach ($child in $mdRoot.ChildNodes) {
	if ($child.NodeType -eq 'Element' -and $child.NamespaceURI -eq "http://v8.1c.ru/8.3/MDClasses") {
		$typeNode = $child
		$mdType = $child.LocalName
		break
	}
}

if (-not $typeNode) {
	Write-Host "[ERROR] Cannot detect metadata type"
	exit 1
}

# --- Type name maps ---
$typeNameMap = @{
	"Catalog"="Справочник"; "Document"="Документ"; "Enum"="Перечисление"
	"Constant"="Константа"; "InformationRegister"="Регистр сведений"
	"AccumulationRegister"="Регистр накопления"; "AccountingRegister"="Регистр бухгалтерии"
	"CalculationRegister"="Регистр расчёта"; "ChartOfAccounts"="План счетов"
	"ChartOfCharacteristicTypes"="План видов характеристик"
	"ChartOfCalculationTypes"="План видов расчёта"; "BusinessProcess"="Бизнес-процесс"
	"Task"="Задача"; "ExchangePlan"="План обмена"; "DocumentJournal"="Журнал документов"
	"Report"="Отчёт"; "DataProcessor"="Обработка"
	"DefinedType"="Определяемый тип"; "CommonModule"="Общий модуль"
	"ScheduledJob"="Регламентное задание"; "EventSubscription"="Подписка на событие"
	"HTTPService"="HTTP-сервис"; "WebService"="Веб-сервис"
}

$refTypeMap = @{
	"CatalogRef"="СправочникСсылка"; "DocumentRef"="ДокументСсылка"
	"EnumRef"="ПеречислениеСсылка"; "ChartOfAccountsRef"="ПланСчетовСсылка"
	"ChartOfCharacteristicTypesRef"="ПВХСсылка"; "ChartOfCalculationTypesRef"="ПВРСсылка"
	"ExchangePlanRef"="ПланОбменаСсылка"; "BusinessProcessRef"="БизнесПроцессСсылка"
	"TaskRef"="ЗадачаСсылка"
}

$regTypeMap = @{
	"AccumulationRegister"="РН"; "AccountingRegister"="РБ"; "CalculationRegister"="РР"
	"InformationRegister"="РС"
}

$periodMap = @{
	"Nonperiodical"="Непериодический"; "Day"="День"; "Month"="Месяц"
	"Quarter"="Квартал"; "Year"="Год"; "Second"="Секунда"
}

$writeModeMap = @{
	"Independent"="независимая"; "RecorderSubordinate"="подчинение регистратору"
}

$reuseMap = @{
	"DontUse"="нет"; "DuringRequest"="на время вызова"; "DuringSession"="на время сеанса"
}

$eventMap = @{
	"BeforeWrite"="ПередЗаписью"; "OnWrite"="ПриЗаписи"; "AfterWrite"="ПослеЗаписи"
	"BeforeDelete"="ПередУдалением"; "Posting"="ОбработкаПроведения"
	"UndoPosting"="ОбработкаУдаленияПроведения"
	"OnReadAtServer"="ПриЧтенииНаСервере"
	"FillCheckProcessing"="ОбработкаПроверкиЗаполнения"
}

$objectTypeMap = @{
	"CatalogObject"="СправочникОбъект"; "DocumentObject"="ДокументОбъект"
	"ChartOfAccountsObject"="ПланСчетовОбъект"
	"ChartOfCharacteristicTypesObject"="ПВХОбъект"
	"BusinessProcessObject"="БизнесПроцессОбъект"; "TaskObject"="ЗадачаОбъект"
	"ExchangePlanObject"="ПланОбменаОбъект"
	"InformationRegisterRecordSet"="НаборЗаписейРС"
	"AccumulationRegisterRecordSet"="НаборЗаписейРН"
	"AccountingRegisterRecordSet"="НаборЗаписейРБ"
}

$numberPeriodMap = @{
	"Year"="по году"; "Quarter"="по кварталу"; "Month"="по месяцу"; "Day"="по дню"
	"WholeCatalog"="сквозная"
}

$ruTypeName = if ($typeNameMap.ContainsKey($mdType)) { $typeNameMap[$mdType] } else { $mdType }

# --- Helpers ---

function Get-MLText($node) {
	if (-not $node) { return "" }
	$c = $node.SelectSingleNode("v8:item[v8:lang='ru']/v8:content", $ns)
	if ($c) { return $c.InnerText }
	$c = $node.SelectSingleNode("v8:item/v8:content", $ns)
	if ($c) { return $c.InnerText }
	$text = $node.InnerText.Trim()
	if ($text) { return $text }
	return ""
}

# Тип-множество: голое имя метатипа без `.Имя` означает ВСЕ ссылки этого класса
# (см. docs/meta-dsl-spec.md §«Тип-множество»). Конкретный тип всегда пишется с точкой,
# поэтому «СправочникСсылка» без точки читается однозначно как обобщённый.
function Format-SingleTypeSet([string]$raw) {
	$raw = $raw -replace '^d\d+p\d+:', 'cfg:'
	if ($raw -match '^cfg:DefinedType\.(.+)$')   { return "ОпределяемыйТип.$($Matches[1])" }
	if ($raw -match '^cfg:Characteristic\.(.+)$') { return "Характеристика.$($Matches[1])" }
	if ($raw -eq 'cfg:AnyRef')   { return "ЛюбаяСсылка" }
	if ($raw -eq 'cfg:AnyIBRef') { return "ЛюбаяСсылкаИБ" }
	if ($raw -match '^cfg:(\w+Ref)$' -and $refTypeMap.ContainsKey($Matches[1])) {
		return $refTypeMap[$Matches[1]]
	}
	if ($raw -match '^cfg:(.+)$') { return $Matches[1] }
	return $raw
}

function Format-Type($typeNode) {
	if (-not $typeNode) { return "" }
	$types = @()
	foreach ($t in $typeNode.SelectNodes("v8:Type", $ns)) {
		$raw = $t.InnerText
		$types += Format-SingleType $raw $typeNode
	}
	foreach ($t in $typeNode.SelectNodes("v8:TypeSet", $ns)) {
		$types += Format-SingleTypeSet $t.InnerText
	}
	if ($types.Count -eq 0) { return "" }
	if ($types.Count -eq 1) { return $types[0] }
	return ($types -join " | ")
}

function Format-SingleType([string]$raw, $parentNode) {
	switch -Wildcard ($raw) {
		"xs:string" {
			$sq = $parentNode.SelectSingleNode("v8:StringQualifiers/v8:Length", $ns)
			$len = if ($sq) { $sq.InnerText } else { "" }
			if ($len) { return "Строка($len)" } else { return "Строка" }
		}
		"xs:decimal" {
			$dg = $parentNode.SelectSingleNode("v8:NumberQualifiers/v8:Digits", $ns)
			$fr = $parentNode.SelectSingleNode("v8:NumberQualifiers/v8:FractionDigits", $ns)
			$d = if ($dg) { $dg.InnerText } else { "" }
			$f = if ($fr) { $fr.InnerText } else { "0" }
			if ($d) { return "Число($d,$f)" } else { return "Число" }
		}
		"xs:boolean" { return "Булево" }
		"xs:dateTime" {
			$dq = $parentNode.SelectSingleNode("v8:DateQualifiers/v8:DateFractions", $ns)
			if ($dq) {
				switch ($dq.InnerText) {
					"Date" { return "Дата" }
					"Time" { return "Время" }
					"DateTime" { return "ДатаВремя" }
					default { return "Дата" }
				}
			}
			return "ДатаВремя"
		}
		"v8:ValueStorage" { return "ХранилищеЗначения" }
		"v8:UUID" { return "УникальныйИдентификатор" }
		"v8:Null" { return "Null" }
		default {
			# Normalize d5p1:/dNpN: -> cfg:
			$raw = $raw -replace '^d\d+p\d+:', 'cfg:'
			# cfg:CatalogRef.Xxx -> СправочникСсылка.Xxx
			if ($raw -match '^cfg:(\w+)Ref\.(.+)$') {
				$prefix = "$($Matches[1])Ref"
				$objn = $Matches[2]
				if ($refTypeMap.ContainsKey($prefix)) {
					return "$($refTypeMap[$prefix]).$objn"
				}
			}
			# cfg:EnumRef.Xxx
			if ($raw -match '^cfg:EnumRef\.(.+)$') {
				return "ПеречислениеСсылка.$($Matches[1])"
			}
			# cfg:Characteristic.Xxx
			if ($raw -match '^cfg:Characteristic\.(.+)$') {
				return "Характеристика.$($Matches[1])"
			}
			# cfg:DefinedType.Xxx
			if ($raw -match '^cfg:DefinedType\.(.+)$') {
				return "ОпределяемыйТип.$($Matches[1])"
			}
			# Strip cfg: prefix for unknown
			if ($raw -match '^cfg:(.+)$') {
				return $Matches[1]
			}
			return $raw
		}
	}
}

function Format-Flags($propsNode, [bool]$isDimension = $false) {
	$flags = @()
	$fc = $propsNode.SelectSingleNode("md:FillChecking", $ns)
	if ($fc -and $fc.InnerText -eq "ShowError") { $flags += "обязательный" }

	$idx = $propsNode.SelectSingleNode("md:Indexing", $ns)
	if ($idx) {
		switch ($idx.InnerText) {
			"Index" { $flags += "индекс" }
			"IndexWithAdditionalOrder" { $flags += "индекс+доп" }
		}
	}

	if ($isDimension) {
		$master = $propsNode.SelectSingleNode("md:Master", $ns)
		if ($master -and $master.InnerText -eq "true") { $flags += "ведущее" }
	}

	$ml = $propsNode.SelectSingleNode("md:MultiLine", $ns)
	if ($ml -and $ml.InnerText -eq "true") { $flags += "многострочный" }

	$use = $propsNode.SelectSingleNode("md:Use", $ns)
	if ($use) {
		switch ($use.InnerText) {
			"ForFolder" { $flags += "для папок" }
			"ForFolderAndItem" { $flags += "для папок и элементов" }
		}
	}

	if ($flags.Count -eq 0) { return "" }
	return "  [$($flags -join ', ')]"
}

# Ссылка на объект метаданных (`Catalog.Валюты` в Owners и подобных) — не тип значения,
# но в выводе показываем именно тип, который присваивается: СправочникСсылка.Валюты.
function Format-MDObjectRef([string]$raw) {
	if ($raw -match '^(\w+)\.(.+)$') {
		$key = "$($Matches[1])Ref"
		if ($refTypeMap.ContainsKey($key)) { return "$($refTypeMap[$key]).$($Matches[2])" }
	}
	return $raw
}

# --- Стандартные реквизиты ---

# Сколько типов состава печатать списком, прежде чем свернуть в счётчик. По корпусу
# acc/erp/ut/unf состав ПВХ доходит до 151 типа, при этом 20 из 42 ПВХ укладываются в 5.
$script:composedTypeThreshold = 5

# Имена полей, состав которых свернули в счётчик, — по ним в конце вывода печатается
# единственная подсказка, чем состав развернуть.
$script:collapsedNames = @()

# Блок StandardAttributes в XML опционален: платформа материализует его только когда хотя бы
# один стандартный реквизит кастомизирован (docs/meta-dsl-spec.md §7.1.1). Когда блока нет,
# действуют платформенные дефолты — профиль ниже совпадает с $stdAttrProfile в meta-compile
# (выведен из корпуса acc+erp). Правки держать синхронными, иначе навыки разъедутся молча.
$stdAttrRequiredDefault = @{
	"Catalog"                    = @{ "Owner" = $true; "Description" = $true }
	"Document"                   = @{ "Date" = $true }
	"ExchangePlan"               = @{ "Description" = $true; "Code" = $true }
	"ChartOfAccounts"            = @{ "Description" = $true; "Code" = $true }
	"ChartOfCharacteristicTypes" = @{ "Description" = $true }
	"ChartOfCalculationTypes"    = @{ "Description" = $true }
}

function Test-StdAttrRequired($propsNode, [string]$attrName, [string]$objType) {
	$fc = $propsNode.SelectSingleNode("md:StandardAttributes/xr:StandardAttribute[@name='$attrName']/xr:FillChecking", $ns)
	if ($fc) { return ($fc.InnerText -eq "ShowError") }
	if ($stdAttrRequiredDefault.ContainsKey($objType) -and $stdAttrRequiredDefault[$objType].ContainsKey($attrName)) {
		return $stdAttrRequiredDefault[$objType][$attrName]
	}
	return $false
}

function New-StdAttr([string]$name, [string]$type, $flags) {
	$f = @($flags | Where-Object { $_ })
	$flagStr = if ($f.Count -gt 0) { "  [$($f -join ', ')]" } else { "" }
	return @{ Name = $name; Type = $type; Flags = $flagStr }
}

# Стандартные реквизиты, наличие и характеристики которых задаются настройками объекта, —
# их нельзя вывести из остального вывода, поэтому они попадают и в overview. Ссылка,
# ПометкаУдаления, Родитель и прочее следуют из типа объекта и строки «Иерархический» —
# они только в full (см. Get-StandardAttributesFull).
function Get-StandardAttributes($propsNode, [string]$objType, [string]$mode) {
	$result = @()

	# Владелец — только у подчинённого справочника; состав типов ниоткуда не выводится
	if ($objType -eq "Catalog") {
		$ownersNode = $propsNode.SelectSingleNode("md:Owners", $ns)
		$ownerTypes = @()
		if ($ownersNode) {
			foreach ($it in $ownersNode.SelectNodes("xr:Item", $ns)) {
				$ownerTypes += Format-MDObjectRef $it.InnerText
			}
		}
		if ($ownerTypes.Count -gt 0) {
			$req = if (Test-StdAttrRequired $propsNode "Owner" $objType) { "обязательный" } else { $null }
			$result += New-StdAttr "Владелец" ($ownerTypes -join ", ") @($req)
		}
	}

	# ТипЗначения ПВХ — до полутора сотен типов, полный список раздул бы сводку в обоих
	# режимах, поэтому сворачиваем в счётчик. Состав смотреть через -Name ТипЗначения.
	if ($objType -eq "ChartOfCharacteristicTypes") {
		$vt = $propsNode.SelectSingleNode("md:Type", $ns)
		if ($vt) {
			$cnt = $vt.SelectNodes("v8:Type", $ns).Count + $vt.SelectNodes("v8:TypeSet", $ns).Count
			$typeStr = if ($cnt -gt $script:composedTypeThreshold) {
				$script:collapsedNames += "ТипЗначения"
				"Составной ($cnt)"
			} else { Format-Type $vt }
			if ($typeStr) { $result += New-StdAttr "ТипЗначения" $typeStr @() }
		}
	}

	# Дата — есть всегда, ценна флагом обязательности
	if ($objType -in @("Document", "BusinessProcess", "Task")) {
		$req = if (Test-StdAttrRequired $propsNode "Date" $objType) { "обязательный" } else { $null }
		$result += New-StdAttr "Дата" "Дата" @($req)
	}

	# Номер — тип и длина задаются объектом, при NumberLength=0 номера нет
	if ($objType -in @("Document", "BusinessProcess", "Task")) {
		$numLen = $propsNode.SelectSingleNode("md:NumberLength", $ns)
		if ($numLen -and [int]$numLen.InnerText -gt 0) {
			$numType = $propsNode.SelectSingleNode("md:NumberType", $ns)
			$ntRu = if ($numType -and $numType.InnerText -eq "Number") { "Число" } else { "Строка" }
			$flags = @()
			if (Test-StdAttrRequired $propsNode "Number" $objType) { $flags += "обязательный" }
			$numPer = $propsNode.SelectSingleNode("md:NumberPeriodicity", $ns)
			if ($numPer -and $numberPeriodMap.ContainsKey($numPer.InnerText)) { $flags += $numberPeriodMap[$numPer.InnerText] }
			$numAllowed = $propsNode.SelectSingleNode("md:NumberAllowedLength", $ns)
			if ($numAllowed -and $numAllowed.InnerText -eq "Fixed") { $flags += "фикс. длина" }
			$autoNum = $propsNode.SelectSingleNode("md:Autonumbering", $ns)
			if ($autoNum -and $autoNum.InnerText -eq "true") { $flags += "авто" }
			$result += New-StdAttr "Номер" "$ntRu($($numLen.InnerText))" $flags
		}
	}

	# Код — при CodeLength=0 кода у объекта нет вовсе, строку не печатаем
	$codeLen = $propsNode.SelectSingleNode("md:CodeLength", $ns)
	if ($codeLen -and [int]$codeLen.InnerText -gt 0) {
		$codeType = $propsNode.SelectSingleNode("md:CodeType", $ns)
		$ctRu = if ($codeType -and $codeType.InnerText -eq "Number") { "Число" } else { "Строка" }
		$flags = @()
		if (Test-StdAttrRequired $propsNode "Code" $objType) { $flags += "обязательный" }
		$codeAllowed = $propsNode.SelectSingleNode("md:CodeAllowedLength", $ns)
		if ($codeAllowed -and $codeAllowed.InnerText -eq "Fixed") { $flags += "фикс. длина" }
		$result += New-StdAttr "Код" "$ctRu($($codeLen.InnerText))" $flags
	}

	# Наименование — при DescriptionLength=0 наименования нет
	$descLen = $propsNode.SelectSingleNode("md:DescriptionLength", $ns)
	if ($descLen -and [int]$descLen.InnerText -gt 0) {
		$req = if (Test-StdAttrRequired $propsNode "Description" $objType) { "обязательный" } else { $null }
		$result += New-StdAttr "Наименование" "Строка($($descLen.InnerText))" @($req)
	}

	return $result
}

# Остальные стандартные реквизиты — предсказуемы по типу объекта, но в full полезны
# как перечень доступных полей для запроса.
function Get-StandardAttributesFull($propsNode, [string]$objType, [string]$objName) {
	$selfRef = switch ($objType) {
		"Catalog"                    { "СправочникСсылка.$objName" }
		"ChartOfCharacteristicTypes" { "ПВХСсылка.$objName" }
		"ChartOfAccounts"            { "ПланСчетовСсылка.$objName" }
		"ChartOfCalculationTypes"    { "ПВРСсылка.$objName" }
		"ExchangePlan"               { "ПланОбменаСсылка.$objName" }
		"Document"                   { "ДокументСсылка.$objName" }
		"BusinessProcess"            { "БизнесПроцессСсылка.$objName" }
		"Task"                       { "ЗадачаСсылка.$objName" }
		default                      { "" }
	}
	$result = @()
	if ($selfRef) { $result += New-StdAttr "Ссылка" $selfRef @() }
	$result += New-StdAttr "ПометкаУдаления" "Булево" @()

	# Родитель и ЭтоГруппа существуют только у иерархических объектов
	$hier = $propsNode.SelectSingleNode("md:Hierarchical", $ns)
	$isHier = ($hier -and $hier.InnerText -eq "true") -or $objType -eq "ChartOfAccounts"
	if ($isHier -and $selfRef) {
		$result += New-StdAttr "Родитель" $selfRef @()
		$ht = $propsNode.SelectSingleNode("md:HierarchyType", $ns)
		if ($objType -eq "Catalog" -and (-not $ht -or $ht.InnerText -eq "HierarchyFoldersAndItems")) {
			$result += New-StdAttr "ЭтоГруппа" "Булево" @()
		}
	}

	switch ($objType) {
		"Document" {
			$result += New-StdAttr "Проведен" "Булево" @()
		}
		"ExchangePlan" {
			$result += New-StdAttr "ЭтотУзел" "Булево" @()
			$result += New-StdAttr "НомерОтправленного" "Число" @()
			$result += New-StdAttr "НомерПринятого" "Число" @()
		}
		"BusinessProcess" {
			$result += New-StdAttr "Стартован" "Булево" @()
			$result += New-StdAttr "Завершен" "Булево" @()
			$result += New-StdAttr "ВедущаяЗадача" "ЗадачаСсылка" @()
		}
		"Task" {
			$result += New-StdAttr "Выполнена" "Булево" @()
			$result += New-StdAttr "БизнесПроцесс" "БизнесПроцессСсылка" @()
			$result += New-StdAttr "ТочкаМаршрута" "БизнесПроцессТочкаМаршрутаСсылка" @()
		}
		"ChartOfAccounts" {
			$result += New-StdAttr "Вид" "ВидСчета" @()
			$result += New-StdAttr "Забалансовый" "Булево" @()
			$result += New-StdAttr "Порядок" "Число" @()
		}
		"ChartOfCalculationTypes" {
			$result += New-StdAttr "ПериодДействияБазовый" "Булево" @()
		}
	}

	# Предопределённые данные есть у всех перечисленных типов, кроме документов и задач
	if ($objType -in @("Catalog", "ChartOfCharacteristicTypes", "ChartOfAccounts", "ChartOfCalculationTypes")) {
		$result += New-StdAttr "Предопределенный" "Булево" @()
		$result += New-StdAttr "ИмяПредопределенныхДанных" "Строка" @()
	}
	return $result
}

function Get-Attributes($parentNode, [string]$childTag = "Attribute", [bool]$isDimension = $false) {
	$result = @()
	foreach ($attr in $parentNode.SelectNodes("md:$childTag", $ns)) {
		$aprops = $attr.SelectSingleNode("md:Properties", $ns)
		if (-not $aprops) { continue }
		$attrName = $aprops.SelectSingleNode("md:Name", $ns).InnerText
		$typeStr = Format-Type $aprops.SelectSingleNode("md:Type", $ns)
		$aflags = Format-Flags $aprops $isDimension
		$result += @{ Name=$attrName; Type=$typeStr; Flags=$aflags; Props=$aprops }
	}
	return $result
}

function Get-TabularSections($parentNode) {
	$result = @()
	foreach ($ts in $parentNode.SelectNodes("md:TabularSection", $ns)) {
		$tprops = $ts.SelectSingleNode("md:Properties", $ns)
		$tsName = $tprops.SelectSingleNode("md:Name", $ns).InnerText
		$tchildObjs = $ts.SelectSingleNode("md:ChildObjects", $ns)
		$cols = @()
		if ($tchildObjs) { $cols = @(Get-Attributes $tchildObjs) }
		$result += @{ Name=$tsName; Columns=$cols; ColCount=$cols.Count }
	}
	return $result
}

function Format-AttrLine([hashtable]$attr, [int]$maxNameLen = 30) {
	$padded = $attr.Name.PadRight($maxNameLen)
	return "  $padded $($attr.Type)$($attr.Flags)"
}

function Get-MaxNameLen($attrs) {
	$max = 10
	foreach ($a in $attrs) {
		if ($a.Name.Length -gt $max) { $max = $a.Name.Length }
	}
	return [Math]::Min($max + 2, 40)
}

function Get-SimpleChildren($parentNode, [string]$tag) {
	$result = @()
	foreach ($child in $parentNode.SelectNodes("md:$tag", $ns)) {
		# Form/Template в ChildObjects — простые узлы с именем в тексте, а Command — узел
		# с вложенным Properties: у него InnerText склеил бы всё содержимое в одну строку.
		$nameNode = $child.SelectSingleNode("md:Properties/md:Name", $ns)
		if ($nameNode) { $result += $nameNode.InnerText } else { $result += $child.InnerText }
	}
	return $result
}

function Sort-AttrsRefFirst($attrs) {
	$refs = @()
	$prims = @()
	foreach ($a in $attrs) {
		$t = $a.Type
		if ($t -match 'Ссылка\.' -or $t -match 'Характеристика\.' -or $t -match 'ОпределяемыйТип\.' -or $t -match 'ПланСчетовСсылка' -or $t -match 'ПВХСсылка' -or $t -match 'ПВРСсылка') {
			$refs += $a
		} else {
			$prims += $a
		}
	}
	return @($refs) + @($prims)
}

function Decline-Cols([int]$n) {
	$m = $n % 10
	$h = $n % 100
	if ($h -ge 11 -and $h -le 19) { return "колонок" }
	if ($m -eq 1) { return "колонка" }
	if ($m -ge 2 -and $m -le 4) { return "колонки" }
	return "колонок"
}

function Format-SourceType([string]$raw) {
	$raw = $raw -replace '^d\d+p\d+:', 'cfg:'
	if ($raw -match '^cfg:(\w+)\.(.+)$') {
		$prefix = $Matches[1]; $name = $Matches[2]
		if ($objectTypeMap.ContainsKey($prefix)) { return "$($objectTypeMap[$prefix]).$name" }
	}
	if ($raw -match '^cfg:(.+)$') { return $Matches[1] }
	return $raw
}

function Get-HTTPEndpoints($childObjs) {
	$result = @()
	foreach ($tpl in $childObjs.SelectNodes("md:URLTemplate", $ns)) {
		$tp = $tpl.SelectSingleNode("md:Properties", $ns)
		$tplName = $tp.SelectSingleNode("md:Name", $ns).InnerText
		$template = $tp.SelectSingleNode("md:Template", $ns).InnerText
		$methods = @()
		$tplCO = $tpl.SelectSingleNode("md:ChildObjects", $ns)
		if ($tplCO) {
			foreach ($m in $tplCO.SelectNodes("md:Method", $ns)) {
				$mp = $m.SelectSingleNode("md:Properties", $ns)
				$httpMethod = $mp.SelectSingleNode("md:HTTPMethod", $ns).InnerText
				$handler = $mp.SelectSingleNode("md:Handler", $ns).InnerText
				$methods += @{ HTTPMethod=$httpMethod; Handler=$handler; Name=$mp.SelectSingleNode("md:Name",$ns).InnerText }
			}
		}
		$result += @{ Name=$tplName; Template=$template; Methods=$methods }
	}
	return $result
}

function Get-WSOperations($childObjs) {
	$result = @()
	foreach ($op in $childObjs.SelectNodes("md:Operation", $ns)) {
		$oprops = $op.SelectSingleNode("md:Properties", $ns)
		$opName = $oprops.SelectSingleNode("md:Name", $ns).InnerText
		$retType = $oprops.SelectSingleNode("md:XDTOReturningValueType", $ns)
		$retStr = if ($retType -and $retType.InnerText) { $retType.InnerText } else { "void" }
		$procName = $oprops.SelectSingleNode("md:ProcedureName", $ns)
		$params = @()
		$opCO = $op.SelectSingleNode("md:ChildObjects", $ns)
		if ($opCO) {
			foreach ($p in $opCO.SelectNodes("md:Parameter", $ns)) {
				$pp = $p.SelectSingleNode("md:Properties", $ns)
				$pName = $pp.SelectSingleNode("md:Name", $ns).InnerText
				$pType = $pp.SelectSingleNode("md:XDTOValueType", $ns)
				$pTypeStr = if ($pType) { $pType.InnerText } else { "?" }
				$dir = $pp.SelectSingleNode("md:TransferDirection", $ns)
				$dirStr = if ($dir -and $dir.InnerText -ne "In") { " [$($dir.InnerText.ToLower())]" } else { "" }
				$params += "${pName}: ${pTypeStr}${dirStr}"
			}
		}
		$paramStr = $params -join ", "
		$result += @{ Name=$opName; Params=$paramStr; ReturnType=$retStr; ProcName=$(if ($procName) { $procName.InnerText } else { "" }) }
	}
	return $result
}

# --- Support status of this object (Ext/ParentConfigurations.bin) ---
# See docs/1c-support-state-spec.md. Walks up to the config root, decodes the
# object's support rule. Never throws — degrades to "не на поддержке".
function Test-ExternalObjectRoot([string]$xmlPath) {
	if (-not (Test-Path $xmlPath)) { return $false }
	try {
		[xml]$mx = Get-Content -Path $xmlPath -Encoding UTF8
		$el = $mx.DocumentElement.FirstChild
		while ($el -and $el.NodeType -ne 'Element') { $el = $el.NextSibling }
		if ($el) { return @('ExternalDataProcessor','ExternalReport') -contains $el.LocalName }
	} catch {}
	return $false
}
function Get-ObjectSupportStatus([string]$objUuid) {
	try {
		if (Test-ExternalObjectRoot $ObjectPath) { return $null }
		# Walk up to the config root (dir with Configuration.xml or Ext/ParentConfigurations.bin).
		$d = [System.IO.Path]::GetDirectoryName($ObjectPath)
		$binPath = $null
		for ($i = 0; $i -lt 8 -and $d; $i++) {
			$cand = Join-Path (Join-Path $d "Ext") "ParentConfigurations.bin"
			if ((Test-Path $cand) -or (Test-Path (Join-Path $d "Configuration.xml"))) { $binPath = $cand; break }
			$parent = [System.IO.Path]::GetDirectoryName($d)
			if ($parent -eq $d) { break }
			$d = $parent
		}
		if (-not $binPath -or -not (Test-Path $binPath)) { return "не на поддержке" }
		$bytes = [System.IO.File]::ReadAllBytes($binPath)
		if ($bytes.Length -le 32) { return "снято с поддержки (правки свободны)" }
		$start = 0
		if ($bytes.Length -ge 3 -and $bytes[0] -eq 0xEF -and $bytes[1] -eq 0xBB -and $bytes[2] -eq 0xBF) { $start = 3 }
		$text = [System.Text.Encoding]::UTF8.GetString($bytes, $start, $bytes.Length - $start)
		$h = [regex]::Match($text, '^\{6,(\d+),(\d+),')
		if (-not $h.Success) { return "не на поддержке" }
		$G = [int]$h.Groups[1].Value
		$K = [int]$h.Groups[2].Value
		if ($K -eq 0) { return "снято с поддержки (правки свободны)" }
		if ($G -eq 1) { return "конфигурация read-only (возможность изменения выключена) — правки невозможны без включения" }
		if (-not $objUuid) { return "не на поддержке" }
		# Conservative fold: locked if f1=0 in ANY vendor block.
		$u = [regex]::Escape($objUuid.ToLower())
		$best = $null
		foreach ($m in [regex]::Matches($text, "([0-2]),0,$u")) {
			$f1 = [int]$m.Groups[1].Value
			if ($null -eq $best -or $f1 -lt $best) { $best = $f1 }
		}
		if ($null -eq $best) { return "не на поддержке" }
		switch ($best) {
			0 { return "на замке — прямая правка сломает обновления; дорабатывай через cfe-* либо включи редактирование объекта" }
			1 { return "редактируется с сохранением поддержки" }
			2 { return "снято с поддержки (правки свободны)" }
		}
		return "не на поддержке"
	} catch { return "не на поддержке" }
}

# --- Extract metadata ---
$props = $typeNode.SelectSingleNode("md:Properties", $ns)
$childObjs = $typeNode.SelectSingleNode("md:ChildObjects", $ns)
$objName = $props.SelectSingleNode("md:Name", $ns).InnerText
$synNode = $props.SelectSingleNode("md:Synonym", $ns)
$synonym = Get-MLText $synNode

# Presentations (type-choice dialogs show "Представление объекта" as the ref type name)
$objPresentation = Get-MLText $props.SelectSingleNode("md:ObjectPresentation", $ns)
$extObjPresentation = Get-MLText $props.SelectSingleNode("md:ExtendedObjectPresentation", $ns)
$listPresentation = Get-MLText $props.SelectSingleNode("md:ListPresentation", $ns)
$extListPresentation = Get-MLText $props.SelectSingleNode("md:ExtendedListPresentation", $ns)

# Reference (ref-typed) metadata objects — those with a ...Ref type
$refMdTypes = @("Catalog","Document","Enum","ChartOfAccounts","ChartOfCharacteristicTypes",
	"ChartOfCalculationTypes","ExchangePlan","BusinessProcess","Task")
$isRefObject = $refMdTypes -contains $mdType

# Effective type presentation: ObjectPresentation -> Synonym -> Name
$typePresentation = if ($objPresentation) { $objPresentation }
	elseif ($synonym) { $synonym }
	else { $objName }

# --- Handle -Name drill-down ---
$drillDone = $false
if ($Name -and $childObjs) {
	# Search in attributes/dimensions/resources
	$attrTags = @("Attribute","Dimension","Resource")
	foreach ($tag in $attrTags) {
		if ($drillDone) { break }
		foreach ($attr in $childObjs.SelectNodes("md:$tag", $ns)) {
			$ap = $attr.SelectSingleNode("md:Properties", $ns)
			if (-not $ap) { continue }
			$an = $ap.SelectSingleNode("md:Name", $ns).InnerText
			if ($an -eq $Name) {
				$tagRu = switch ($tag) {
					"Attribute" { "Реквизит" }
					"Dimension" { "Измерение" }
					"Resource" { "Ресурс" }
				}
				Out "$($tagRu): $an"
				$typeStr = Format-Type $ap.SelectSingleNode("md:Type", $ns)
				Out "  Тип: $typeStr"
				$fc = $ap.SelectSingleNode("md:FillChecking", $ns)
				Out "  Обязательный: $(if ($fc -and $fc.InnerText -eq 'ShowError') { 'да' } else { 'нет' })"
				$idx = $ap.SelectSingleNode("md:Indexing", $ns)
				$idxRu = if (-not $idx -or $idx.InnerText -eq 'DontIndex') { 'нет' }
					elseif ($idx.InnerText -eq 'Index') { 'Индекс' }
					elseif ($idx.InnerText -eq 'IndexWithAdditionalOrder') { 'Индекс с доп. упорядочиванием' }
					else { $idx.InnerText }
				Out "  Индексирование: $idxRu"
				$ml = $ap.SelectSingleNode("md:MultiLine", $ns)
				if ($ml -and $ml.InnerText -eq "true") { Out "  Многострочный: да" }
				$use = $ap.SelectSingleNode("md:Use", $ns)
				if ($use -and $use.InnerText -ne "ForItem") {
					$useRu = switch ($use.InnerText) {
						"ForFolder" { "для папок" }
						"ForFolderAndItem" { "для папок и элементов" }
						default { $use.InnerText }
					}
					Out "  Использование: $useRu"
				}
				$fv = $ap.SelectSingleNode("md:FillValue", $ns)
				if ($fv -and -not ($fv.GetAttribute("nil", "http://www.w3.org/2001/XMLSchema-instance") -eq "true") -and $fv.InnerText) {
					$fvText = $fv.InnerText
					if ($fvText -match '\.EmptyRef$') { $fvText = "Пустая ссылка" }
					elseif ($fvText -eq "false") { $fvText = "Ложь" }
					elseif ($fvText -eq "true") { $fvText = "Истина" }
					Out "  Значение заполнения: $fvText"
				} else {
					Out "  Значение заполнения: —"
				}
				if ($tag -eq "Dimension") {
					$master = $ap.SelectSingleNode("md:Master", $ns)
					Out "  Ведущее: $(if ($master -and $master.InnerText -eq 'true') { 'да' } else { 'нет' })"
					$mf = $ap.SelectSingleNode("md:MainFilter", $ns)
					Out "  Основной отбор: $(if ($mf -and $mf.InnerText -eq 'true') { 'да' } else { 'нет' })"
				}
				$synA = $ap.SelectSingleNode("md:Synonym", $ns)
				$synText = Get-MLText $synA
				if ($synText -and $synText -ne $an) { Out "  Синоним: $synText" }
				$drillDone = $true
				break
			}
		}
	}

	# Search in tabular sections
	if (-not $drillDone) {
		foreach ($ts in $childObjs.SelectNodes("md:TabularSection", $ns)) {
			$tp = $ts.SelectSingleNode("md:Properties", $ns)
			$tn = $tp.SelectSingleNode("md:Name", $ns).InnerText
			if ($tn -eq $Name) {
				$tsCO = $ts.SelectSingleNode("md:ChildObjects", $ns)
				$cols = @()
				if ($tsCO) { $cols = @(Get-Attributes $tsCO) }
				Out "ТЧ: $tn ($($cols.Count) $(Decline-Cols $cols.Count)):"
				if ($cols.Count -gt 0) {
					$ml = Get-MaxNameLen $cols
					foreach ($c in $cols) { Out (Format-AttrLine $c $ml) }
				}
				$drillDone = $true
				break
			}
		}
	}

	# Search in enum values
	if (-not $drillDone) {
		foreach ($ev in $childObjs.SelectNodes("md:EnumValue", $ns)) {
			$ep = $ev.SelectSingleNode("md:Properties", $ns)
			$en = $ep.SelectSingleNode("md:Name", $ns).InnerText
			if ($en -eq $Name) {
				$synE = $ep.SelectSingleNode("md:Synonym", $ns)
				$synText = Get-MLText $synE
				Out "Значение перечисления: $en"
				if ($synText) { Out "  Синоним: `"$synText`"" }
				$cm = $ep.SelectSingleNode("md:Comment", $ns)
				if ($cm -and $cm.InnerText) { Out "  Комментарий: $($cm.InnerText)" }
				$drillDone = $true
				break
			}
		}
	}

	# Search in HTTPService URLTemplates
	if (-not $drillDone -and $mdType -eq "HTTPService" -and $childObjs) {
		foreach ($tpl in $childObjs.SelectNodes("md:URLTemplate", $ns)) {
			$tp = $tpl.SelectSingleNode("md:Properties", $ns)
			if ($tp.SelectSingleNode("md:Name", $ns).InnerText -eq $Name) {
				$template = $tp.SelectSingleNode("md:Template", $ns).InnerText
				Out "Шаблон URL: $Name"
				Out "  Путь: $template"
				$tplCO = $tpl.SelectSingleNode("md:ChildObjects", $ns)
				if ($tplCO) {
					foreach ($m in $tplCO.SelectNodes("md:Method", $ns)) {
						$mp = $m.SelectSingleNode("md:Properties", $ns)
						$httpMethod = $mp.SelectSingleNode("md:HTTPMethod", $ns).InnerText
						$handler = $mp.SelectSingleNode("md:Handler", $ns).InnerText
						Out "  $httpMethod → $handler"
					}
				}
				$drillDone = $true; break
			}
		}
	}

	# Search in WebService Operations
	if (-not $drillDone -and $mdType -eq "WebService" -and $childObjs) {
		foreach ($op in $childObjs.SelectNodes("md:Operation", $ns)) {
			$oprops = $op.SelectSingleNode("md:Properties", $ns)
			if ($oprops.SelectSingleNode("md:Name", $ns).InnerText -eq $Name) {
				Out "Операция: $Name"
				$retType = $oprops.SelectSingleNode("md:XDTOReturningValueType", $ns)
				Out "  Возвращает: $(if ($retType -and $retType.InnerText) { $retType.InnerText } else { 'void' })"
				$procName = $oprops.SelectSingleNode("md:ProcedureName", $ns)
				if ($procName -and $procName.InnerText) { Out "  Процедура: $($procName.InnerText)" }
				$comment = $oprops.SelectSingleNode("md:Comment", $ns)
				if ($comment -and $comment.InnerText) { Out "  Комментарий: $($comment.InnerText)" }
				$opCO = $op.SelectSingleNode("md:ChildObjects", $ns)
				if ($opCO) {
					$params = $opCO.SelectNodes("md:Parameter", $ns)
					if ($params.Count -gt 0) {
						Out "  Параметры:"
						foreach ($p in $params) {
							$pp = $p.SelectSingleNode("md:Properties", $ns)
							$pName = $pp.SelectSingleNode("md:Name", $ns).InnerText
							$pType = $pp.SelectSingleNode("md:XDTOValueType", $ns)
							$dir = $pp.SelectSingleNode("md:TransferDirection", $ns)
							$dirStr = if ($dir -and $dir.InnerText -ne "In") { " [$($dir.InnerText.ToLower())]" } else { "" }
							Out "    ${pName}: $(if ($pType) { $pType.InnerText } else { '?' })${dirStr}"
						}
					}
				}
				$drillDone = $true; break
			}
		}
	}

	# Не нашли среди дочерних объектов — пробуем стандартные реквизиты ниже
}

# Drill-down по стандартному реквизиту — единственный способ увидеть состав типов целиком,
# когда в сводке он свёрнут в счётчик. Живёт в Properties, а не в ChildObjects, поэтому идёт
# отдельной веткой и работает даже у объектов без ChildObjects.
if ($Name -and -not $drillDone) {
	$stdAll = @(Get-StandardAttributes $props $mdType "full") + @(Get-StandardAttributesFull $props $mdType $objName)
	foreach ($s in $stdAll) {
		if ($s.Name -ne $Name) { continue }
		Out "Стандартный реквизит: $($s.Name)"

		# Состав типов разворачиваем списком: ради этого drill-down и нужен
		$typeSrc = $null
		if ($Name -eq "ТипЗначения") { $typeSrc = $props.SelectSingleNode("md:Type", $ns) }
		$typeList = @()
		if ($typeSrc) {
			foreach ($t in $typeSrc.SelectNodes("v8:Type", $ns))    { $typeList += Format-SingleType $t.InnerText $typeSrc }
			foreach ($t in $typeSrc.SelectNodes("v8:TypeSet", $ns)) { $typeList += Format-SingleTypeSet $t.InnerText }
		} elseif ($Name -eq "Владелец") {
			$ownersNode = $props.SelectSingleNode("md:Owners", $ns)
			if ($ownersNode) {
				foreach ($it in $ownersNode.SelectNodes("xr:Item", $ns)) { $typeList += Format-MDObjectRef $it.InnerText }
			}
		}

		if ($typeList.Count -gt 0) {
			Out "  Типы ($($typeList.Count)):"
			foreach ($t in $typeList) { Out "    $t" }
		} else {
			Out "  Тип: $($s.Type)"
		}
		$flagText = $s.Flags.Trim()
		if ($flagText) { Out "  Свойства: $($flagText.Trim('[', ']'))" }
		$drillDone = $true
		break
	}
	if (-not $drillDone) {
		Write-Host "[ERROR] '$Name' not found in $objName"
		exit 1
	}
}

# --- Main output (not drill-down) ---
if (-not $drillDone) {

	# --- Build header ---
	$header = "=== $ruTypeName`: $objName"
	if ($synonym -and $synonym -ne $objName) { $header += " — `"$synonym`"" }
	$header += " ==="
	Out $header
	$support = Get-ObjectSupportStatus $typeNode.GetAttribute('uuid')
	if ($null -ne $support) { Out "Поддержка: $support" }

	# --- Type presentation (ref objects) ---
	if ($isRefObject) {
		Out "Представление типа: $typePresentation"
		if ($Mode -eq "full") {
			if ($objPresentation)     { Out "Представление объекта: $objPresentation" }
			if ($extObjPresentation)  { Out "Расширенное представление объекта: $extObjPresentation" }
			if ($listPresentation)    { Out "Представление списка: $listPresentation" }
			if ($extListPresentation) { Out "Расширенное представление списка: $extListPresentation" }
		}
	}

	# --- Mode: brief ---
	if ($Mode -eq "brief") {
		# Подчинённость — единственный факт структуры, который из brief не выводится никак,
		# а без него код записи элемента падает. Про обязательность здесь намеренно молчим:
		# обязательными бывают и обычные реквизиты, а их brief не разбирает.
		if ($mdType -eq "Catalog") {
			$ownersNode = $props.SelectSingleNode("md:Owners", $ns)
			$ownerNames = @()
			if ($ownersNode) {
				foreach ($it in $ownersNode.SelectNodes("xr:Item", $ns)) {
					$ownerNames += ($it.InnerText -replace '^\w+\.', '')
				}
			}
			if ($ownerNames.Count -gt 0) { Out "Подчинён: $($ownerNames -join ', ')" }
		}

		# Attributes
		$attrs = @()
		if ($childObjs) { $attrs = @(Get-Attributes $childObjs) }
		if ($attrs.Count -gt 0) {
			$names = ($attrs | ForEach-Object { $_.Name }) -join ", "
			Out "Реквизиты ($($attrs.Count)): $names"
		}

		# Dimensions/Resources for registers
		if ($mdType -match "Register$") {
			$dims = @()
			if ($childObjs) { $dims = @(Get-Attributes $childObjs "Dimension" $true) }
			if ($dims.Count -gt 0) {
				$names = ($dims | ForEach-Object { $_.Name }) -join ", "
				Out "Измерения ($($dims.Count)): $names"
			}
			$res = @()
			if ($childObjs) { $res = @(Get-Attributes $childObjs "Resource") }
			if ($res.Count -gt 0) {
				$names = ($res | ForEach-Object { $_.Name }) -join ", "
				Out "Ресурсы ($($res.Count)): $names"
			}
		}

		# Tabular sections
		$tss = @()
		if ($childObjs) { $tss = @(Get-TabularSections $childObjs) }
		if ($tss.Count -gt 0) {
			$tsParts = $tss | ForEach-Object { "$($_.Name)($($_.ColCount))" }
			Out "ТЧ ($($tss.Count)): $($tsParts -join ', ')"
		}

		# Enum values
		if ($mdType -eq "Enum") {
			$vals = @()
			if ($childObjs) {
				foreach ($ev in $childObjs.SelectNodes("md:EnumValue", $ns)) {
					$ep = $ev.SelectSingleNode("md:Properties", $ns)
					$vals += $ep.SelectSingleNode("md:Name", $ns).InnerText
				}
			}
			if ($vals.Count -gt 0) {
				Out "Значения ($($vals.Count)): $($vals -join ', ')"
			}
		}

		# DefinedType brief
		if ($mdType -eq "DefinedType") {
			$typeNode2 = $props.SelectSingleNode("md:Type", $ns)
			if ($typeNode2) {
				$types = @()
				foreach ($t in $typeNode2.SelectNodes("v8:Type", $ns)) {
					$types += Format-SingleType $t.InnerText $typeNode2
				}
				if ($types.Count -gt 0) {
					Out "Типы ($($types.Count)): $($types -join ', ')"
				}
			}
		}

		# CommonModule brief (same as overview — already compact)
		if ($mdType -eq "CommonModule") {
			$flags = @()
			if ($props.SelectSingleNode("md:Global", $ns).InnerText -eq "true") { $flags += "Глобальный" }
			if ($props.SelectSingleNode("md:Server", $ns).InnerText -eq "true") { $flags += "Сервер" }
			if ($props.SelectSingleNode("md:ServerCall", $ns).InnerText -eq "true") { $flags += "Вызов сервера" }
			if ($props.SelectSingleNode("md:ClientManagedApplication", $ns).InnerText -eq "true") { $flags += "Клиент управляемое" }
			if ($props.SelectSingleNode("md:ClientOrdinaryApplication", $ns).InnerText -eq "true") { $flags += "Обычный клиент" }
			if ($props.SelectSingleNode("md:ExternalConnection", $ns).InnerText -eq "true") { $flags += "Внешнее соединение" }
			if ($props.SelectSingleNode("md:Privileged", $ns).InnerText -eq "true") { $flags += "Привилегированный" }
			$reuse = $props.SelectSingleNode("md:ReturnValuesReuse", $ns)
			if ($reuse -and $reuse.InnerText -ne "DontUse") {
				$reuseRu = if ($reuseMap.ContainsKey($reuse.InnerText)) { $reuseMap[$reuse.InnerText] } else { $reuse.InnerText }
				$flags += "Повторное использование: $reuseRu"
			}
			if ($flags.Count -gt 0) { Out ($flags -join " | ") }
		}

		# ScheduledJob brief (same as overview — already compact)
		if ($mdType -eq "ScheduledJob") {
			$method = $props.SelectSingleNode("md:MethodName", $ns)
			if ($method -and $method.InnerText) {
				$mName = $method.InnerText
				if ($mName -match '^CommonModule\.(.+)$') { $mName = $Matches[1] }
				Out "Метод: $mName"
			}
			$sjParts = @()
			$use = $props.SelectSingleNode("md:Use", $ns)
			$sjParts += "Использование: $(if ($use -and $use.InnerText -eq 'true') { 'да' } else { 'нет' })"
			$predef = $props.SelectSingleNode("md:Predefined", $ns)
			$sjParts += "Предопределённое: $(if ($predef -and $predef.InnerText -eq 'true') { 'да' } else { 'нет' })"
			$restartCnt = $props.SelectSingleNode("md:RestartCountOnFailure", $ns)
			$restartInt = $props.SelectSingleNode("md:RestartIntervalOnFailure", $ns)
			if ($restartCnt -and [int]$restartCnt.InnerText -gt 0) {
				$sjParts += "Перезапуск: $($restartCnt.InnerText) (через $($restartInt.InnerText) сек)"
			}
			Out ($sjParts -join " | ")
		}

		# EventSubscription brief
		if ($mdType -eq "EventSubscription") {
			$esParts = @()
			$event = $props.SelectSingleNode("md:Event", $ns)
			if ($event -and $event.InnerText) {
				$evRu = if ($eventMap.ContainsKey($event.InnerText)) { $eventMap[$event.InnerText] } else { $event.InnerText }
				$esParts += "Событие: $evRu"
			}
			$handler = $props.SelectSingleNode("md:Handler", $ns)
			if ($handler -and $handler.InnerText) {
				$hName = $handler.InnerText
				if ($hName -match '^CommonModule\.(.+)$') { $hName = $Matches[1] }
				$esParts += "Обработчик: $hName"
			}
			$source = $props.SelectSingleNode("md:Source", $ns)
			if ($source) {
				$srcCount = $source.SelectNodes("v8:Type", $ns).Count
				if ($srcCount -gt 0) { $esParts += "Источники: $srcCount" }
			}
			if ($esParts.Count -gt 0) { Out ($esParts -join " | ") }
		}

		# HTTPService brief
		if ($mdType -eq "HTTPService") {
			$rootURL = $props.SelectSingleNode("md:RootURL", $ns)
			if ($rootURL -and $rootURL.InnerText) { Out "Корневой URL: /$($rootURL.InnerText)" }
			if ($childObjs) {
				$endpoints = @(Get-HTTPEndpoints $childObjs)
				if ($endpoints.Count -gt 0) {
					$totalMethods = ($endpoints | ForEach-Object { $_.Methods.Count } | Measure-Object -Sum).Sum
					Out "Шаблоны: $($endpoints.Count) | Методы: $totalMethods"
				}
			}
		}

		# WebService brief
		if ($mdType -eq "WebService") {
			$nsUrl = $props.SelectSingleNode("md:Namespace", $ns)
			if ($nsUrl -and $nsUrl.InnerText) { Out "Пространство имён: $($nsUrl.InnerText)" }
			if ($childObjs) {
				$ops = @(Get-WSOperations $childObjs)
				if ($ops.Count -gt 0) { Out "Операции: $($ops.Count)" }
			}
		}
	} else {
		# --- Mode: overview / full ---

		# Document-specific header properties
		if ($mdType -eq "Document") {
			$posting = $props.SelectSingleNode("md:Posting", $ns)

			$parts = @()
			# Номер уехал в блок стандартных реквизитов — здесь остаются свойства объекта
			if ($posting) {
				$parts += "Проведение: $(if ($posting.InnerText -eq 'Allow') { 'да' } else { 'нет' })"
			}
			if ($parts.Count -gt 0) { Out ($parts -join " | ") }
		}

		# Свойства иерархии и подчинения. Иерархия — не только у справочников: свойство
		# Hierarchical есть и у ПВХ (в корпусе acc/erp/ut/unf их 13), а без этой строки
		# в full появлялся Родитель, происхождение которого было ниоткуда не видно.
		$parts = @()
		$hier = $props.SelectSingleNode("md:Hierarchical", $ns)
		if ($hier -and $hier.InnerText -eq "true") {
			$ht = $props.SelectSingleNode("md:HierarchyType", $ns)
			$htText = if ($ht -and $ht.InnerText -eq "HierarchyFoldersAndItems") { "группы и элементы" } else { "элементы" }
			$limitNode = $props.SelectSingleNode("md:LimitLevelCount", $ns)
			$levelNode = $props.SelectSingleNode("md:LevelCount", $ns)
			if ($limitNode -and $limitNode.InnerText -eq "true" -and $levelNode) {
				$htText += ", уровней: $($levelNode.InnerText)"
			} else {
				$htText += ", без ограничения уровней"
			}
			$parts += "Иерархический: $htText"
		}
		# Код и Наименование уехали в блок стандартных реквизитов — здесь только свойства объекта.
		# Подчинение печатаем лишь когда оно отличается от дефолта ToItems.
		if ($mdType -eq "Catalog") {
			$sub = $props.SelectSingleNode("md:SubordinationUse", $ns)
			if ($sub -and $sub.InnerText -ne "ToItems") {
				$subRu = switch ($sub.InnerText) {
					"ToFolders"         { "группам" }
					"ToFoldersAndItems" { "группам и элементам" }
					default             { $sub.InnerText }
				}
				$parts += "Подчинение: $subRu"
			}
		}
		if ($parts.Count -gt 0) { Out ($parts -join " | ") }

		# Register-specific header properties
		if ($mdType -match "Register$") {
			$parts = @()
			if ($mdType -eq "InformationRegister") {
				$per = $props.SelectSingleNode("md:InformationRegisterPeriodicity", $ns)
				if ($per) {
					$perRu = if ($periodMap.ContainsKey($per.InnerText)) { $periodMap[$per.InnerText] } else { $per.InnerText }
					$parts += "Периодичность: $perRu"
				}
				$wm = $props.SelectSingleNode("md:WriteMode", $ns)
				if ($wm) {
					$wmRu = if ($writeModeMap.ContainsKey($wm.InnerText)) { $writeModeMap[$wm.InnerText] } else { $wm.InnerText }
					$parts += "Запись: $wmRu"
				}
			}
			if ($mdType -eq "AccumulationRegister") {
				$regKind = $props.SelectSingleNode("md:RegisterType", $ns)
				if ($regKind) {
					$rkRu = switch ($regKind.InnerText) {
						"Balances" { "остатки" }
						"Turnovers" { "обороты" }
						default { $regKind.InnerText }
					}
					$parts += "Вид: $rkRu"
				}
			}
			if ($parts.Count -gt 0) { Out ($parts -join " | ") }
		}

		# Constant-specific: show type
		if ($mdType -eq "Constant") {
			$typeStr = Format-Type $props.SelectSingleNode("md:Type", $ns)
			if ($typeStr) { Out "Тип: $typeStr" }
		}

		# Report-specific: MainDataCompositionSchema
		if ($mdType -eq "Report") {
			$mainDCS = $props.SelectSingleNode("md:MainDataCompositionSchema", $ns)
			if ($mainDCS -and $mainDCS.InnerText) {
				$dcsName = $mainDCS.InnerText
				if ($dcsName -match '\.Template\.(.+)$') { $dcsName = $Matches[1] }
				Out "Основная СКД: $dcsName"
			}
		}

		# DefinedType: show types
		if ($mdType -eq "DefinedType") {
			$typeNode2 = $props.SelectSingleNode("md:Type", $ns)
			if ($typeNode2) {
				$types = @()
				foreach ($t in $typeNode2.SelectNodes("v8:Type", $ns)) {
					$types += Format-SingleType $t.InnerText $typeNode2
				}
				if ($types.Count -gt 0) {
					Out "Типы ($($types.Count)):"
					foreach ($t in $types) { Out "  $t" }
				}
			}
		}

		# CommonModule: show flags
		if ($mdType -eq "CommonModule") {
			$flags = @()
			if ($props.SelectSingleNode("md:Global", $ns).InnerText -eq "true") { $flags += "Глобальный" }
			if ($props.SelectSingleNode("md:Server", $ns).InnerText -eq "true") { $flags += "Сервер" }
			if ($props.SelectSingleNode("md:ServerCall", $ns).InnerText -eq "true") { $flags += "Вызов сервера" }
			if ($props.SelectSingleNode("md:ClientManagedApplication", $ns).InnerText -eq "true") { $flags += "Клиент управляемое" }
			if ($props.SelectSingleNode("md:ClientOrdinaryApplication", $ns).InnerText -eq "true") { $flags += "Обычный клиент" }
			if ($props.SelectSingleNode("md:ExternalConnection", $ns).InnerText -eq "true") { $flags += "Внешнее соединение" }
			if ($props.SelectSingleNode("md:Privileged", $ns).InnerText -eq "true") { $flags += "Привилегированный" }
			$reuse = $props.SelectSingleNode("md:ReturnValuesReuse", $ns)
			if ($reuse -and $reuse.InnerText -ne "DontUse") {
				$reuseRu = if ($reuseMap.ContainsKey($reuse.InnerText)) { $reuseMap[$reuse.InnerText] } else { $reuse.InnerText }
				$flags += "Повторное использование: $reuseRu"
			}
			if ($flags.Count -gt 0) { Out ($flags -join " | ") }
		}

		# ScheduledJob: show method and flags
		if ($mdType -eq "ScheduledJob") {
			$method = $props.SelectSingleNode("md:MethodName", $ns)
			if ($method -and $method.InnerText) {
				$mName = $method.InnerText
				if ($mName -match '^CommonModule\.(.+)$') { $mName = $Matches[1] }
				Out "Метод: $mName"
			}
			$sjParts = @()
			$use = $props.SelectSingleNode("md:Use", $ns)
			$sjParts += "Использование: $(if ($use -and $use.InnerText -eq 'true') { 'да' } else { 'нет' })"
			$predef = $props.SelectSingleNode("md:Predefined", $ns)
			$sjParts += "Предопределённое: $(if ($predef -and $predef.InnerText -eq 'true') { 'да' } else { 'нет' })"
			$restartCnt = $props.SelectSingleNode("md:RestartCountOnFailure", $ns)
			$restartInt = $props.SelectSingleNode("md:RestartIntervalOnFailure", $ns)
			if ($restartCnt -and [int]$restartCnt.InnerText -gt 0) {
				$sjParts += "Перезапуск: $($restartCnt.InnerText) (через $($restartInt.InnerText) сек)"
			}
			Out ($sjParts -join " | ")
		}

		# EventSubscription: show event, handler, sources
		if ($mdType -eq "EventSubscription") {
			$event = $props.SelectSingleNode("md:Event", $ns)
			if ($event -and $event.InnerText) {
				$evRu = if ($eventMap.ContainsKey($event.InnerText)) { $eventMap[$event.InnerText] } else { $event.InnerText }
				Out "Событие: $evRu"
			}
			$handler = $props.SelectSingleNode("md:Handler", $ns)
			if ($handler -and $handler.InnerText) {
				$hName = $handler.InnerText
				if ($hName -match '^CommonModule\.(.+)$') { $hName = $Matches[1] }
				Out "Обработчик: $hName"
			}
			$source = $props.SelectSingleNode("md:Source", $ns)
			if ($source) {
				$srcTypes = @()
				foreach ($t in $source.SelectNodes("v8:Type", $ns)) {
					$srcTypes += Format-SourceType $t.InnerText
				}
				if ($srcTypes.Count -gt 0) {
					if ($Mode -eq "full") {
						Out "Источники ($($srcTypes.Count)):"
						foreach ($s in $srcTypes) { Out "  $s" }
					} else {
						Out "Источники ($($srcTypes.Count))"
					}
				}
			}
		}

		# HTTPService: show root URL and endpoints
		if ($mdType -eq "HTTPService") {
			$rootURL = $props.SelectSingleNode("md:RootURL", $ns)
			if ($rootURL -and $rootURL.InnerText) { Out "Корневой URL: /$($rootURL.InnerText)" }
			if ($childObjs) {
				$endpoints = @(Get-HTTPEndpoints $childObjs)
				if ($endpoints.Count -gt 0) {
					Out ""
					Out "Шаблоны URL ($($endpoints.Count)):"
					foreach ($ep in $endpoints) {
						Out "  $($ep.Template)"
						foreach ($m in $ep.Methods) {
							Out "    $($m.HTTPMethod.PadRight(6)) → $($m.Handler)"
						}
					}
				}
			}
		}

		# WebService: show namespace and operations
		if ($mdType -eq "WebService") {
			$nsUrl = $props.SelectSingleNode("md:Namespace", $ns)
			if ($nsUrl -and $nsUrl.InnerText) { Out "Пространство имён: $($nsUrl.InnerText)" }
			if ($childObjs) {
				$ops = @(Get-WSOperations $childObjs)
				if ($ops.Count -gt 0) {
					Out ""
					Out "Операции ($($ops.Count)):"
					foreach ($op in $ops) {
						Out "  $($op.Name)($($op.Params)) → $($op.ReturnType)"
					}
				}
			}
		}

		# --- Enum values ---
		if ($mdType -eq "Enum" -and $childObjs) {
			$vals = @()
			foreach ($ev in $childObjs.SelectNodes("md:EnumValue", $ns)) {
				$ep = $ev.SelectSingleNode("md:Properties", $ns)
				$vName = $ep.SelectSingleNode("md:Name", $ns).InnerText
				$vSyn = Get-MLText $ep.SelectSingleNode("md:Synonym", $ns)
				$vals += @{ Name=$vName; Synonym=$vSyn }
			}
			if ($vals.Count -gt 0) {
				Out ""
				Out "Значения ($($vals.Count)):"
				$ml = Get-MaxNameLen $vals
				foreach ($v in $vals) {
					$padded = $v.Name.PadRight($ml)
					$synText = if ($v.Synonym -and $v.Synonym -ne $v.Name) { "`"$($v.Synonym)`"" } else { "" }
					Out "  $padded $synText"
				}
			}
		}

		# --- Standard attributes ---
		$stdAttrs = @(Get-StandardAttributes $props $mdType $Mode)
		if ($Mode -eq "full") { $stdAttrs += @(Get-StandardAttributesFull $props $mdType $objName) }
		if ($stdAttrs.Count -gt 0) {
			Out ""
			Out "Стандартные реквизиты:"
			$ml = Get-MaxNameLen $stdAttrs
			foreach ($s in $stdAttrs) { Out (Format-AttrLine $s $ml) }
		}

		# --- Dimensions (registers) ---
		if ($mdType -match "Register$" -and $childObjs) {
			$dims = @(Get-Attributes $childObjs "Dimension" $true)
			if ($dims.Count -gt 0) {
				Out ""
				Out "Измерения ($($dims.Count)):"
				$ml = Get-MaxNameLen $dims
				foreach ($d in $dims) { Out (Format-AttrLine $d $ml) }
			}
		}

		# --- Resources (registers) ---
		if ($mdType -match "Register$" -and $childObjs) {
			$res = @(Get-Attributes $childObjs "Resource")
			if ($res.Count -gt 0) {
				Out ""
				Out "Ресурсы ($($res.Count)):"
				$ml = Get-MaxNameLen $res
				foreach ($r in $res) { Out (Format-AttrLine $r $ml) }
			}
		}

		# --- Attributes ---
		if ($childObjs -and $mdType -ne "Enum") {
			$attrs = @(Get-Attributes $childObjs)
			if ($attrs.Count -gt 0) {
				Out ""
				Out "Реквизиты ($($attrs.Count)):"
				$sorted = Sort-AttrsRefFirst $attrs
				$ml = Get-MaxNameLen $sorted
				foreach ($a in $sorted) { Out (Format-AttrLine $a $ml) }
			}
		}

		# --- Tabular sections ---
		if ($childObjs -and $mdType -ne "Enum") {
			$tss = @(Get-TabularSections $childObjs)
			if ($tss.Count -gt 0) {
				if ($Mode -eq "full") {
					foreach ($ts in $tss) {
						Out ""
						Out "ТЧ $($ts.Name) ($($ts.ColCount) $(Decline-Cols $ts.ColCount)):"
						if ($ts.ColCount -gt 0) {
							$sortedCols = Sort-AttrsRefFirst $ts.Columns
							$ml = Get-MaxNameLen $sortedCols
							foreach ($c in $sortedCols) { Out (Format-AttrLine $c $ml) }
						}
					}
				} else {
					# overview — just names with column counts
					Out ""
					$tsParts = $tss | ForEach-Object { "$($_.Name)($($_.ColCount))" }
					Out "ТЧ ($($tss.Count)): $($tsParts -join ', ')"
				}
			}
		}

		# Forms/Templates/Commands in overview for Reports & DataProcessors
		if ($Mode -eq "overview" -and $childObjs -and ($mdType -eq "Report" -or $mdType -eq "DataProcessor")) {
			$forms = @(Get-SimpleChildren $childObjs "Form")
			if ($forms.Count -gt 0) { Out "Формы: $($forms -join ', ')" }
			$templates = @(Get-SimpleChildren $childObjs "Template")
			if ($templates.Count -gt 0) { Out "Макеты: $($templates -join ', ')" }
			$commands = @(Get-SimpleChildren $childObjs "Command")
			if ($commands.Count -gt 0) { Out "Команды: $($commands -join ', ')" }
		}

		# --- Full mode: additional sections ---
		if ($Mode -eq "full" -and $childObjs) {
			# Register records (documents)
			if ($mdType -eq "Document") {
				$regRecs = @()
				foreach ($item in $props.SelectNodes("md:RegisterRecords/xr:Item", $ns)) {
					$raw = $item.InnerText
					if ($raw -match '^(\w+)\.(.+)$') {
						$prefix = $Matches[1]
						$rname = $Matches[2]
						$short = if ($regTypeMap.ContainsKey($prefix)) { $regTypeMap[$prefix] } else { $prefix }
						$regRecs += "$short.$rname"
					} else {
						$regRecs += $raw
					}
				}
				if ($regRecs.Count -gt 0) {
					Out ""
					Out "Движения ($($regRecs.Count)): $($regRecs -join ', ')"
				}

				# BasedOn
				$basedOn = @()
				foreach ($item in $props.SelectNodes("md:BasedOn/xr:Item", $ns)) {
					$raw = $item.InnerText
					if ($raw -match '^\w+\.(.+)$') { $basedOn += $Matches[1] } else { $basedOn += $raw }
				}
				if ($basedOn.Count -gt 0) {
					Out "Ввод на основании: $($basedOn -join ', ')"
				}
			}

			# Forms
			$forms = @(Get-SimpleChildren $childObjs "Form")
			if ($forms.Count -gt 0) {
				Out "Формы: $($forms -join ', ')"
			}

			# Templates
			$templates = @(Get-SimpleChildren $childObjs "Template")
			if ($templates.Count -gt 0) {
				Out "Макеты: $($templates -join ', ')"
			}

			# Commands
			$commands = @(Get-SimpleChildren $childObjs "Command")
			if ($commands.Count -gt 0) {
				Out "Команды: $($commands -join ', ')"
			}
		}
	}

	# Единственная подсказка на весь вывод — чем развернуть свёрнутый в счётчик состав
	if ($script:collapsedNames.Count -gt 0) {
		$hints = ($script:collapsedNames | ForEach-Object { "-Name $_" }) -join ", "
		Out ""
		Out "Полный состав типов: $hints"
	}
}

# --- Pagination and output ---
$totalLines = $script:lines.Count
$outLines = $script:lines

if ($Offset -gt 0) {
	if ($Offset -ge $totalLines) {
		Write-Host "[INFO] Offset $Offset exceeds total lines ($totalLines). Nothing to show."
		exit 0
	}
	$outLines = $outLines[$Offset..($totalLines - 1)]
}

if ($Limit -gt 0 -and $outLines.Count -gt $Limit) {
	$shown = $outLines[0..($Limit - 1)]
	$remaining = $totalLines - $Offset - $Limit
	$shown += ""
	$shown += "[ОБРЕЗАНО] Показано $Limit из $totalLines строк. Используйте -Offset $($Offset + $Limit) для продолжения."
	$outLines = $shown
}

if ($OutFile) {
	if (-not [System.IO.Path]::IsPathRooted($OutFile)) {
		$OutFile = Join-Path (Get-Location).Path $OutFile
	}
	$utf8 = New-Object System.Text.UTF8Encoding($true)
	[System.IO.File]::WriteAllLines($OutFile, $outLines, $utf8)
	Write-Host "Output written to $OutFile"
} else {
	foreach ($l in $outLines) { Write-Host $l }
}
