# stub-db-create v1.10 — Create temp 1C infobase with metadata stubs for EPF/ERF build
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
param(
	[Parameter(Mandatory)]
	[string]$SourceDir,

	[Parameter(Mandatory)]
	[string]$V8Path,

	[string]$TempBasePath,

	# XML проверяемой обработки/отчёта: объект кладётся в конфигурацию-заглушку, чтобы платформа
	# смогла проверить его штатными проверками. Без параметра стаб работает как раньше.
	[string]$EmbedSourceFile,

	[string[]]$AdditionalV8Arguments = @(),

	[string[]]$AdditionalIbcmdArguments = @()
)

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

function ConvertTo-CleanPath {
    # Forgive what is unambiguous in a path the caller passed: surrounding whitespace,
    # surrounding quotes that survived shell parsing, a trailing separator. A quote left
    # inside afterwards cannot be part of a real path — reject it by name instead of letting
    # 1C answer with its opaque "Неверные или отсутствующие параметры соединения".
    param([string]$Value, [string]$ParamName)
    if (-not $Value) { return $Value }
    $v = $Value.Trim()
    if ($v.Length -ge 2 -and $v[0] -eq $v[-1] -and ($v[0] -eq '"' -or $v[0] -eq "'")) {
        $v = $v.Substring(1, $v.Length - 2).Trim()
    }
    if ($v.Length -gt 3 -and ($v[-1] -eq '\' -or $v[-1] -eq '/')) { $v = $v.Substring(0, $v.Length - 1) }
    if ($v.Contains('"')) {
        Write-Host "Error: $ParamName contains a quote character: $Value" -ForegroundColor Red
        exit 1
    }
    return $v
}

$SourceDir = ConvertTo-CleanPath $SourceDir '-SourceDir'
$V8Path = ConvertTo-CleanPath $V8Path '-V8Path'
$TempBasePath = ConvertTo-CleanPath $TempBasePath '-TempBasePath'

# --- Additional platform arguments ---
$script:V8OwnedKeys = @(
    'DESIGNER', 'ENTERPRISE', 'CREATEINFOBASE', 'CONFIG',
    '/F', '/S', '/N', '/P', '/Out', '/DisableStartupDialogs',
    '/UseTemplate', '/AddToList', '/Execute', '/C', '/URL', '/UC',
    '/DumpIB', '/RestoreIB', '/DumpCfg', '/LoadCfg',
    '/DumpConfigToFiles', '/LoadConfigFromFiles', '/UpdateDBCfg',
    '/DumpExternalDataProcessorOrReportToFiles', '/LoadExternalDataProcessorOrReportFromFiles'
)
# Пакетные команды платформы. В одной командной строке DESIGNER выполняет ТОЛЬКО ПОСЛЕДНЮЮ,
# остальные молча отбрасывает (проверено на 8.3.24: /LoadConfigFromFiles вместе с
# /CheckCanApplyConfigurationExtensions завершились кодом 0 с пустым логом, и загрузка НЕ
# состоялась). Такая команда в дополнительных аргументах подменяет собой операцию навыка, а навык
# отчитывается успехом. Дополнительные аргументы — это опции, а не режимы.
$script:V8BatchKeys = @(
    '/CheckConfig', '/CheckModules', '/CheckCanApplyConfigurationExtensions',
    '/DumpDBCfgList', '/DeleteCfg', '/UpdateCfg', '/CompareCfg', '/MergeCfg',
    '/ManageCfgSupport', '/RollbackCfg', '/ConvertFiles'
)

$script:IbcmdOwnedKeys = @(
    '--db-path', '--data', '--out', '--file', '--load', '--restore',
    '--import', '--export', '--apply', '--force', '--create-database',
    '--user', '--password'
)
$script:V8SecretKeys = @('/P', '/UC', '/WSP', '/AWSP')
$script:IbcmdSecretKeys = @('--password', '--token', '--db-pwd')

function Test-ArgKeyMatch {
    # A token matches a key when it equals the key, or starts with it and the next
    # character is not a letter — catches glued /N"user" and --password=x, while
    # keeping /ClearCache distinct from /C.
    param([string]$Token, [string]$Key)
    if ($Token.Length -lt $Key.Length) { return $false }
    if (-not $Token.Substring(0, $Key.Length).Equals($Key, [System.StringComparison]::OrdinalIgnoreCase)) { return $false }
    if ($Token.Length -eq $Key.Length) { return $true }
    return -not [char]::IsLetter($Token[$Key.Length])
}

function Get-ProjectExtraArgs {
    # v8args / ibcmdargs from .v8-project.json — same upward walk as v8path.
    param([string]$Name)
    $dir = (Get-Location).Path
    while ($dir) {
        $pf = Join-Path $dir ".v8-project.json"
        if (Test-Path $pf) {
            try {
                $j = Get-Content $pf -Raw -Encoding UTF8 | ConvertFrom-Json
                if ($j.$Name) { return @($j.$Name | ForEach-Object { [string]$_ }) }
            } catch {}
            return @()
        }
        $parent = Split-Path $dir -Parent
        if (-not $parent -or $parent -eq $dir) { break }
        $dir = $parent
    }
    return @()
}

function Assert-ExtraArgs {
    # The platform accepts only one batch operation, and a duplicate connection or
    # output key fails with an opaque 1C error — reject what the skill owns itself.
    param([string[]]$ExtraArgs, [string]$Engine, [hashtable]$Hints)
    $paramName = if ($Engine -eq 'ibcmd') { '-AdditionalIbcmdArguments' } else { '-AdditionalV8Arguments' }
    $owned = if ($Engine -eq 'ibcmd') { $script:IbcmdOwnedKeys } else { $script:V8OwnedKeys }
    foreach ($tok in $ExtraArgs) {
        if ($Engine -eq 'ibcmd' -and $tok -notmatch '^-') {
            Write-Host "Error: '$tok' is a positional token — pass values as --key=value ($paramName cannot extend the ibcmd command)" -ForegroundColor Red
            exit 1
        }
        if ($Engine -ne 'ibcmd') {
            foreach ($b in $script:V8BatchKeys) {
                if (Test-ArgKeyMatch $tok $b) {
                    Write-Host "Error: $b is a batch command; passed via $paramName it would replace the skill's own operation (a command line runs only its last batch command)" -ForegroundColor Red
                    exit 1
                }
            }
        }
        foreach ($k in $owned) {
            if (Test-ArgKeyMatch $tok $k) {
                $hint = ''
                if ($Hints -and $Hints.ContainsKey($k)) { $hint = " (use $($Hints[$k]))" }
                Write-Host "Error: $k is controlled by the skill and cannot be passed via $paramName$hint" -ForegroundColor Red
                exit 1
            }
        }
    }
}

function Resolve-ExtraArgs {
    # Pick the argument list for the selected engine and validate it. An explicitly passed
    # parameter for the other engine is an error; the same keys coming from .v8-project.json
    # simply do not apply — a project may describe both engines.
    param([string]$Engine, [string[]]$V8Extra, [string[]]$IbcmdExtra, [hashtable]$Hints)
    # powershell.exe -File — how skills are invoked — cannot bind an array parameter:
    # space-separated values spill into positional ones, a comma-joined list arrives as a
    # single token. So accept the repo's list convention (comma-separated) and split here;
    # a native array call keeps working. A value containing a comma is not supported.
    $V8Extra = @($V8Extra | ForEach-Object { $_ -split ',' } | Where-Object { $_ -ne '' })
    $IbcmdExtra = @($IbcmdExtra | ForEach-Object { $_ -split ',' } | Where-Object { $_ -ne '' })
    if ($Engine -eq 'ibcmd' -and $V8Extra.Count -gt 0) {
        Write-Host "Error: -AdditionalV8Arguments applies to 1cv8 only; the selected engine is ibcmd (use -AdditionalIbcmdArguments)" -ForegroundColor Red
        exit 1
    }
    if ($Engine -ne 'ibcmd' -and $IbcmdExtra.Count -gt 0) {
        Write-Host "Error: -AdditionalIbcmdArguments applies to ibcmd only; the selected engine is 1cv8 (use -AdditionalV8Arguments)" -ForegroundColor Red
        exit 1
    }
    if ($Engine -eq 'ibcmd') {
        $extra = @(Get-ProjectExtraArgs 'ibcmdargs') + @($IbcmdExtra)
    } else {
        $extra = @(Get-ProjectExtraArgs 'v8args') + @($V8Extra)
    }
    if ($extra.Count -gt 0) { Assert-ExtraArgs $extra $Engine $Hints }
    # Plain return, no comma trick: the caller re-collects with @(...), and ,@() there
    # would nest the array — the tokens would then be glued into one argument.
    return $extra
}

function Format-ArgsForDisplay {
    # Redact values of secret-prone keys in glued, =-joined and separate forms.
    # Matching here is a plain prefix (no letter rule): over-masking costs nothing,
    # a leaked password does.
    param([string[]]$ArgList, [string]$Engine)
    $keys = if ($Engine -eq 'ibcmd') { $script:IbcmdSecretKeys } else { $script:V8SecretKeys }
    $res = @()
    $maskNext = $false
    foreach ($tok in $ArgList) {
        if ($maskNext) { $res += '***'; $maskNext = $false; continue }
        $hit = $null
        foreach ($k in $keys) {
            if ($tok.Length -ge $k.Length -and $tok.Substring(0, $k.Length).Equals($k, [System.StringComparison]::OrdinalIgnoreCase)) { $hit = $k; break }
        }
        if (-not $hit) { $res += $tok; continue }
        if ($tok.Length -eq $hit.Length) { $res += $tok; $maskNext = $true }
        elseif ($tok[$hit.Length] -eq '=') { $res += ($hit + '=***') }
        else { $res += ($hit + '***') }
    }
    return ,$res
}


# Версия формата как число: "2.20" → 220. Строковое сравнение неверно ("2.9" > "2.17").
function Get-FormatRank([string]$ver) {
	if ($ver -match '^(\d+)\.(\d+)$') { return [int]$Matches[1] * 100 + [int]$Matches[2] }
	return 0
}

# --- 1. Scan XML files for reference types ---

$typeMap = @{}  # MetadataType -> @(Name1, Name2, ...)

# Версия формата заглушечной конфигурации. Платформа грузит формат не новее себя, поэтому зашитая
# версия ломала бы сборку исходников более старого формата на соответствующей ей платформе. Берём
# версию из корня собираемого объекта (ExternalDataProcessor/ExternalReport); вложенные файлы —
# запасной вариант, если корень почему-то не попался.
$srcRootVersion = ""
$srcAnyVersion = ""

$xmlFiles = Get-ChildItem -Path $SourceDir -Filter "*.xml" -Recurse -File
foreach ($f in $xmlFiles) {
	$content = [System.IO.File]::ReadAllText($f.FullName, [System.Text.Encoding]::UTF8)

	if ($content -match '<MetaDataObject[^>]+version="(\d+\.\d+)"') {
		$ver = $Matches[1]
		if (-not $srcAnyVersion) { $srcAnyVersion = $ver }
		if (-not $srcRootVersion -and $content -match '<(ExternalDataProcessor|ExternalReport)[ >]') {
			$srcRootVersion = $ver
		}
	}

	# Ref types: cfg:CatalogRef.XXX or d5p1:CatalogRef.XXX (and similar depth prefixes d4p1, d3p1, etc.)
	$refPattern = '(?:cfg:|d\dp1:)(CatalogRef|DocumentRef|EnumRef|ChartOfAccountsRef|ChartOfCharacteristicTypesRef|ChartOfCalculationTypesRef|ExchangePlanRef|BusinessProcessRef|TaskRef)\.([A-Za-z\u0400-\u04FF\d_]+)'
	foreach ($m in [regex]::Matches($content, $refPattern)) {
		$prefix = $m.Groups[1].Value
		$name   = $m.Groups[2].Value
		$metaType = switch ($prefix) {
			"CatalogRef"                      { "Catalog" }
			"DocumentRef"                     { "Document" }
			"EnumRef"                         { "Enum" }
			"ChartOfAccountsRef"              { "ChartOfAccounts" }
			"ChartOfCharacteristicTypesRef"   { "ChartOfCharacteristicTypes" }
			"ChartOfCalculationTypesRef"      { "ChartOfCalculationTypes" }
			"ExchangePlanRef"                 { "ExchangePlan" }
			"BusinessProcessRef"              { "BusinessProcess" }
			"TaskRef"                         { "Task" }
		}
		if (-not $typeMap.ContainsKey($metaType)) { $typeMap[$metaType] = @{} }
		$typeMap[$metaType][$name] = $true
	}

	# Object types: cfg:CatalogObject.XXX etc.
	$objPattern = '(?:cfg:|d\dp1:)(CatalogObject|DocumentObject|ChartOfAccountsObject|ChartOfCharacteristicTypesObject|ChartOfCalculationTypesObject|ExchangePlanObject|BusinessProcessObject|TaskObject)\.([A-Za-z\u0400-\u04FF\d_]+)'
	foreach ($m in [regex]::Matches($content, $objPattern)) {
		$prefix = $m.Groups[1].Value
		$name   = $m.Groups[2].Value
		$metaType = switch ($prefix) {
			"CatalogObject"                      { "Catalog" }
			"DocumentObject"                     { "Document" }
			"ChartOfAccountsObject"              { "ChartOfAccounts" }
			"ChartOfCharacteristicTypesObject"   { "ChartOfCharacteristicTypes" }
			"ChartOfCalculationTypesObject"      { "ChartOfCalculationTypes" }
			"ExchangePlanObject"                 { "ExchangePlan" }
			"BusinessProcessObject"              { "BusinessProcess" }
			"TaskObject"                         { "Task" }
		}
		if (-not $typeMap.ContainsKey($metaType)) { $typeMap[$metaType] = @{} }
		$typeMap[$metaType][$name] = $true
	}

	# RecordSet types: cfg:InformationRegisterRecordSet.XXX etc.
	$rsPattern = '(?:cfg:|d\dp1:)(InformationRegisterRecordSet|AccumulationRegisterRecordSet|AccountingRegisterRecordSet|CalculationRegisterRecordSet)\.([A-Za-z\u0400-\u04FF\d_]+)'
	foreach ($m in [regex]::Matches($content, $rsPattern)) {
		$prefix = $m.Groups[1].Value
		$name   = $m.Groups[2].Value
		$metaType = switch ($prefix) {
			"InformationRegisterRecordSet"   { "InformationRegister" }
			"AccumulationRegisterRecordSet"  { "AccumulationRegister" }
			"AccountingRegisterRecordSet"    { "AccountingRegister" }
			"CalculationRegisterRecordSet"   { "CalculationRegister" }
		}
		if (-not $typeMap.ContainsKey($metaType)) { $typeMap[$metaType] = @{} }
		$typeMap[$metaType][$name] = $true
	}

	# Characteristic TypeSet: cfg:Characteristic.XXX
	$charPattern = 'cfg:Characteristic\.([A-Za-z\u0400-\u04FF\d_]+)'
	foreach ($m in [regex]::Matches($content, $charPattern)) {
		$name = $m.Groups[1].Value
		if (-not $typeMap.ContainsKey("ChartOfCharacteristicTypes")) { $typeMap["ChartOfCharacteristicTypes"] = @{} }
		$typeMap["ChartOfCharacteristicTypes"][$name] = $true
	}

	# DefinedType TypeSet: cfg:DefinedType.XXX
	$dtPattern = 'cfg:DefinedType\.([A-Za-z\u0400-\u04FF\d_]+)'
	foreach ($m in [regex]::Matches($content, $dtPattern)) {
		$name = $m.Groups[1].Value
		if (-not $typeMap.ContainsKey("DefinedType")) { $typeMap["DefinedType"] = @{} }
		$typeMap["DefinedType"][$name] = $true
	}
}

# --- 1b. Scan Form.xml for register record set columns ---
# When a form attribute has type like InformationRegisterRecordSet.XXX,
# the form references columns via DataPath "AttrName.ColumnName".
# We need to create matching dimensions/resources/attributes in stub registers.

$registerColumns = @{}  # "RegisterType.RegisterName" -> @{ col1=$true; col2=$true }

# Standard attributes that don't need explicit declaration
$stdRegCols = @("LineNumber","Period","Recorder","Active","RecordType")

foreach ($f in $xmlFiles) {
	$content = [System.IO.File]::ReadAllText($f.FullName, [System.Text.Encoding]::UTF8)

	# Find form attributes with register record set types using XmlDocument for reliability
	$regAttrMap = @{}  # formAttrName -> "RegisterType.RegisterName"

	# Only process Form.xml files (they contain <Attributes> with <Attribute> children)
	if ($f.Name -eq "Form.xml" -and $content -match '<Attributes>') {
		try {
			$xml = New-Object System.Xml.XmlDocument
			$xml.LoadXml($content)
			$nsMgr = New-Object System.Xml.XmlNamespaceManager($xml.NameTable)
			$nsMgr.AddNamespace("v8", "http://v8.1c.ru/8.1/data/core")
			$nsMgr.AddNamespace("f", "http://v8.1c.ru/8.3/xcf/logform")
			$attrNodes = $xml.SelectNodes("//f:Attributes/f:Attribute", $nsMgr)
			foreach ($attrNode in $attrNodes) {
				$attrName = $attrNode.GetAttribute("name")
				$typeNodes = $attrNode.SelectNodes("f:Type/v8:Type", $nsMgr)
				foreach ($tn in $typeNodes) {
					$typeText = $tn.InnerText
					$rsMatch = [regex]::Match($typeText, '^(?:cfg:|d\dp1:)(InformationRegisterRecordSet|AccumulationRegisterRecordSet|AccountingRegisterRecordSet|CalculationRegisterRecordSet)\.(.+)$')
					if ($rsMatch.Success) {
						$rsPrefix = $rsMatch.Groups[1].Value
						$regName = $rsMatch.Groups[2].Value
						$regType = switch ($rsPrefix) {
							"InformationRegisterRecordSet"   { "InformationRegister" }
							"AccumulationRegisterRecordSet"  { "AccumulationRegister" }
							"AccountingRegisterRecordSet"    { "AccountingRegister" }
							"CalculationRegisterRecordSet"   { "CalculationRegister" }
						}
						$regKey = "$regType.$regName"
						$regAttrMap[$attrName] = $regKey
						if (-not $registerColumns.ContainsKey($regKey)) {
							$registerColumns[$regKey] = @{}
						}
					}
				}
			}
		} catch {
			# XML parse failed, skip
		}
	}

	# Now find DataPath references like "AttrName.ColumnName"
	if ($regAttrMap.Count -gt 0) {
		$dpPattern = '<DataPath>([A-Za-z\u0400-\u04FF\d_]+)\.([A-Za-z\u0400-\u04FF\d_]+)</DataPath>'
		foreach ($m in [regex]::Matches($content, $dpPattern)) {
			$attrName = $m.Groups[1].Value
			$colName = $m.Groups[2].Value
			if ($regAttrMap.ContainsKey($attrName) -and $colName -notin $stdRegCols) {
				$regKey = $regAttrMap[$attrName]
				$registerColumns[$regKey][$colName] = $true
			}
		}
	}
}

$hasRefTypes = $typeMap.Count -gt 0
# Конфигурация нужна и тогда, когда ссылочных типов нет: в неё кладётся сам объект.
$embedRequested = -not [string]::IsNullOrWhiteSpace($EmbedSourceFile)
$needCfg = $hasRefTypes -or $embedRequested

# --- 2. Determine TempBasePath ---
if (-not $TempBasePath) {
	$TempBasePath = Join-Path $env:TEMP "epf_stub_db_$(Get-Random)"
}

# --- 3. If registers need a registrator, add stub document ---
$registratorTypes = @("AccumulationRegister","AccountingRegister","CalculationRegister")
$needsRegistrator = $false
foreach ($rt in $registratorTypes) {
	if ($typeMap.ContainsKey($rt) -and $typeMap[$rt].Count -gt 0) {
		$needsRegistrator = $true
		break
	}
}
if ($needsRegistrator) {
	if (-not $typeMap.ContainsKey("Document")) { $typeMap["Document"] = @{} }
	$typeMap["Document"]["ЗаглушкаРегистратора"] = $true
}

# --- Внедрение проверяемого объекта в конфигурацию-заглушку ---
# Платформа не умеет проверять внешнюю обработку: /LoadExternalDataProcessorOrReportFromFiles
# только упаковывает XML и модули не компилирует. Зато она проверяет объект КОНФИГУРАЦИИ, а
# внешняя обработка отличается от него немногим (замер 8.3.24): корневым тегом, именем
# порождаемого объектного типа и отсутствием типа менеджера. Правим ровно эти точки и переносим
# остальное как есть — под проверку попадает всё, что написал автор, включая реквизиты, формы и
# макеты, а формат может расти без правок здесь.
#
# Подстановка типа делается ТОЛЬКО в .xml (это DefaultForm и основной реквизит формы); в .bsl
# такой же текст был бы кодом, и трогать его нельзя.
function Add-SourceObjectToConfig {
	param([string]$SourceXml, [string]$CfgDir)

	# Копия объекта живёт в конфигурации базы, а следом в ту же базу грузится исходник как ВНЕШНЯЯ
	# обработка. С одинаковыми идентификаторами платформа путает их и через раз отвечает «Исключение
	# XDTO при чтении файла» на исправном исходнике — поэтому у копии все GUID свои, но согласованные
	# между её файлами (ссылки внутри объекта идут по идентификатору).
	$guidMap = @{}
	$reGuid = [regex]'[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}'
	$reissue = {
		param($m)
		$k = $m.Value.ToLower()
		if (-not $guidMap.ContainsKey($k)) { $guidMap[$k] = [guid]::NewGuid().ToString() }
		$guidMap[$k]
	}

	$text = [IO.File]::ReadAllText($SourceXml, [Text.Encoding]::UTF8)
	if ($text -match '<ExternalDataProcessor[\s>]') {
		$extTag = 'ExternalDataProcessor'; $cfgTag = 'DataProcessor'; $folder = 'DataProcessors'
	} elseif ($text -match '<ExternalReport[\s>]') {
		$extTag = 'ExternalReport'; $cfgTag = 'Report'; $folder = 'Reports'
	} else {
		return $null
	}

	$name = if ($text -match '<Name>([^<]+)</Name>') { $Matches[1] } else { [IO.Path]::GetFileNameWithoutExtension($SourceXml) }

	$conv = $reGuid.Replace($text, $reissue)
	$conv = $conv.Replace("<$extTag ", "<$cfgTag ").Replace("<$extTag>", "<$cfgTag>").Replace("</$extTag>", "</$cfgTag>")
	$conv = $conv.Replace("${extTag}Object.", "${cfgTag}Object.").Replace("$extTag.", "$cfgTag.")

	# Тип менеджера у внешней обработки не объявлен, а объекту конфигурации он обязателен:
	# без него платформа отвечает «отсутствует один или более типов объекта».
	$mgr = "`t`t`t<xr:GeneratedType name=`"${cfgTag}Manager.$name`" category=`"Manager`">`r`n" +
	       "`t`t`t`t<xr:TypeId>$([guid]::NewGuid().ToString())</xr:TypeId>`r`n" +
	       "`t`t`t`t<xr:ValueId>$([guid]::NewGuid().ToString())</xr:ValueId>`r`n" +
	       "`t`t`t</xr:GeneratedType>`r`n"
	if ($conv -match '</InternalInfo>') {
		$conv = [regex]::Replace($conv, '(\s*)</InternalInfo>', ("`r`n" + $mgr + "`t`t</InternalInfo>"), 1)
	} else {
		$objType = "`t`t`t<xr:GeneratedType name=`"${cfgTag}Object.$name`" category=`"Object`">`r`n" +
		           "`t`t`t`t<xr:TypeId>$([guid]::NewGuid().ToString())</xr:TypeId>`r`n" +
		           "`t`t`t`t<xr:ValueId>$([guid]::NewGuid().ToString())</xr:ValueId>`r`n" +
		           "`t`t`t</xr:GeneratedType>`r`n"
		$conv = [regex]::Replace($conv, "(<$cfgTag[^>]*>)", ("`$1`r`n`t`t<InternalInfo>`r`n" + $objType + $mgr + "`t`t</InternalInfo>"), 1)
	}

	$objDir = Join-Path $CfgDir $folder
	New-Item -ItemType Directory -Path $objDir -Force | Out-Null
	$encBom = New-Object System.Text.UTF8Encoding($true)
	[IO.File]::WriteAllText((Join-Path $objDir "$name.xml"), $conv, $encBom)

	# Содержимое объекта — как есть; в XML та же подстановка типа, .bsl копируются байт в байт.
	$srcContent = Join-Path (Split-Path $SourceXml -Parent) $name
	if (Test-Path $srcContent) {
		# Длину префикса берём у РАЗРЕШЁННОГО пути, а не у переданной строки: путь может
		# прийти с коротким именем (C:\Users\NSHIRO~1\…), а Get-ChildItem отдаёт полное — тогда
		# отрезание по длине исходной строки оставляет в относительном пути чужие символы.
		$srcRoot = (Get-Item -LiteralPath $srcContent).FullName.TrimEnd('\', '/')
		$dstContent = Join-Path $objDir $name
		foreach ($f in (Get-ChildItem -LiteralPath $srcRoot -Recurse -File)) {
			$rel = $f.FullName.Substring($srcRoot.Length).TrimStart('\', '/')
			$dst = Join-Path $dstContent $rel
			New-Item -ItemType Directory -Path (Split-Path $dst -Parent) -Force | Out-Null
			if ($f.Extension -ieq '.xml') {
				$t = [IO.File]::ReadAllText($f.FullName, [Text.Encoding]::UTF8)
				$t = $reGuid.Replace($t, $reissue)
				$t = $t.Replace("${extTag}Object.", "${cfgTag}Object.").Replace("$extTag.", "$cfgTag.")
				[IO.File]::WriteAllText($dst, $t, $encBom)
			} else {
				Copy-Item -Path $f.FullName -Destination $dst -Force
			}
		}
	}

	return @{ Tag = $cfgTag; Name = $name }
}

# --- 4. Generate configuration XML ---

if ($needCfg) {
	$enc = New-Object System.Text.UTF8Encoding($true)
	$cfgDir = Join-Path $TempBasePath "cfg"
	New-Item -ItemType Directory -Path $cfgDir -Force | Out-Null

	$embedded = $null
	if ($embedRequested) {
		$embedded = Add-SourceObjectToConfig $EmbedSourceFile $cfgDir
		if (-not $embedded) {
			Write-Host "Error: $EmbedSourceFile is neither ExternalDataProcessor nor ExternalReport" -ForegroundColor Red
			exit 1
		}
	}

	# Заглушке нужна САМАЯ НИЗКАЯ работающая версия, а не версия исходников: ограничение платформы
	# одностороннее — она читает формат не новее себя. Отсюда min(версия исходников, 2.17): на 2.17+
	# заглушка остаётся 2.17 (как было), а под исходники 2.13-2.16 опускается до их версии, иначе
	# конфигурация не загрузится платформой, которая эти исходники и выгрузила («Неизвестная версия
	# формата 2.17 загружаемого файла», замерено на 8.3.20).
	#
	$srcVersion = if ($srcRootVersion) { $srcRootVersion } elseif ($srcAnyVersion) { $srcAnyVersion } else { "2.17" }
	$srcRank = Get-FormatRank $srcVersion
	$stubFormatVersion = if ($srcRank -gt 0 -and $srcRank -lt (Get-FormatRank "2.17")) { $srcVersion } else { "2.17" }
	# Режим совместимости заглушки — по той же логике. Платформа отказывается работать с
	# конфигурацией, чей режим выше её самой («Для работы с конфигурацией необходима версия
	# платформы не меньше, чем 8.3.24»), и тогда объекты заглушки в базу не попадают: загрузка
	# рапортует успех, а сборка падает на «Неизвестное имя типа». Ступени — лестница версий
	# формата из docs/1c-configuration-spec.md.
	$compatByFormat = @{ "2.13" = "Version8_3_20"; "2.14" = "Version8_3_21"; "2.15" = "Version8_3_22"; "2.16" = "Version8_3_23" }
	$stubCompatMode = if ($compatByFormat.ContainsKey($stubFormatVersion)) { $compatByFormat[$stubFormatVersion] } else { "Version8_3_24" }

	$ns = 'xmlns="http://v8.1c.ru/8.3/MDClasses" xmlns:app="http://v8.1c.ru/8.2/managed-application/core" xmlns:cfg="http://v8.1c.ru/8.1/data/enterprise/current-config" xmlns:cmi="http://v8.1c.ru/8.2/managed-application/cmi" xmlns:ent="http://v8.1c.ru/8.1/data/enterprise" xmlns:lf="http://v8.1c.ru/8.2/managed-application/logform" xmlns:style="http://v8.1c.ru/8.1/data/ui/style" xmlns:sys="http://v8.1c.ru/8.1/data/ui/fonts/system" xmlns:v8="http://v8.1c.ru/8.1/data/core" xmlns:v8ui="http://v8.1c.ru/8.1/data/ui" xmlns:web="http://v8.1c.ru/8.1/data/ui/colors/web" xmlns:win="http://v8.1c.ru/8.1/data/ui/colors/windows" xmlns:xen="http://v8.1c.ru/8.3/xcf/enums" xmlns:xpr="http://v8.1c.ru/8.3/xcf/predef" xmlns:xr="http://v8.1c.ru/8.3/xcf/readable" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="' + $stubFormatVersion + '"'

	# GeneratedType definitions per metadata type
	$gtDefs = @{
		"Catalog" = @(
			@{p="CatalogObject";c="Object"},@{p="CatalogRef";c="Ref"},@{p="CatalogSelection";c="Selection"},
			@{p="CatalogList";c="List"},@{p="CatalogManager";c="Manager"}
		)
		"Document" = @(
			@{p="DocumentObject";c="Object"},@{p="DocumentRef";c="Ref"},@{p="DocumentSelection";c="Selection"},
			@{p="DocumentList";c="List"},@{p="DocumentManager";c="Manager"}
		)
		"Enum" = @(
			@{p="EnumRef";c="Ref"},@{p="EnumManager";c="Manager"},@{p="EnumList";c="List"}
		)
		"ChartOfAccounts" = @(
			@{p="ChartOfAccountsObject";c="Object"},@{p="ChartOfAccountsRef";c="Ref"},@{p="ChartOfAccountsSelection";c="Selection"},
			@{p="ChartOfAccountsList";c="List"},@{p="ChartOfAccountsManager";c="Manager"}
		)
		"ChartOfCharacteristicTypes" = @(
			@{p="ChartOfCharacteristicTypesObject";c="Object"},@{p="ChartOfCharacteristicTypesRef";c="Ref"},@{p="ChartOfCharacteristicTypesSelection";c="Selection"},
			@{p="ChartOfCharacteristicTypesList";c="List"},@{p="Characteristic";c="Characteristic"},@{p="ChartOfCharacteristicTypesManager";c="Manager"}
		)
		"ChartOfCalculationTypes" = @(
			@{p="ChartOfCalculationTypesObject";c="Object"},@{p="ChartOfCalculationTypesRef";c="Ref"},@{p="ChartOfCalculationTypesSelection";c="Selection"},
			@{p="ChartOfCalculationTypesList";c="List"},@{p="ChartOfCalculationTypesManager";c="Manager"}
		)
		"ExchangePlan" = @(
			@{p="ExchangePlanObject";c="Object"},@{p="ExchangePlanRef";c="Ref"},@{p="ExchangePlanSelection";c="Selection"},
			@{p="ExchangePlanList";c="List"},@{p="ExchangePlanManager";c="Manager"}
		)
		"BusinessProcess" = @(
			@{p="BusinessProcessObject";c="Object"},@{p="BusinessProcessRef";c="Ref"},@{p="BusinessProcessSelection";c="Selection"},
			@{p="BusinessProcessList";c="List"},@{p="BusinessProcessManager";c="Manager"}
		)
		"Task" = @(
			@{p="TaskObject";c="Object"},@{p="TaskRef";c="Ref"},@{p="TaskSelection";c="Selection"},
			@{p="TaskList";c="List"},@{p="TaskManager";c="Manager"}
		)
		"InformationRegister" = @(
			@{p="InformationRegisterRecord";c="Record"},@{p="InformationRegisterManager";c="Manager"},
			@{p="InformationRegisterSelection";c="Selection"},@{p="InformationRegisterList";c="List"},
			@{p="InformationRegisterRecordSet";c="RecordSet"},@{p="InformationRegisterRecordKey";c="RecordKey"},
			@{p="InformationRegisterRecordManager";c="RecordManager"}
		)
		"AccumulationRegister" = @(
			@{p="AccumulationRegisterRecord";c="Record"},@{p="AccumulationRegisterManager";c="Manager"},
			@{p="AccumulationRegisterSelection";c="Selection"},@{p="AccumulationRegisterList";c="List"},
			@{p="AccumulationRegisterRecordSet";c="RecordSet"},@{p="AccumulationRegisterRecordKey";c="RecordKey"}
		)
		"AccountingRegister" = @(
			@{p="AccountingRegisterRecord";c="Record"},@{p="AccountingRegisterManager";c="Manager"},
			@{p="AccountingRegisterSelection";c="Selection"},@{p="AccountingRegisterExtDimensions";c="ExtDimensions"},
			@{p="AccountingRegisterList";c="List"},@{p="AccountingRegisterRecordSet";c="RecordSet"},
			@{p="AccountingRegisterRecordKey";c="RecordKey"}
		)
		"CalculationRegister" = @(
			@{p="CalculationRegisterRecord";c="Record"},@{p="CalculationRegisterManager";c="Manager"},
			@{p="CalculationRegisterSelection";c="Selection"},@{p="CalculationRegisterList";c="List"},
			@{p="CalculationRegisterRecordSet";c="RecordSet"},@{p="CalculationRegisterRecordKey";c="RecordKey"}
		)
		"DefinedType" = @(
			@{p="DefinedType";c="DefinedType"}
		)
	}

	# Metadata type -> XML tag and directory
	$metaInfo = @{
		"Catalog"                      = @{tag="Catalog";dir="Catalogs"}
		"Document"                     = @{tag="Document";dir="Documents"}
		"Enum"                         = @{tag="Enum";dir="Enums"}
		"ChartOfAccounts"              = @{tag="ChartOfAccounts";dir="ChartsOfAccounts"}
		"ChartOfCharacteristicTypes"   = @{tag="ChartOfCharacteristicTypes";dir="ChartsOfCharacteristicTypes"}
		"ChartOfCalculationTypes"      = @{tag="ChartOfCalculationTypes";dir="ChartsOfCalculationTypes"}
		"ExchangePlan"                 = @{tag="ExchangePlan";dir="ExchangePlans"}
		"BusinessProcess"              = @{tag="BusinessProcess";dir="BusinessProcesses"}
		"Task"                         = @{tag="Task";dir="Tasks"}
		"InformationRegister"          = @{tag="InformationRegister";dir="InformationRegisters"}
		"AccumulationRegister"         = @{tag="AccumulationRegister";dir="AccumulationRegisters"}
		"AccountingRegister"           = @{tag="AccountingRegister";dir="AccountingRegisters"}
		"CalculationRegister"          = @{tag="CalculationRegister";dir="CalculationRegisters"}
		"DefinedType"                  = @{tag="DefinedType";dir="DefinedTypes"}
	}

	# StandardAttribute boilerplate
	$stdAttrXml = @'
				<xr:LinkByType/>
				<xr:FillChecking>DontCheck</xr:FillChecking>
				<xr:MultiLine>false</xr:MultiLine>
				<xr:FillFromFillingValue>false</xr:FillFromFillingValue>
				<xr:CreateOnInput>Auto</xr:CreateOnInput>
				<xr:MaxValue xsi:nil="true"/>
				<xr:ToolTip/>
				<xr:ExtendedEdit>false</xr:ExtendedEdit>
				<xr:Format/>
				<xr:ChoiceForm/>
				<xr:QuickChoice>Auto</xr:QuickChoice>
				<xr:ChoiceHistoryOnInput>Auto</xr:ChoiceHistoryOnInput>
				<xr:EditFormat/>
				<xr:PasswordMode>false</xr:PasswordMode>
				<xr:DataHistory>Use</xr:DataHistory>
				<xr:MarkNegatives>false</xr:MarkNegatives>
				<xr:MinValue xsi:nil="true"/>
				<xr:Synonym/>
				<xr:Comment/>
				<xr:FullTextSearch>Use</xr:FullTextSearch>
				<xr:ChoiceParameterLinks/>
				<xr:FillValue xsi:nil="true"/>
				<xr:Mask/>
				<xr:ChoiceParameters/>
'@

	$stdAttrsByType = @{
		"Catalog" = @("PredefinedDataName","Predefined","Ref","DeletionMark","IsFolder","Owner","Parent","Description","Code")
		"Document" = @("Posted","Ref","DeletionMark","Date","Number")
		"Enum" = @("Order","Ref")
		"ChartOfAccounts" = @("PredefinedDataName","Predefined","Ref","DeletionMark","Description","Code","Parent","Order","Type","OffBalance")
		"ChartOfCharacteristicTypes" = @("PredefinedDataName","Predefined","Ref","DeletionMark","Description","Code","Parent","ValueType")
		"ChartOfCalculationTypes" = @("PredefinedDataName","Predefined","Ref","DeletionMark","Description","Code","ActionPeriodIsBasic")
		"ExchangePlan" = @("Ref","DeletionMark","Code","Description","ThisNode","SentNo","ReceivedNo")
		"BusinessProcess" = @("Ref","DeletionMark","Date","Number","Started","Completed","HeadTask")
		"Task" = @("Ref","DeletionMark","Date","Number","Executed","Description","RoutePoint","BusinessProcess")
		"InformationRegister" = @("Active","LineNumber","Recorder","Period")
		"AccumulationRegister" = @("Active","LineNumber","Recorder","Period")
		"AccountingRegister" = @("Active","Period","Recorder","LineNumber","Account")
		"CalculationRegister" = @("Active","Recorder","LineNumber","RegistrationPeriod","CalculationType","ReversingEntry")
	}

	function Build-StdAttrs([string]$metaType) {
		$attrs = $stdAttrsByType[$metaType]
		if (-not $attrs) { return "" }
		$sb = New-Object System.Text.StringBuilder
		$sb.AppendLine("`t`t`t<StandardAttributes>") | Out-Null
		foreach ($a in $attrs) {
			$sb.AppendLine("`t`t`t`t<xr:StandardAttribute name=`"$a`">") | Out-Null
			$sb.AppendLine($stdAttrXml) | Out-Null
			$sb.AppendLine("`t`t`t`t</xr:StandardAttribute>") | Out-Null
		}
		$sb.AppendLine("`t`t`t</StandardAttributes>") | Out-Null
		return $sb.ToString()
	}

	# --- 4a. Configuration.xml ---
	$uuidCfg = [guid]::NewGuid().ToString()
	$uuidLang = [guid]::NewGuid().ToString()

	$coIds = @()
	for ($i = 0; $i -lt 7; $i++) { $coIds += [guid]::NewGuid().ToString() }
	$classIds = @(
		"9cd510cd-abfc-11d4-9434-004095e12fc7",
		"9fcd25a0-4822-11d4-9414-008048da11f9",
		"e3687481-0a87-462c-a166-9f34594f9bba",
		"9de14907-ec23-4a07-96f0-85521cb6b53b",
		"51f2d5d8-ea4d-4064-8892-82951750031e",
		"e68182ea-4237-4383-967f-90c1e3370bc7",
		"fb282519-d103-4dd3-bc12-cb271d631dfc"
	)

	$coXml = ""
	for ($i = 0; $i -lt 7; $i++) {
		$coXml += "`r`n`t`t`t<xr:ContainedObject>`r`n`t`t`t`t<xr:ClassId>$($classIds[$i])</xr:ClassId>`r`n`t`t`t`t<xr:ObjectId>$($coIds[$i])</xr:ObjectId>`r`n`t`t`t</xr:ContainedObject>"
	}

	# ChildObjects entries
	$childXml = "`r`n`t`t`t<Language>Русский</Language>"
	foreach ($metaType in $typeMap.Keys) {
		if (-not $metaInfo.ContainsKey($metaType)) { continue }
		$tag = $metaInfo[$metaType].tag
		foreach ($name in $typeMap[$metaType].Keys) {
			$childXml += "`r`n`t`t`t<$tag>$name</$tag>"
		}
	}
	if ($embedded) { $childXml += "`r`n`t`t`t<$($embedded.Tag)>$($embedded.Name)</$($embedded.Tag)>" }

	$cfgXml = @"
<?xml version="1.0" encoding="UTF-8"?>
<MetaDataObject $ns>
	<Configuration uuid="$uuidCfg">
		<InternalInfo>$coXml
		</InternalInfo>
		<Properties>
			<Name>StubConfig</Name>
			<Synonym/>
			<Comment/>
			<NamePrefix/>
			<ConfigurationExtensionCompatibilityMode>$stubCompatMode</ConfigurationExtensionCompatibilityMode>
			<DefaultRunMode>ManagedApplication</DefaultRunMode>
			<UsePurposes>
				<v8:Value xsi:type="app:ApplicationUsePurpose">PlatformApplication</v8:Value>
			</UsePurposes>
			<ScriptVariant>Russian</ScriptVariant>
			<DefaultRoles/>
			<Vendor/>
			<Version/>
			<UpdateCatalogAddress/>
			<IncludeHelpInContents>false</IncludeHelpInContents>
			<UseManagedFormInOrdinaryApplication>false</UseManagedFormInOrdinaryApplication>
			<UseOrdinaryFormInManagedApplication>false</UseOrdinaryFormInManagedApplication>
			<AdditionalFullTextSearchDictionaries/>
			<CommonSettingsStorage/>
			<ReportsUserSettingsStorage/>
			<ReportsVariantsStorage/>
			<FormDataSettingsStorage/>
			<DynamicListsUserSettingsStorage/>
			<URLExternalDataStorage/>
			<Content/>
			<DefaultReportForm/>
			<DefaultReportVariantForm/>
			<DefaultReportSettingsForm/>
			<DefaultReportAppearanceTemplate/>
			<DefaultDynamicListSettingsForm/>
			<DefaultSearchForm/>
			<DefaultDataHistoryChangeHistoryForm/>
			<DefaultDataHistoryVersionDataForm/>
			<DefaultDataHistoryVersionDifferencesForm/>
			<DefaultCollaborationSystemUsersChoiceForm/>
			<RequiredMobileApplicationPermissions/>
			<UsedMobileApplicationFunctionalities/>
			<StandaloneConfigurationRestrictionRoles/>
			<MobileApplicationURLs/>
			<AllowedIncomingShareRequestTypes/>
			<MainClientApplicationWindowMode>Normal</MainClientApplicationWindowMode>
			<DefaultInterface/>
			<DefaultStyle/>
			<DefaultLanguage>Language.Русский</DefaultLanguage>
			<BriefInformation/>
			<DetailedInformation/>
			<Copyright/>
			<VendorInformationAddress/>
			<ConfigurationInformationAddress/>
			<DataLockControlMode>Managed</DataLockControlMode>
			<ObjectAutonumerationMode>NotAutoFree</ObjectAutonumerationMode>
			<ModalityUseMode>DontUse</ModalityUseMode>
			<SynchronousPlatformExtensionAndAddInCallUseMode>DontUse</SynchronousPlatformExtensionAndAddInCallUseMode>
			<InterfaceCompatibilityMode>Taxi</InterfaceCompatibilityMode>
			<DatabaseTablespacesUseMode>DontUse</DatabaseTablespacesUseMode>
			<CompatibilityMode>$stubCompatMode</CompatibilityMode>
			<DefaultConstantsForm/>
		</Properties>
		<ChildObjects>$childXml
		</ChildObjects>
	</Configuration>
</MetaDataObject>
"@

	[System.IO.File]::WriteAllText((Join-Path $cfgDir "Configuration.xml"), $cfgXml, $enc)

	# --- 4b. Language ---
	$langDir = Join-Path $cfgDir "Languages"
	New-Item -ItemType Directory -Path $langDir -Force | Out-Null

	$langXml = @"
<?xml version="1.0" encoding="UTF-8"?>
<MetaDataObject $ns>
	<Language uuid="$uuidLang">
		<Properties>
			<Name>Русский</Name>
			<Synonym>
				<v8:item>
					<v8:lang>ru</v8:lang>
					<v8:content>Русский</v8:content>
				</v8:item>
			</Synonym>
			<Comment/>
			<LanguageCode>ru</LanguageCode>
		</Properties>
	</Language>
</MetaDataObject>
"@
	[System.IO.File]::WriteAllText((Join-Path $langDir "Русский.xml"), $langXml, $enc)

	# --- 4c. Metadata object stubs ---
	foreach ($metaType in $typeMap.Keys) {
		if (-not $metaInfo.ContainsKey($metaType)) { continue }
		$info = $metaInfo[$metaType]
		$objDir = Join-Path $cfgDir $info.dir
		New-Item -ItemType Directory -Path $objDir -Force | Out-Null

		foreach ($objName in $typeMap[$metaType].Keys) {
			$uuid = [guid]::NewGuid().ToString()

			# InternalInfo with GeneratedTypes
			$internalXml = ""
			$gts = $gtDefs[$metaType]
			if ($gts) {
				$internalXml = "`r`n`t`t<InternalInfo>"
				if ($metaType -eq "ExchangePlan") {
					$internalXml += "`r`n`t`t`t<xr:ThisNode>$([guid]::NewGuid().ToString())</xr:ThisNode>"
				}
				foreach ($gt in $gts) {
					$fullName = "$($gt.p).$objName"
					$tid = [guid]::NewGuid().ToString()
					$vid = [guid]::NewGuid().ToString()
					$internalXml += "`r`n`t`t`t<xr:GeneratedType name=`"$fullName`" category=`"$($gt.c)`">"
					$internalXml += "`r`n`t`t`t`t<xr:TypeId>$tid</xr:TypeId>"
					$internalXml += "`r`n`t`t`t`t<xr:ValueId>$vid</xr:ValueId>"
					$internalXml += "`r`n`t`t`t</xr:GeneratedType>"
				}
				$internalXml += "`r`n`t`t</InternalInfo>"
			}

			# Properties + ChildObjects depending on type
			$propsXml = ""
			$childObjXml = ""

			switch ($metaType) {
				"Catalog" {
					$stdAttrs = Build-StdAttrs "Catalog"
					$propsXml = @"
			<Name>$objName</Name>
			<Synonym/>
			<Comment/>
			<Hierarchical>false</Hierarchical>
			<HierarchyType>HierarchyFoldersAndItems</HierarchyType>
			<LimitLevelCount>false</LimitLevelCount>
			<LevelCount>2</LevelCount>
			<FoldersOnTop>true</FoldersOnTop>
			<UseStandardCommands>false</UseStandardCommands>
			<Owners/>
			<SubordinationUse>ToItems</SubordinationUse>
			<CodeLength>9</CodeLength>
			<DescriptionLength>25</DescriptionLength>
			<CodeType>String</CodeType>
			<CodeAllowedLength>Variable</CodeAllowedLength>
			<CodeSeries>WholeCatalog</CodeSeries>
			<CheckUnique>false</CheckUnique>
			<Autonumbering>true</Autonumbering>
			<DefaultPresentation>AsDescription</DefaultPresentation>
$stdAttrs			<Characteristics/>
			<PredefinedDataUpdate>Auto</PredefinedDataUpdate>
			<EditType>InDialog</EditType>
			<QuickChoice>true</QuickChoice>
			<ChoiceMode>BothWays</ChoiceMode>
			<InputByString/>
			<SearchStringModeOnInputByString>Begin</SearchStringModeOnInputByString>
			<FullTextSearchOnInputByString>DontUse</FullTextSearchOnInputByString>
			<ChoiceDataGetModeOnInputByString>Directly</ChoiceDataGetModeOnInputByString>
			<DefaultObjectForm/>
			<DefaultFolderForm/>
			<DefaultListForm/>
			<DefaultChoiceForm/>
			<DefaultFolderChoiceForm/>
			<AuxiliaryObjectForm/>
			<AuxiliaryFolderForm/>
			<AuxiliaryListForm/>
			<AuxiliaryChoiceForm/>
			<AuxiliaryFolderChoiceForm/>
			<IncludeHelpInContents>false</IncludeHelpInContents>
			<BasedOn/>
			<DataLockFields/>
			<DataLockControlMode>Automatic</DataLockControlMode>
			<FullTextSearch>Use</FullTextSearch>
			<ObjectPresentation/>
			<ExtendedObjectPresentation/>
			<ListPresentation/>
			<ExtendedListPresentation/>
			<Explanation/>
			<CreateOnInput>DontUse</CreateOnInput>
			<ChoiceHistoryOnInput>Auto</ChoiceHistoryOnInput>
			<DataHistory>DontUse</DataHistory>
			<UpdateDataHistoryImmediatelyAfterWrite>false</UpdateDataHistoryImmediatelyAfterWrite>
			<ExecuteAfterWriteDataHistoryVersionProcessing>false</ExecuteAfterWriteDataHistoryVersionProcessing>
"@
				}
				"Document" {
					$stdAttrs = Build-StdAttrs "Document"
					$regRecordsXml = "<RegisterRecords/>"
					# If this is the stub registrator, set register records
					if ($objName -eq "ЗаглушкаРегистратора") {
						$rrLines = @()
						foreach ($rt in $registratorTypes) {
							if ($typeMap.ContainsKey($rt) -and $typeMap[$rt].Count -gt 0) {
								foreach ($rn in $typeMap[$rt].Keys) {
									$rrLines += "`t`t`t`t<xr:Item xsi:type=`"xr:MDObjectRef`">$rt.$rn</xr:Item>"
								}
							}
						}
						if ($rrLines.Count -gt 0) {
							$regRecordsXml = "<RegisterRecords>`r`n$($rrLines -join "`r`n")`r`n`t`t`t</RegisterRecords>"
						}
					}
					$propsXml = @"
			<Name>$objName</Name>
			<Synonym/>
			<Comment/>
			<UseStandardCommands>false</UseStandardCommands>
			<Numerator/>
			<NumberType>String</NumberType>
			<NumberLength>11</NumberLength>
			<NumberAllowedLength>Variable</NumberAllowedLength>
			<NumberPeriodicity>Year</NumberPeriodicity>
			<CheckUnique>false</CheckUnique>
			<Autonumbering>true</Autonumbering>
$stdAttrs			<Characteristics/>
			<BasedOn/>
			<InputByString/>
			<CreateOnInput>DontUse</CreateOnInput>
			<SearchStringModeOnInputByString>Begin</SearchStringModeOnInputByString>
			<FullTextSearchOnInputByString>DontUse</FullTextSearchOnInputByString>
			<ChoiceDataGetModeOnInputByString>Directly</ChoiceDataGetModeOnInputByString>
			<DefaultObjectForm/>
			<DefaultListForm/>
			<DefaultChoiceForm/>
			<AuxiliaryObjectForm/>
			<AuxiliaryListForm/>
			<AuxiliaryChoiceForm/>
			<Posting>Allow</Posting>
			<RealTimePosting>Deny</RealTimePosting>
			<RegisterRecordsDeletion>AutoDelete</RegisterRecordsDeletion>
			<RegisterRecordsWritingOnPost>WriteModified</RegisterRecordsWritingOnPost>
			<SequenceFilling>AutoFill</SequenceFilling>
			$regRecordsXml
			<PostInPrivilegedMode>true</PostInPrivilegedMode>
			<UnpostInPrivilegedMode>true</UnpostInPrivilegedMode>
			<IncludeHelpInContents>false</IncludeHelpInContents>
			<DataLockFields/>
			<DataLockControlMode>Automatic</DataLockControlMode>
			<FullTextSearch>Use</FullTextSearch>
			<ObjectPresentation/>
			<ExtendedObjectPresentation/>
			<ListPresentation/>
			<ExtendedListPresentation/>
			<Explanation/>
			<ChoiceHistoryOnInput>Auto</ChoiceHistoryOnInput>
			<DataHistory>DontUse</DataHistory>
			<UpdateDataHistoryImmediatelyAfterWrite>false</UpdateDataHistoryImmediatelyAfterWrite>
			<ExecuteAfterWriteDataHistoryVersionProcessing>false</ExecuteAfterWriteDataHistoryVersionProcessing>
"@
				}
				"Enum" {
					$stdAttrs = Build-StdAttrs "Enum"
					$propsXml = @"
			<Name>$objName</Name>
			<Synonym/>
			<Comment/>
			<UseStandardCommands>false</UseStandardCommands>
$stdAttrs			<Characteristics/>
			<QuickChoice>true</QuickChoice>
			<ChoiceMode>BothWays</ChoiceMode>
			<DefaultListForm/>
			<DefaultChoiceForm/>
			<AuxiliaryListForm/>
			<AuxiliaryChoiceForm/>
			<ListPresentation/>
			<ExtendedListPresentation/>
			<Explanation/>
			<ChoiceHistoryOnInput>Auto</ChoiceHistoryOnInput>
"@
				}
				"InformationRegister" {
					$stdAttrs = Build-StdAttrs "InformationRegister"
					$propsXml = @"
			<Name>$objName</Name>
			<Synonym/>
			<Comment/>
			<UseStandardCommands>false</UseStandardCommands>
			<EditType>InDialog</EditType>
			<DefaultRecordForm/>
			<DefaultListForm/>
			<AuxiliaryRecordForm/>
			<AuxiliaryListForm/>
$stdAttrs			<InformationRegisterPeriodicity>Nonperiodical</InformationRegisterPeriodicity>
			<WriteMode>Independent</WriteMode>
			<MainFilterOnPeriod>false</MainFilterOnPeriod>
			<IncludeHelpInContents>false</IncludeHelpInContents>
			<DataLockControlMode>Automatic</DataLockControlMode>
			<FullTextSearch>Use</FullTextSearch>
			<EnableTotalsSliceFirst>false</EnableTotalsSliceFirst>
			<EnableTotalsSliceLast>false</EnableTotalsSliceLast>
			<RecordPresentation/>
			<ExtendedRecordPresentation/>
			<ListPresentation/>
			<ExtendedListPresentation/>
			<Explanation/>
			<DataHistory>DontUse</DataHistory>
			<UpdateDataHistoryImmediatelyAfterWrite>false</UpdateDataHistoryImmediatelyAfterWrite>
			<ExecuteAfterWriteDataHistoryVersionProcessing>false</ExecuteAfterWriteDataHistoryVersionProcessing>
"@
				}
				"AccumulationRegister" {
					$stdAttrs = Build-StdAttrs "AccumulationRegister"
					$propsXml = @"
			<Name>$objName</Name>
			<Synonym/>
			<Comment/>
			<UseStandardCommands>false</UseStandardCommands>
			<DefaultListForm/>
			<AuxiliaryListForm/>
			<RegisterType>Balance</RegisterType>
			<IncludeHelpInContents>false</IncludeHelpInContents>
$stdAttrs			<DataLockControlMode>Automatic</DataLockControlMode>
			<FullTextSearch>Use</FullTextSearch>
			<EnableTotalsSplitting>true</EnableTotalsSplitting>
			<ListPresentation/>
			<ExtendedListPresentation/>
			<Explanation/>
"@
				}
				"AccountingRegister" {
					$stdAttrs = Build-StdAttrs "AccountingRegister"
					$propsXml = @"
			<Name>$objName</Name>
			<Synonym/>
			<Comment/>
			<UseStandardCommands>false</UseStandardCommands>
			<DefaultListForm/>
			<AuxiliaryListForm/>
			<IncludeHelpInContents>false</IncludeHelpInContents>
			<ChartOfAccounts/>
			<Correspondence>false</Correspondence>
$stdAttrs			<DataLockControlMode>Automatic</DataLockControlMode>
			<FullTextSearch>Use</FullTextSearch>
			<EnableTotalsSplitting>true</EnableTotalsSplitting>
			<ListPresentation/>
			<ExtendedListPresentation/>
			<Explanation/>
"@
				}
				"CalculationRegister" {
					$stdAttrs = Build-StdAttrs "CalculationRegister"
					$propsXml = @"
			<Name>$objName</Name>
			<Synonym/>
			<Comment/>
			<UseStandardCommands>false</UseStandardCommands>
			<DefaultListForm/>
			<AuxiliaryListForm/>
			<IncludeHelpInContents>false</IncludeHelpInContents>
			<ChartOfCalculationTypes/>
$stdAttrs			<DataLockControlMode>Automatic</DataLockControlMode>
			<FullTextSearch>Use</FullTextSearch>
			<ListPresentation/>
			<ExtendedListPresentation/>
			<Explanation/>
"@
				}
				"ChartOfAccounts" {
					$stdAttrs = Build-StdAttrs "ChartOfAccounts"
					$propsXml = @"
			<Name>$objName</Name>
			<Synonym/>
			<Comment/>
			<UseStandardCommands>false</UseStandardCommands>
			<CodeMask/>
			<CodeLength>20</CodeLength>
			<DescriptionLength>100</DescriptionLength>
			<CodeSeries>WholeCatalog</CodeSeries>
			<CheckUnique>false</CheckUnique>
			<Autonumbering>true</Autonumbering>
			<DefaultPresentation>AsDescription</DefaultPresentation>
$stdAttrs			<Characteristics/>
			<PredefinedDataUpdate>Auto</PredefinedDataUpdate>
			<EditType>InDialog</EditType>
			<QuickChoice>true</QuickChoice>
			<ChoiceMode>BothWays</ChoiceMode>
			<InputByString/>
			<SearchStringModeOnInputByString>Begin</SearchStringModeOnInputByString>
			<FullTextSearchOnInputByString>DontUse</FullTextSearchOnInputByString>
			<ChoiceDataGetModeOnInputByString>Directly</ChoiceDataGetModeOnInputByString>
			<DefaultObjectForm/>
			<DefaultListForm/>
			<DefaultChoiceForm/>
			<DefaultFolderForm/>
			<DefaultFolderChoiceForm/>
			<AuxiliaryObjectForm/>
			<AuxiliaryListForm/>
			<AuxiliaryChoiceForm/>
			<AuxiliaryFolderForm/>
			<AuxiliaryFolderChoiceForm/>
			<AutoOrderByCode>true</AutoOrderByCode>
			<OrderLength>5</OrderLength>
			<MaxExtDimensionCount>0</MaxExtDimensionCount>
			<IncludeHelpInContents>false</IncludeHelpInContents>
			<BasedOn/>
			<DataLockFields/>
			<DataLockControlMode>Automatic</DataLockControlMode>
			<FullTextSearch>Use</FullTextSearch>
			<ObjectPresentation/>
			<ExtendedObjectPresentation/>
			<ListPresentation/>
			<ExtendedListPresentation/>
			<Explanation/>
			<CreateOnInput>DontUse</CreateOnInput>
			<ChoiceHistoryOnInput>Auto</ChoiceHistoryOnInput>
			<DataHistory>DontUse</DataHistory>
			<UpdateDataHistoryImmediatelyAfterWrite>false</UpdateDataHistoryImmediatelyAfterWrite>
			<ExecuteAfterWriteDataHistoryVersionProcessing>false</ExecuteAfterWriteDataHistoryVersionProcessing>
"@
				}
				"ChartOfCharacteristicTypes" {
					$stdAttrs = Build-StdAttrs "ChartOfCharacteristicTypes"
					$propsXml = @"
			<Name>$objName</Name>
			<Synonym/>
			<Comment/>
			<UseStandardCommands>false</UseStandardCommands>
			<CodeLength>9</CodeLength>
			<CodeAllowedLength>Variable</CodeAllowedLength>
			<DescriptionLength>25</DescriptionLength>
			<CheckUnique>false</CheckUnique>
			<Autonumbering>true</Autonumbering>
			<DefaultPresentation>AsDescription</DefaultPresentation>
			<CharacteristicExtValues/>
			<Type>
				<v8:Type>xs:boolean</v8:Type>
				<v8:Type>xs:string</v8:Type>
				<v8:StringQualifiers>
					<v8:Length>0</v8:Length>
					<v8:AllowedLength>Variable</v8:AllowedLength>
				</v8:StringQualifiers>
				<v8:Type>xs:decimal</v8:Type>
				<v8:NumberQualifiers>
					<v8:Digits>15</v8:Digits>
					<v8:FractionDigits>2</v8:FractionDigits>
					<v8:AllowedSign>Any</v8:AllowedSign>
				</v8:NumberQualifiers>
				<v8:Type>xs:dateTime</v8:Type>
				<v8:DateQualifiers>
					<v8:DateFractions>DateTime</v8:DateFractions>
				</v8:DateQualifiers>
			</Type>
			<Hierarchical>false</Hierarchical>
			<FoldersOnTop>true</FoldersOnTop>
$stdAttrs			<Characteristics/>
			<PredefinedDataUpdate>Auto</PredefinedDataUpdate>
			<EditType>InDialog</EditType>
			<QuickChoice>true</QuickChoice>
			<ChoiceMode>BothWays</ChoiceMode>
			<InputByString/>
			<SearchStringModeOnInputByString>Begin</SearchStringModeOnInputByString>
			<FullTextSearchOnInputByString>DontUse</FullTextSearchOnInputByString>
			<ChoiceDataGetModeOnInputByString>Directly</ChoiceDataGetModeOnInputByString>
			<DefaultObjectForm/>
			<DefaultFolderForm/>
			<DefaultListForm/>
			<DefaultChoiceForm/>
			<DefaultFolderChoiceForm/>
			<AuxiliaryObjectForm/>
			<AuxiliaryFolderForm/>
			<AuxiliaryListForm/>
			<AuxiliaryChoiceForm/>
			<AuxiliaryFolderChoiceForm/>
			<IncludeHelpInContents>false</IncludeHelpInContents>
			<BasedOn/>
			<DataLockFields/>
			<DataLockControlMode>Automatic</DataLockControlMode>
			<FullTextSearch>Use</FullTextSearch>
			<ObjectPresentation/>
			<ExtendedObjectPresentation/>
			<ListPresentation/>
			<ExtendedListPresentation/>
			<Explanation/>
			<CreateOnInput>DontUse</CreateOnInput>
			<ChoiceHistoryOnInput>Auto</ChoiceHistoryOnInput>
			<DataHistory>DontUse</DataHistory>
			<UpdateDataHistoryImmediatelyAfterWrite>false</UpdateDataHistoryImmediatelyAfterWrite>
			<ExecuteAfterWriteDataHistoryVersionProcessing>false</ExecuteAfterWriteDataHistoryVersionProcessing>
"@
				}
				"ChartOfCalculationTypes" {
					$stdAttrs = Build-StdAttrs "ChartOfCalculationTypes"
					$propsXml = @"
			<Name>$objName</Name>
			<Synonym/>
			<Comment/>
			<UseStandardCommands>false</UseStandardCommands>
			<CodeLength>9</CodeLength>
			<DescriptionLength>25</DescriptionLength>
			<CodeType>String</CodeType>
			<CodeAllowedLength>Variable</CodeAllowedLength>
			<CodeSeries>WholeCatalog</CodeSeries>
			<CheckUnique>false</CheckUnique>
			<Autonumbering>true</Autonumbering>
			<DefaultPresentation>AsDescription</DefaultPresentation>
$stdAttrs			<Characteristics/>
			<PredefinedDataUpdate>Auto</PredefinedDataUpdate>
			<EditType>InDialog</EditType>
			<QuickChoice>true</QuickChoice>
			<ChoiceMode>BothWays</ChoiceMode>
			<InputByString/>
			<SearchStringModeOnInputByString>Begin</SearchStringModeOnInputByString>
			<FullTextSearchOnInputByString>DontUse</FullTextSearchOnInputByString>
			<ChoiceDataGetModeOnInputByString>Directly</ChoiceDataGetModeOnInputByString>
			<DependenceOnCalculationTypes>NotDepend</DependenceOnCalculationTypes>
			<BaseCalculationTypes/>
			<ActionPeriodUse>false</ActionPeriodUse>
			<DefaultObjectForm/>
			<DefaultListForm/>
			<DefaultChoiceForm/>
			<AuxiliaryObjectForm/>
			<AuxiliaryListForm/>
			<AuxiliaryChoiceForm/>
			<IncludeHelpInContents>false</IncludeHelpInContents>
			<BasedOn/>
			<DataLockFields/>
			<DataLockControlMode>Automatic</DataLockControlMode>
			<FullTextSearch>Use</FullTextSearch>
			<ObjectPresentation/>
			<ExtendedObjectPresentation/>
			<ListPresentation/>
			<ExtendedListPresentation/>
			<Explanation/>
			<CreateOnInput>DontUse</CreateOnInput>
			<ChoiceHistoryOnInput>Auto</ChoiceHistoryOnInput>
			<DataHistory>DontUse</DataHistory>
			<UpdateDataHistoryImmediatelyAfterWrite>false</UpdateDataHistoryImmediatelyAfterWrite>
			<ExecuteAfterWriteDataHistoryVersionProcessing>false</ExecuteAfterWriteDataHistoryVersionProcessing>
"@
				}
				"ExchangePlan" {
					$stdAttrs = Build-StdAttrs "ExchangePlan"
					$propsXml = @"
			<Name>$objName</Name>
			<Synonym/>
			<Comment/>
			<UseStandardCommands>false</UseStandardCommands>
			<CodeLength>9</CodeLength>
			<DescriptionLength>25</DescriptionLength>
			<CodeAllowedLength>Variable</CodeAllowedLength>
$stdAttrs			<DefaultPresentation>AsDescription</DefaultPresentation>
			<Characteristics/>
			<PredefinedDataUpdate>Auto</PredefinedDataUpdate>
			<EditType>InDialog</EditType>
			<QuickChoice>true</QuickChoice>
			<ChoiceMode>BothWays</ChoiceMode>
			<InputByString/>
			<SearchStringModeOnInputByString>Begin</SearchStringModeOnInputByString>
			<FullTextSearchOnInputByString>DontUse</FullTextSearchOnInputByString>
			<ChoiceDataGetModeOnInputByString>Directly</ChoiceDataGetModeOnInputByString>
			<DefaultObjectForm/>
			<DefaultListForm/>
			<DefaultChoiceForm/>
			<AuxiliaryObjectForm/>
			<AuxiliaryListForm/>
			<AuxiliaryChoiceForm/>
			<IncludeHelpInContents>false</IncludeHelpInContents>
			<BasedOn/>
			<DataLockFields/>
			<DataLockControlMode>Automatic</DataLockControlMode>
			<FullTextSearch>Use</FullTextSearch>
			<ObjectPresentation/>
			<ExtendedObjectPresentation/>
			<ListPresentation/>
			<ExtendedListPresentation/>
			<Explanation/>
			<CreateOnInput>DontUse</CreateOnInput>
			<ChoiceHistoryOnInput>Auto</ChoiceHistoryOnInput>
			<DistributedInfoBase>false</DistributedInfoBase>
			<DataHistory>DontUse</DataHistory>
			<UpdateDataHistoryImmediatelyAfterWrite>false</UpdateDataHistoryImmediatelyAfterWrite>
			<ExecuteAfterWriteDataHistoryVersionProcessing>false</ExecuteAfterWriteDataHistoryVersionProcessing>
"@
				}
				"BusinessProcess" {
					$stdAttrs = Build-StdAttrs "BusinessProcess"
					$propsXml = @"
			<Name>$objName</Name>
			<Synonym/>
			<Comment/>
			<UseStandardCommands>false</UseStandardCommands>
			<Numerator/>
			<NumberType>String</NumberType>
			<NumberLength>11</NumberLength>
			<NumberAllowedLength>Variable</NumberAllowedLength>
			<NumberPeriodicity>Year</NumberPeriodicity>
			<CheckUnique>false</CheckUnique>
			<Autonumbering>true</Autonumbering>
$stdAttrs			<Characteristics/>
			<Task/>
			<CreateTaskInPrivilegedMode>false</CreateTaskInPrivilegedMode>
			<DefaultObjectForm/>
			<DefaultListForm/>
			<DefaultChoiceForm/>
			<AuxiliaryObjectForm/>
			<AuxiliaryListForm/>
			<AuxiliaryChoiceForm/>
			<IncludeHelpInContents>false</IncludeHelpInContents>
			<BasedOn/>
			<DataLockFields/>
			<DataLockControlMode>Automatic</DataLockControlMode>
			<FullTextSearch>Use</FullTextSearch>
			<ObjectPresentation/>
			<ExtendedObjectPresentation/>
			<ListPresentation/>
			<ExtendedListPresentation/>
			<Explanation/>
			<ChoiceHistoryOnInput>Auto</ChoiceHistoryOnInput>
			<DataHistory>DontUse</DataHistory>
			<UpdateDataHistoryImmediatelyAfterWrite>false</UpdateDataHistoryImmediatelyAfterWrite>
			<ExecuteAfterWriteDataHistoryVersionProcessing>false</ExecuteAfterWriteDataHistoryVersionProcessing>
"@
				}
				"Task" {
					$stdAttrs = Build-StdAttrs "Task"
					$propsXml = @"
			<Name>$objName</Name>
			<Synonym/>
			<Comment/>
			<UseStandardCommands>false</UseStandardCommands>
			<Numerator/>
			<NumberType>String</NumberType>
			<NumberLength>11</NumberLength>
			<NumberAllowedLength>Variable</NumberAllowedLength>
			<NumberPeriodicity>Year</NumberPeriodicity>
			<CheckUnique>false</CheckUnique>
			<Autonumbering>true</Autonumbering>
			<DescriptionLength>25</DescriptionLength>
$stdAttrs			<Characteristics/>
			<InputByString/>
			<SearchStringModeOnInputByString>Begin</SearchStringModeOnInputByString>
			<FullTextSearchOnInputByString>DontUse</FullTextSearchOnInputByString>
			<ChoiceDataGetModeOnInputByString>Directly</ChoiceDataGetModeOnInputByString>
			<DefaultObjectForm/>
			<DefaultListForm/>
			<DefaultChoiceForm/>
			<AuxiliaryObjectForm/>
			<AuxiliaryListForm/>
			<AuxiliaryChoiceForm/>
			<IncludeHelpInContents>false</IncludeHelpInContents>
			<BasedOn/>
			<DataLockFields/>
			<DataLockControlMode>Automatic</DataLockControlMode>
			<FullTextSearch>Use</FullTextSearch>
			<ObjectPresentation/>
			<ExtendedObjectPresentation/>
			<ListPresentation/>
			<ExtendedListPresentation/>
			<Explanation/>
			<Addressing/>
			<MainAddressingAttribute/>
			<CurrentUserAlias/>
			<CurrentUserValue/>
			<ChoiceHistoryOnInput>Auto</ChoiceHistoryOnInput>
			<DataHistory>DontUse</DataHistory>
			<UpdateDataHistoryImmediatelyAfterWrite>false</UpdateDataHistoryImmediatelyAfterWrite>
			<ExecuteAfterWriteDataHistoryVersionProcessing>false</ExecuteAfterWriteDataHistoryVersionProcessing>
"@
				}
				"DefinedType" {
					$propsXml = @"
			<Name>$objName</Name>
			<Synonym/>
			<Comment/>
			<Type>
				<v8:Type>xs:string</v8:Type>
				<v8:StringQualifiers>
					<v8:Length>0</v8:Length>
					<v8:AllowedLength>Variable</v8:AllowedLength>
				</v8:StringQualifiers>
			</Type>
"@
				}
			}

			$childObjLine = "`n`t`t<ChildObjects/>"
		if ($metaType -eq "DefinedType") {
			$childObjLine = ""
		} elseif ($metaType -eq "InformationRegister") {
			# Check if we have actual column names from form scanning
			$regKey = "InformationRegister.$objName"
			$cols = if ($registerColumns.ContainsKey($regKey) -and $registerColumns[$regKey].Count -gt 0) {
				$registerColumns[$regKey].Keys
			} else {
				@("Заглушка")
			}
			# First column as Dimension (for MainFilter), rest as Attributes (no index pressure)
			$dimXmlParts = @()
			$isFirst = $true
			foreach ($colName in $cols) {
				$elemUuid = [guid]::NewGuid().ToString()
				if ($isFirst) {
					$dimXmlParts += @"
			<Dimension uuid="$elemUuid">
				<Properties>
					<Name>$colName</Name>
					<Synonym/>
					<Comment/>
					<Type>
						<v8:Type>xs:string</v8:Type>
						<v8:StringQualifiers>
							<v8:Length>10</v8:Length>
							<v8:AllowedLength>Variable</v8:AllowedLength>
						</v8:StringQualifiers>
					</Type>
					<PasswordMode>false</PasswordMode>
					<Format/>
					<EditFormat/>
					<ToolTip/>
					<MarkNegatives>false</MarkNegatives>
					<Mask/>
					<MultiLine>false</MultiLine>
					<ExtendedEdit>false</ExtendedEdit>
					<MinValue xsi:nil="true"/>
					<MaxValue xsi:nil="true"/>
					<FillFromFillingValue>false</FillFromFillingValue>
					<FillValue xsi:nil="true"/>
					<FillChecking>DontCheck</FillChecking>
					<ChoiceFoldersAndItems>Items</ChoiceFoldersAndItems>
					<ChoiceParameterLinks/>
					<ChoiceParameters/>
					<QuickChoice>Auto</QuickChoice>
					<CreateOnInput>Auto</CreateOnInput>
					<ChoiceForm/>
					<LinkByType/>
					<ChoiceHistoryOnInput>Auto</ChoiceHistoryOnInput>
					<Master>false</Master>
					<MainFilter>true</MainFilter>
					<DenyIncompleteValues>false</DenyIncompleteValues>
					<Indexing>DontIndex</Indexing>
					<FullTextSearch>Use</FullTextSearch>
					<DataHistory>Use</DataHistory>
				</Properties>
			</Dimension>
"@
					$isFirst = $false
				} else {
					$dimXmlParts += @"
			<Attribute uuid="$elemUuid">
				<Properties>
					<Name>$colName</Name>
					<Synonym/>
					<Comment/>
					<Type>
						<v8:Type>xs:string</v8:Type>
						<v8:StringQualifiers>
							<v8:Length>10</v8:Length>
							<v8:AllowedLength>Variable</v8:AllowedLength>
						</v8:StringQualifiers>
					</Type>
					<PasswordMode>false</PasswordMode>
					<Format/>
					<EditFormat/>
					<ToolTip/>
					<MarkNegatives>false</MarkNegatives>
					<Mask/>
					<MultiLine>false</MultiLine>
					<ExtendedEdit>false</ExtendedEdit>
					<MinValue xsi:nil="true"/>
					<MaxValue xsi:nil="true"/>
					<FillFromFillingValue>false</FillFromFillingValue>
					<FillValue xsi:nil="true"/>
					<FillChecking>DontCheck</FillChecking>
					<ChoiceFoldersAndItems>Items</ChoiceFoldersAndItems>
					<ChoiceParameterLinks/>
					<ChoiceParameters/>
					<QuickChoice>Auto</QuickChoice>
					<CreateOnInput>Auto</CreateOnInput>
					<ChoiceForm/>
					<LinkByType/>
					<ChoiceHistoryOnInput>Auto</ChoiceHistoryOnInput>
					<FullTextSearch>Use</FullTextSearch>
				</Properties>
			</Attribute>
"@
				}
			}
			$childObjLine = "`r`n`t`t<ChildObjects>`r`n$($dimXmlParts -join "`r`n")`r`n`t`t</ChildObjects>"
		} elseif ($metaType -in @("AccumulationRegister","AccountingRegister","CalculationRegister")) {
			# Check if we have actual column names from form scanning
			$regKey = "$metaType.$objName"
			$cols = if ($registerColumns.ContainsKey($regKey) -and $registerColumns[$regKey].Count -gt 0) {
				$registerColumns[$regKey].Keys
			} else {
				@()
			}
			$childParts = @()
			# AccumulationRegister requires at least one Resource
			$stubResUuid = [guid]::NewGuid().ToString()
			$childParts += @"
			<Resource uuid="$stubResUuid">
				<Properties>
					<Name>Заглушка</Name>
					<Synonym/>
					<Comment/>
					<Type>
						<v8:Type>xs:decimal</v8:Type>
						<v8:NumberQualifiers>
							<v8:Digits>15</v8:Digits>
							<v8:FractionDigits>2</v8:FractionDigits>
							<v8:AllowedSign>Any</v8:AllowedSign>
						</v8:NumberQualifiers>
					</Type>
					<PasswordMode>false</PasswordMode>
					<Format/>
					<EditFormat/>
					<ToolTip/>
					<MarkNegatives>false</MarkNegatives>
					<Mask/>
					<MultiLine>false</MultiLine>
					<ExtendedEdit>false</ExtendedEdit>
					<MinValue xsi:nil="true"/>
					<MaxValue xsi:nil="true"/>
					<FillChecking>DontCheck</FillChecking>
					<ChoiceFoldersAndItems>Items</ChoiceFoldersAndItems>
					<ChoiceParameterLinks/>
					<ChoiceParameters/>
					<QuickChoice>Auto</QuickChoice>
					<CreateOnInput>Auto</CreateOnInput>
					<ChoiceForm/>
					<LinkByType/>
					<ChoiceHistoryOnInput>Auto</ChoiceHistoryOnInput>
					<FullTextSearch>Use</FullTextSearch>
				</Properties>
			</Resource>
"@
			# Add all form-referenced columns as Dimensions (short strings to avoid index overflow)
			foreach ($colName in $cols) {
				$dimUuid = [guid]::NewGuid().ToString()
				$childParts += @"
			<Dimension uuid="$dimUuid">
				<Properties>
					<Name>$colName</Name>
					<Synonym/>
					<Comment/>
					<Type>
						<v8:Type>xs:string</v8:Type>
						<v8:StringQualifiers>
							<v8:Length>10</v8:Length>
							<v8:AllowedLength>Variable</v8:AllowedLength>
						</v8:StringQualifiers>
					</Type>
					<PasswordMode>false</PasswordMode>
					<Format/>
					<EditFormat/>
					<ToolTip/>
					<MarkNegatives>false</MarkNegatives>
					<Mask/>
					<MultiLine>false</MultiLine>
					<ExtendedEdit>false</ExtendedEdit>
					<MinValue xsi:nil="true"/>
					<MaxValue xsi:nil="true"/>
					<FillChecking>DontCheck</FillChecking>
					<ChoiceFoldersAndItems>Items</ChoiceFoldersAndItems>
					<ChoiceParameterLinks/>
					<ChoiceParameters/>
					<QuickChoice>Auto</QuickChoice>
					<CreateOnInput>Auto</CreateOnInput>
					<ChoiceForm/>
					<LinkByType/>
					<ChoiceHistoryOnInput>Auto</ChoiceHistoryOnInput>
					<FullTextSearch>Use</FullTextSearch>
				</Properties>
			</Dimension>
"@
			}
			$childObjLine = "`r`n`t`t<ChildObjects>`r`n$($childParts -join "`r`n")`r`n`t`t</ChildObjects>"
		}
		$objXml = @"
<?xml version="1.0" encoding="UTF-8"?>
<MetaDataObject $ns>
	<$($info.tag) uuid="$uuid">$internalXml
		<Properties>
$propsXml		</Properties>$childObjLine
	</$($info.tag)>
</MetaDataObject>
"@
			[System.IO.File]::WriteAllText((Join-Path $objDir "$objName.xml"), $objXml, $enc)
		}
	}

	Write-Host "Generated stub configuration with $($typeMap.Count) metadata types"
	if ($registerColumns.Count -gt 0) {
		Write-Host "WARNING: Register column categories (Dimension/Resource/Attribute) are guessed. Form field bindings may not survive round-trip through a real database." -ForegroundColor Yellow
	}
}

# --- 5a. Stub via ibcmd (one call: create [--import --apply]) ---
function ConvertFrom-PlatformBytes {
    # ibcmd writes UTF-8 (checked on 8.3.24, 8.3.27, 8.5), a crashing 1cv8 may still emit
    # OEM text. Decode strictly as UTF-8 and fall back to cp866 on invalid bytes — guessing
    # one of them outright mangles Cyrillic.
    param([byte[]]$Bytes)
    if (-not $Bytes -or $Bytes.Length -eq 0) { return '' }
    try {
        $strict = New-Object System.Text.UTF8Encoding($false, $true)
        return $strict.GetString($Bytes)
    } catch {
        return [System.Text.Encoding]::GetEncoding(866).GetString($Bytes)
    }
}

function Invoke-PlatformProcess {
    # Run the platform non-interactively and capture its console output. A closed stdin pipe
    # (EOF) makes an auth prompt fast-fail instead of hanging; capturing keeps the child's
    # text out of our stream until we print it labelled (and out of the wrong encoding).
    # Returns @{ Output; ExitCode }.
    #
    # Quoting differs by engine, so the caller says which it built:
    #   ibcmd    — tokens are bare (--db-path=C:\a b), the whole token gets quoted here;
    #   1cv8     — -PreQuoted: the caller already put quotes inside the token (File="C:\a b"),
    #              which is where 1C's own parser expects them; quoting again breaks the value.
    param([string]$Exe, [string[]]$ProcArgs, [switch]$PreQuoted)
    $psi = New-Object System.Diagnostics.ProcessStartInfo
    $psi.FileName = $Exe
    $psi.Arguments = if ($PreQuoted) {
        $ProcArgs -join ' '
    } else {
        ($ProcArgs | ForEach-Object { if ($_ -match '[\s"]') { '"' + ($_ -replace '"', '\"') + '"' } else { $_ } }) -join ' '
    }
    $psi.UseShellExecute = $false
    $psi.CreateNoWindow = $true
    $psi.RedirectStandardInput = $true
    $psi.RedirectStandardOutput = $true
    $psi.RedirectStandardError = $true
    $p = [System.Diagnostics.Process]::Start($psi)
    $p.StandardInput.Close()
    # stderr is drained in parallel: reading the streams one after another deadlocks
    # as soon as the other one fills its pipe buffer.
    $errMs = New-Object System.IO.MemoryStream
    $errTask = $p.StandardError.BaseStream.CopyToAsync($errMs)
    $outMs = New-Object System.IO.MemoryStream
    $p.StandardOutput.BaseStream.CopyTo($outMs)
    $errTask.Wait()
    $p.WaitForExit()
    $out = ConvertFrom-PlatformBytes $outMs.ToArray()
    $err = ConvertFrom-PlatformBytes $errMs.ToArray()
    if ($err) { $out += $err }
    return [pscustomobject]@{ Output = $out; ExitCode = $p.ExitCode }
}

function Write-PlatformOutput {
    # Print what the platform wrote to the console as its own labelled block. Silence stays
    # silent: in batch mode 1cv8 reports through /Out and prints nothing here.
    param([string]$Text)
    if (-not $Text) { return }
    $t = $Text.TrimEnd()
    if (-not $t) { return }
    $limit = 65536
    if ($t.Length -gt $limit) {
        $t = "[... обрезано, показаны последние $limit символов ...]`r`n" + $t.Substring($t.Length - $limit)
    }
    Write-Host "--- Вывод платформы ---"
    Write-Host $t
    Write-Host "--- End ---"
}


$stubEngine = if ((Split-Path $V8Path -Leaf) -match '^ibcmd') { "ibcmd" } else { "1cv8" }

# --- Resolve additional arguments for the selected engine ---
$argHints = @{ '/F' = '-TempBasePath'; '--db-path' = '-TempBasePath' }
$extraArgs = @(Resolve-ExtraArgs $stubEngine $AdditionalV8Arguments $AdditionalIbcmdArguments $argHints)

function Format-ArgToken {
	# Start-Process takes these argument lists as one string, so quote each token that needs it.
	param([string]$Token)
	if ($Token -match '[\s"]') { return ' "' + ($Token -replace '"', '\"') + '"' }
	return " $Token"
}
$extraArgString = -join ($extraArgs | ForEach-Object { Format-ArgToken $_ })
if ($stubEngine -eq "ibcmd") {
	Write-Host "Creating infobase (ibcmd): $TempBasePath"
	$ibData = Join-Path $env:TEMP "stub_data_$(Get-Random)"
	New-Item -ItemType Directory -Path $ibData -Force | Out-Null
	$ibArgs = @("infobase", "create", "--db-path=$TempBasePath", "--create-database")
	if ($needCfg) { $ibArgs += "--import=$(Join-Path $TempBasePath 'cfg')", "--apply", "--force" }
	$ibArgs += "--data=$ibData"
	$ibArgs += $extraArgs
	$__ib = Invoke-PlatformProcess $V8Path $ibArgs
	$ibOut = $__ib.Output
	$ibRc = $__ib.ExitCode
	Remove-Item -Path $ibData -Recurse -Force -ErrorAction SilentlyContinue
	if ($ibRc -ne 0) {
		Write-PlatformOutput $ibOut
		Write-Error "Failed to create stub infobase (code: $ibRc)"
		exit 1
	}
	if ($needCfg) { Remove-Item -Path (Join-Path $TempBasePath "cfg") -Recurse -Force -ErrorAction SilentlyContinue }
	Write-Host "[OK] Stub database created: $TempBasePath"
	Write-Host $TempBasePath
	exit 0
}

# --- 5. Create infobase ---
Write-Host "Creating infobase: $TempBasePath"
$createArgs = "CREATEINFOBASE File=`"$TempBasePath`" /DisableStartupDialogs" + $extraArgString
$proc = Invoke-PlatformProcess $V8Path @($createArgs) -PreQuoted
if ($proc.ExitCode -ne 0) {
	Write-PlatformOutput $proc.Output
	Write-Error "Failed to create infobase (code: $($proc.ExitCode))"
	exit 1
}

# --- 6. Load config and update DB if there is one ---
if ($needCfg) {
	$cfgDir = Join-Path $TempBasePath "cfg"
	# LoadConfigFromFiles
	Write-Host "Loading configuration from files..."
	$loadLog = Join-Path $env:TEMP "stub_load_log.txt"
	$loadArgs = "DESIGNER /F`"$TempBasePath`" /LoadConfigFromFiles `"$cfgDir`" /Out `"$loadLog`" /DisableStartupDialogs" + $extraArgString
	$proc = Invoke-PlatformProcess $V8Path @($loadArgs) -PreQuoted
	if ($proc.ExitCode -ne 0) {
		if (Test-Path $loadLog) { Get-Content $loadLog -Raw -ErrorAction SilentlyContinue | Write-Host }
		Write-PlatformOutput $proc.Output
		Write-Error "Failed to load config (code: $($proc.ExitCode))"
		exit 1
	}

	# UpdateDBCfg
	Write-Host "Updating database configuration..."
	$updateLog = Join-Path $env:TEMP "stub_update_log.txt"
	$updateArgs = "DESIGNER /F`"$TempBasePath`" /UpdateDBCfg /Out `"$updateLog`" /DisableStartupDialogs" + $extraArgString
	$proc = Invoke-PlatformProcess $V8Path @($updateArgs) -PreQuoted
	if ($proc.ExitCode -ne 0) {
		if (Test-Path $updateLog) { Get-Content $updateLog -Raw -ErrorAction SilentlyContinue | Write-Host }
		Write-PlatformOutput $proc.Output
		Write-Error "Failed to update DB config (code: $($proc.ExitCode))"
		exit 1
	}

	# Cleanup cfg dir
	Remove-Item -Path $cfgDir -Recurse -Force -ErrorAction SilentlyContinue
}

# --- 7. Output base path ---
Write-Host "[OK] Stub database created: $TempBasePath"
Write-Host $TempBasePath
