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

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

# --- Output helper ---
$script:lines = @()
function Out([string]$text) { $script:lines += $text }

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-SupportStatusForPath([string]$targetPath) {
	try {
		$rp = (Resolve-Path $targetPath).Path
		$elemUuid = $null
		$binPath = $null
		# Reads the uuid of the first metadata element in an .xml file (or $null).
		function Get-RootUuid([string]$xmlPath) {
			if (-not (Test-Path $xmlPath)) { return $null }
			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) { $u = $el.GetAttribute("uuid"); if ($u) { return $u } }
			} catch {}
			return $null
		}
		# The target file itself may be the element meta-xml (e.g. Subsystems/X.xml).
		$elemUuid = Get-RootUuid $rp
		if (Test-ExternalObjectRoot $rp) { return $null }
		$d = [System.IO.Path]::GetDirectoryName($rp)
		for ($i = 0; $i -lt 12 -and $d; $i++) {
			if (Test-ExternalObjectRoot "$d.xml") { return $null }
			if (-not $elemUuid) { $elemUuid = Get-RootUuid "$d.xml" }
			if (-not $binPath) {
				$cand = Join-Path (Join-Path $d "Ext") "ParentConfigurations.bin"
				if ((Test-Path $cand) -or (Test-Path (Join-Path $d "Configuration.xml"))) { $binPath = $cand }
			}
			if ($elemUuid -and $binPath) { 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 $elemUuid) { return "не на поддержке" }
		$u = [regex]::Escape($elemUuid.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 "не на поддержке" }
}

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

# --- Helper: get LocalString text ---
function Get-MLText($node) {
	if (-not $node -or -not $node.HasChildNodes) { return "" }
	foreach ($item in $node.ChildNodes) {
		if ($item.NodeType -ne 'Element') { continue }
		$lang = ""; $content = ""
		foreach ($c in $item.ChildNodes) {
			if ($c.NodeType -ne 'Element') { continue }
			if ($c.LocalName -eq "lang") { $lang = $c.InnerText }
			if ($c.LocalName -eq "content") { $content = $c.InnerText }
		}
		if ($lang -eq "ru" -and $content) { return $content }
	}
	# fallback: first item
	foreach ($item in $node.ChildNodes) {
		if ($item.NodeType -ne 'Element') { continue }
		foreach ($c in $item.ChildNodes) {
			if ($c.NodeType -ne 'Element') { continue }
			if ($c.LocalName -eq "content" -and $c.InnerText) { return $c.InnerText }
		}
	}
	return ""
}

# --- Helper: load subsystem XML ---
function Load-SubsystemXml([string]$xmlPath) {
	[xml]$doc = Get-Content -Path $xmlPath -Encoding UTF8
	$ns = New-Object System.Xml.XmlNamespaceManager($doc.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")
	$sub = $doc.SelectSingleNode("/md:MetaDataObject/md:Subsystem", $ns)
	if (-not $sub) {
		Write-Host "[ERROR] Not a valid subsystem XML: $xmlPath"
		exit 1
	}
	return @{ Doc=$doc; Ns=$ns; Sub=$sub }
}

# --- Helper: get content items ---
function Get-ContentItems($props, $ns) {
	$items = @()
	$contentNode = $props.SelectSingleNode("md:Content", $ns)
	if (-not $contentNode -or -not $contentNode.HasChildNodes) { return $items }
	foreach ($item in $contentNode.SelectNodes("xr:Item", $ns)) {
		$items += $item.InnerText
	}
	return $items
}

# --- Helper: get child subsystem names ---
function Get-ChildNames($sub, $ns) {
	$names = @()
	$co = $sub.SelectSingleNode("md:ChildObjects", $ns)
	if (-not $co -or -not $co.HasChildNodes) { return $names }
	foreach ($child in $co.ChildNodes) {
		if ($child.NodeType -eq 'Element' -and $child.LocalName -eq "Subsystem") {
			$names += $child.InnerText
		}
	}
	return $names
}

# --- Helper: group content by type ---
function Group-ContentByType($items) {
	$groups = [ordered]@{}
	foreach ($item in $items) {
		if ($item -match '^([^.]+)\.(.+)$') {
			$type = $Matches[1]
			$name = $Matches[2]
		} elseif ($item -match '^[0-9a-fA-F]{8}-') {
			$type = "[UUID]"
			$name = $item
		} else {
			$type = "[Other]"
			$name = $item
		}
		if (-not $groups.Contains($type)) { $groups[$type] = @() }
		$groups[$type] += $name
	}
	return $groups
}

# --- Helper: find subsystem dir from XML path ---
function Get-SubsystemDir([string]$xmlPath) {
	$dir = [System.IO.Path]::GetDirectoryName($xmlPath)
	$baseName = [System.IO.Path]::GetFileNameWithoutExtension($xmlPath)
	return Join-Path $dir $baseName
}

# --- Show functions for full mode ---
function Show-Overview {
	Out "Подсистема: $subName"
	$support = Get-SupportStatusForPath $SubsystemPath
	if ($null -ne $support) { Out "Поддержка: $support" }
	if ($synonym -and $synonym -ne $subName) { Out "Синоним: $synonym" }
	if ($commentText) { Out "Комментарий: $commentText" }
	Out "ВключатьВКомандныйИнтерфейс: $inclCI"
	Out "ИспользоватьОднуКоманду: $useOneCmd"
	if ($explanation) { Out "Пояснение: $explanation" }
	if ($picText) { Out "Картинка: $picText" }
	if ($contentItems.Count -gt 0) {
		$parts = @()
		foreach ($type in $groups.Keys) {
			$parts += "$type`: $($groups[$type].Count)"
		}
		Out "Состав: $($contentItems.Count) объектов ($($parts -join ', '))"
	} else {
		Out "Состав: пусто"
	}
	if ($childNames.Count -gt 0) {
		Out "Дочерние подсистемы ($($childNames.Count)): $($childNames -join ', ')"
	}
	if ($hasCI) {
		Out "Командный интерфейс: есть"
	}
}

function Show-Content {
	Out "Состав подсистемы $subName ($($contentItems.Count) объектов):"
	Out ""
	if ($Name) {
		if ($groups.Contains($Name)) {
			$filtered = $groups[$Name]
			Out "$Name ($($filtered.Count)):"
			foreach ($n in $filtered) { Out "  $n" }
		} else {
			Out "[INFO] Тип '$Name' не найден в составе."
			Out "Доступные типы: $($groups.Keys -join ', ')"
		}
	} else {
		foreach ($type in $groups.Keys) {
			Out "$type ($($groups[$type].Count)):"
			foreach ($n in $groups[$type]) { Out "  $n" }
			Out ""
		}
	}
}

function Show-CI {
	$localSubDir = Get-SubsystemDir $SubsystemPath
	$localCiPath = Join-Path (Join-Path $localSubDir "Ext") "CommandInterface.xml"

	if (-not (Test-Path $localCiPath)) {
		Out "Командный интерфейс: $subName"
		Out ""
		Out "Файл CommandInterface.xml не найден."
		Out "Путь: $localCiPath"
	} else {
		[xml]$ciDoc = Get-Content -Path $localCiPath -Encoding UTF8
		$ciNs = New-Object System.Xml.XmlNamespaceManager($ciDoc.NameTable)
		$ciNs.AddNamespace("ci", "http://v8.1c.ru/8.3/xcf/extrnprops")
		$ciNs.AddNamespace("xr", "http://v8.1c.ru/8.3/xcf/readable")

		$ciRoot = $ciDoc.DocumentElement

		Out "Командный интерфейс: $subName"
		Out ""

		# --- CommandsVisibility ---
		$visSection = $ciRoot.SelectSingleNode("ci:CommandsVisibility", $ciNs)
		if ($visSection) {
			$hidden = @(); $shown = @()
			foreach ($cmd in $visSection.SelectNodes("ci:Command", $ciNs)) {
				$cmdName = $cmd.GetAttribute("name")
				$vis = $cmd.SelectSingleNode("ci:Visibility/xr:Common", $ciNs)
				if ($vis -and $vis.InnerText -eq "false") { $hidden += $cmdName }
				else { $shown += $cmdName }
			}
			$total = $hidden.Count + $shown.Count
			if (-not $Name -or $Name -eq "visibility") {
				Out "Видимость ($total):"
				if ($hidden.Count -gt 0) {
					Out "  СКРЫТО ($($hidden.Count)):"
					foreach ($h in $hidden) { Out "    $h" }
				}
				if ($shown.Count -gt 0) {
					Out "  ПОКАЗАНО ($($shown.Count)):"
					foreach ($s in $shown) { Out "    $s" }
				}
				Out ""
			}
		}

		# --- CommandsPlacement ---
		$placeSection = $ciRoot.SelectSingleNode("ci:CommandsPlacement", $ciNs)
		if ($placeSection) {
			$placements = @()
			foreach ($cmd in $placeSection.SelectNodes("ci:Command", $ciNs)) {
				$cmdName = $cmd.GetAttribute("name")
				$grp = $cmd.SelectSingleNode("ci:CommandGroup", $ciNs)
				$pl = $cmd.SelectSingleNode("ci:Placement", $ciNs)
				$grpText = if ($grp) { $grp.InnerText } else { "?" }
				$plText = if ($pl) { $pl.InnerText } else { "?" }
				$placements += @{ Name=$cmdName; Group=$grpText; Placement=$plText }
			}
			if ((-not $Name -or $Name -eq "placement") -and $placements.Count -gt 0) {
				Out "Размещение ($($placements.Count)):"
				$arrow = [char]0x2192
				foreach ($p in $placements) {
					Out "  $($p.Name) $arrow $($p.Group) ($($p.Placement))"
				}
				Out ""
			}
		}

		# --- CommandsOrder ---
		$orderSection = $ciRoot.SelectSingleNode("ci:CommandsOrder", $ciNs)
		if ($orderSection) {
			$orderGroups = [ordered]@{}
			foreach ($cmd in $orderSection.SelectNodes("ci:Command", $ciNs)) {
				$cmdName = $cmd.GetAttribute("name")
				$grp = $cmd.SelectSingleNode("ci:CommandGroup", $ciNs)
				$grpText = if ($grp) { $grp.InnerText } else { "?" }
				if (-not $orderGroups.Contains($grpText)) { $orderGroups[$grpText] = @() }
				$orderGroups[$grpText] += $cmdName
			}
			$totalOrder = 0
			foreach ($k in $orderGroups.Keys) { $totalOrder += $orderGroups[$k].Count }
			if ((-not $Name -or $Name -eq "order") -and $totalOrder -gt 0) {
				Out "Порядок команд ($totalOrder):"
				foreach ($grpName in $orderGroups.Keys) {
					Out "  [$grpName]:"
					foreach ($c in $orderGroups[$grpName]) { Out "    $c" }
				}
				Out ""
			}
		}

		# --- SubsystemsOrder ---
		$subOrderSection = $ciRoot.SelectSingleNode("ci:SubsystemsOrder", $ciNs)
		if ($subOrderSection) {
			$subOrder = @()
			foreach ($s in $subOrderSection.SelectNodes("ci:Subsystem", $ciNs)) {
				$subOrder += $s.InnerText
			}
			if ((-not $Name -or $Name -eq "subsystems") -and $subOrder.Count -gt 0) {
				Out "Порядок подсистем ($($subOrder.Count)):"
				for ($i = 0; $i -lt $subOrder.Count; $i++) {
					Out "  $($i+1). $($subOrder[$i])"
				}
				Out ""
			}
		}

		# --- GroupsOrder ---
		$grpOrderSection = $ciRoot.SelectSingleNode("ci:GroupsOrder", $ciNs)
		if ($grpOrderSection) {
			$grpOrder = @()
			foreach ($g in $grpOrderSection.SelectNodes("ci:Group", $ciNs)) {
				$grpOrder += $g.InnerText
			}
			if ((-not $Name -or $Name -eq "groups") -and $grpOrder.Count -gt 0) {
				Out "Порядок групп ($($grpOrder.Count)):"
				foreach ($g in $grpOrder) { Out "  $g" }
			}
		}
	}
}

# ============================================================
# Mode: tree
# ============================================================
if ($Mode -eq "tree") {
	$isDir = Test-Path $SubsystemPath -PathType Container
	$rootDir = $null
	$rootXml = $null

	if ($isDir) {
		# Subsystems/ directory — show all top-level subsystems
		$rootDir = $SubsystemPath
	} else {
		# Specific subsystem XML — show tree from this subsystem
		if (-not (Test-Path $SubsystemPath)) {
			Write-Host "[ERROR] File not found: $SubsystemPath"
			exit 1
		}
		$rootXml = $SubsystemPath
	}

	# Box-drawing chars (PS 5.1 compatible)
	$script:T_BRANCH = [char]0x251C + [char]0x2500 + [char]0x2500 + " "  # ├──
	$script:T_LAST   = [char]0x2514 + [char]0x2500 + [char]0x2500 + " "  # └──
	$script:T_PIPE   = [char]0x2502 + "   "                               # │
	$script:T_ARROW  = [char]0x2192                                        # →

	function Get-TreeLine([string]$xmlPath) {
		$parsed = Load-SubsystemXml $xmlPath
		$sub = $parsed.Sub; $ns = $parsed.Ns
		$props = $sub.SelectSingleNode("md:Properties", $ns)
		$name = $props.SelectSingleNode("md:Name", $ns).InnerText

		$markers = @()
		$subDir = Get-SubsystemDir $xmlPath
		$ciPath = Join-Path (Join-Path $subDir "Ext") "CommandInterface.xml"
		if (Test-Path $ciPath) { $markers += "CI" }
		$useOne = $props.SelectSingleNode("md:UseOneCommand", $ns)
		if ($useOne -and $useOne.InnerText -eq "true") { $markers += "OneCmd" }
		$inclCI = $props.SelectSingleNode("md:IncludeInCommandInterface", $ns)
		if ($inclCI -and $inclCI.InnerText -eq "false") { $markers += "Скрыт" }
		$markerStr = if ($markers.Count -gt 0) { " [$($markers -join ', ')]" } else { "" }

		$contentItems = @(Get-ContentItems $props $ns)
		$childNames = @(Get-ChildNames $sub $ns)
		$childStr = if ($childNames.Count -gt 0) { ", $($childNames.Count) дочерних" } else { "" }

		return @{
			Label = "$name$markerStr ($($contentItems.Count) объектов$childStr)"
			SubDir = $subDir
			ChildNames = $childNames
		}
	}

	function Build-TreeEntry([string]$xmlPath, [string]$prefix, [bool]$isLast, [bool]$isRoot) {
		$info = Get-TreeLine $xmlPath

		$connector = if ($isRoot) { "" } elseif ($isLast) { $script:T_LAST } else { $script:T_BRANCH }
		Out "$prefix$connector$($info.Label)"

		if ($info.ChildNames.Count -gt 0) {
			$childPrefix = if ($isRoot) { "" } elseif ($isLast) { "$prefix    " } else { "$prefix$($script:T_PIPE)" }
			$subsDir = Join-Path $info.SubDir "Subsystems"
			for ($i = 0; $i -lt $info.ChildNames.Count; $i++) {
				$childXml = Join-Path $subsDir "$($info.ChildNames[$i]).xml"
				$childIsLast = ($i -eq $info.ChildNames.Count - 1)
				if (Test-Path $childXml) {
					Build-TreeEntry $childXml $childPrefix $childIsLast $false
				} else {
					$conn2 = if ($childIsLast) { $script:T_LAST } else { $script:T_BRANCH }
					Out "$childPrefix$conn2$($info.ChildNames[$i]) [NOT FOUND]"
				}
			}
		}
	}

	if ($rootDir) {
		$label = Split-Path $rootDir -Leaf
		Out "Дерево подсистем от: $label/"
		Out ""
		$xmlFiles = @(Get-ChildItem $rootDir -Filter "*.xml" -File | Sort-Object Name)
		if ($Name) {
			$xmlFiles = @($xmlFiles | Where-Object { $_.BaseName -eq $Name })
			if ($xmlFiles.Count -eq 0) {
				Write-Host "[ERROR] Subsystem '$Name' not found in $rootDir"
				exit 1
			}
		}
		for ($i = 0; $i -lt $xmlFiles.Count; $i++) {
			Build-TreeEntry $xmlFiles[$i].FullName "" ($i -eq $xmlFiles.Count - 1) $true
		}
	} else {
		Build-TreeEntry $rootXml "" $true $true
	}

} elseif ($Mode -eq "ci") {
# ============================================================
# Mode: ci — CommandInterface.xml
# ============================================================
	if (Test-Path $SubsystemPath -PathType Container) {
		Write-Host "[ERROR] ci mode requires a subsystem .xml file, not a directory"
		exit 1
	}
	# File not found — check Dir/Name/Name.xml → Dir/Name.xml
	if (-not (Test-Path $SubsystemPath)) {
		$fn = [System.IO.Path]::GetFileNameWithoutExtension($SubsystemPath)
		$pd = Split-Path $SubsystemPath
		if ($fn -eq (Split-Path $pd -Leaf)) {
			$c = Join-Path (Split-Path $pd) "$fn.xml"
			if (Test-Path $c) { $SubsystemPath = $c }
		}
	}
	if (-not (Test-Path $SubsystemPath)) {
		Write-Host "[ERROR] File not found: $SubsystemPath"
		exit 1
	}

	$parsed = Load-SubsystemXml $SubsystemPath
	$sub = $parsed.Sub; $ns = $parsed.Ns
	$props = $sub.SelectSingleNode("md:Properties", $ns)
	$subName = $props.SelectSingleNode("md:Name", $ns).InnerText

	Show-CI

} else {
# ============================================================
# Mode: overview / content — requires a subsystem XML file
# ============================================================
	if (Test-Path $SubsystemPath -PathType Container) {
		$dirName = Split-Path $SubsystemPath -Leaf
		$candidate = Join-Path $SubsystemPath "$dirName.xml"
		$sibling = Join-Path (Split-Path $SubsystemPath) "$dirName.xml"
		if (Test-Path $candidate) {
			$SubsystemPath = $candidate
		} elseif (Test-Path $sibling) {
			$SubsystemPath = $sibling
		} else {
			Write-Host "[ERROR] No $dirName.xml found in directory. Use -Mode tree for directory listing."
			exit 1
		}
	}

	# File not found — check Dir/Name/Name.xml → Dir/Name.xml
	if (-not (Test-Path $SubsystemPath)) {
		$fn = [System.IO.Path]::GetFileNameWithoutExtension($SubsystemPath)
		$pd = Split-Path $SubsystemPath
		if ($fn -eq (Split-Path $pd -Leaf)) {
			$c = Join-Path (Split-Path $pd) "$fn.xml"
			if (Test-Path $c) { $SubsystemPath = $c }
		}
	}
	if (-not (Test-Path $SubsystemPath)) {
		Write-Host "[ERROR] File not found: $SubsystemPath"
		exit 1
	}

	$parsed = Load-SubsystemXml $SubsystemPath
	$sub = $parsed.Sub; $ns = $parsed.Ns
	$props = $sub.SelectSingleNode("md:Properties", $ns)

	$subName = $props.SelectSingleNode("md:Name", $ns).InnerText
	$synonym = Get-MLText $props.SelectSingleNode("md:Synonym", $ns)
	$comment = $props.SelectSingleNode("md:Comment", $ns)
	$commentText = if ($comment -and $comment.InnerText) { $comment.InnerText } else { "" }
	$inclHelp = $props.SelectSingleNode("md:IncludeHelpInContents", $ns).InnerText
	$inclCI = $props.SelectSingleNode("md:IncludeInCommandInterface", $ns).InnerText
	$useOneCmd = $props.SelectSingleNode("md:UseOneCommand", $ns).InnerText
	$explanation = Get-MLText $props.SelectSingleNode("md:Explanation", $ns)

	# Picture
	$picNode = $props.SelectSingleNode("md:Picture", $ns)
	$picText = ""
	if ($picNode -and $picNode.HasChildNodes) {
		$picRef = $picNode.SelectSingleNode("xr:Ref", $ns)
		if ($picRef -and $picRef.InnerText) { $picText = $picRef.InnerText }
	}

	# Content
	$contentItems = @(Get-ContentItems $props $ns)
	$groups = Group-ContentByType $contentItems

	# Children
	$childNames = @(Get-ChildNames $sub $ns)

	# CI presence
	$subDir = Get-SubsystemDir $SubsystemPath
	$ciPath = Join-Path (Join-Path $subDir "Ext") "CommandInterface.xml"
	$hasCI = Test-Path $ciPath

	if ($Mode -eq "overview") {
		Show-Overview
	} elseif ($Mode -eq "content") {
		Show-Content
	} elseif ($Mode -eq "full") {
		Show-Overview
		Out ""; Out "--- content ---"; Out ""
		Show-Content
		Out ""; Out "--- ci ---"; Out ""
		Show-CI
	}
}

# --- 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 }
}
