Uninstall-PSAITerminal.ps1

#requires -Version 5.1

[CmdletBinding(SupportsShouldProcess)]
param(
    [ValidateSet('Current','WindowsPowerShell','PowerShell','Both')]
    [string]$TargetHost = 'Current',
    [string]$ModuleRoot,
    [string]$ProfilePath,
    [switch]$NoProfileIntegration,
    [switch]$PurgeUserData,
    [switch]$Force
)

Set-StrictMode -Version 3.0
$ErrorActionPreference = 'Stop'
$safetyScript = Join-Path $PSScriptRoot 'PSAIStorageSafety.ps1'
if (-not (Test-Path -LiteralPath $safetyScript -PathType Leaf)) {
    $safetyScript = Join-Path $PSScriptRoot 'module\PSAIStorageSafety.ps1'
}
. $safetyScript
if ($env:OS -ne 'Windows_NT') { throw 'PSAITerminal 仅支持 Windows。' }
if ($NoProfileIntegration -and -not [string]::IsNullOrWhiteSpace($ProfilePath)) {
    throw '-NoProfileIntegration 与 -ProfilePath 不能同时使用。'
}
if ($TargetHost -eq 'Both' -and (-not [string]::IsNullOrWhiteSpace($ModuleRoot) -or -not [string]::IsNullOrWhiteSpace($ProfilePath))) {
    throw '-TargetHost Both 不能与 -ModuleRoot 或 -ProfilePath 同时使用。'
}
if ($env:PSAI_TEST_DOCUMENTS_HOME -and $env:PSAI_TEST_MODE -ne '1') {
    throw 'PSAI_TEST_DOCUMENTS_HOME 只能在 PSAI_TEST_MODE=1 的隔离测试中使用。'
}
$script:DocumentsRoot = if ($env:PSAI_TEST_DOCUMENTS_HOME) { [IO.Path]::GetFullPath($env:PSAI_TEST_DOCUMENTS_HOME) }
    else { [Environment]::GetFolderPath([Environment+SpecialFolder]::MyDocuments) }
$configHome = if ($env:PSAI_CONFIG_HOME) { $env:PSAI_CONFIG_HOME } else { Join-Path $script:DocumentsRoot 'PowerShell\PSAITerminal' }
$dataHome = if ($env:PSAI_DATA_HOME) { $env:PSAI_DATA_HOME }
    elseif ($env:PSAI_TEST_LOCALAPPDATA) { $env:PSAI_TEST_LOCALAPPDATA }
    else { Join-Path ([Environment]::GetFolderPath('LocalApplicationData')) 'PowerShell\PSAITerminal' }
if ($TargetHost -eq 'Both') {
    foreach ($hostTarget in @('WindowsPowerShell','PowerShell')) {
        $hostModule = Join-Path $script:DocumentsRoot "$hostTarget\Modules\PSAITerminal"
        Assert-PSAIModuleRemoval $hostModule
        Assert-PSAIStorageOutsideModule $hostModule @($configHome,$dataHome)
    }
    $results = foreach ($hostTarget in @('WindowsPowerShell','PowerShell')) {
        $arguments = @{ TargetHost=$hostTarget; NoProfileIntegration=$NoProfileIntegration; PurgeUserData=$PurgeUserData; Force=$Force }
        if ($PSBoundParameters.ContainsKey('Confirm')) { $arguments.Confirm = $PSBoundParameters['Confirm'] }
        if ($WhatIfPreference) { $arguments.WhatIf = $true }
        & $PSCommandPath @arguments
    }
    return $results
}
$script:ResolvedTargetHost = if ($TargetHost -eq 'Current') {
    if ($PSVersionTable.PSEdition -eq 'Core') { 'PowerShell' } else { 'WindowsPowerShell' }
} else { $TargetHost }
$script:PathComparison = [StringComparison]::OrdinalIgnoreCase
$script:DirectorySeparator = [IO.Path]::DirectorySeparatorChar

$resolvedModuleRoot = Resolve-PSAIModuleRoot $ModuleRoot $script:DocumentsRoot $script:ResolvedTargetHost
$resolvedProfilePath = if ($NoProfileIntegration) { $null } else { Resolve-PSAIProfilePath $ProfilePath $script:DocumentsRoot $script:ResolvedTargetHost }
$moduleBase = ConvertTo-PSAINormalizedPath (Join-Path $resolvedModuleRoot 'PSAITerminal')
if ((Split-Path $moduleBase -Leaf) -ne 'PSAITerminal' -or
    -not (ConvertTo-PSAINormalizedPath (Split-Path $moduleBase -Parent)).Equals($resolvedModuleRoot, $script:PathComparison)) {
    throw '拒绝删除无法确认的模块目录。'
}

$moduleBaseWithSeparator = $moduleBase + $script:DirectorySeparator
$loaded = Get-Module PSAITerminal | Where-Object {
    $_.Path -and (ConvertTo-PSAINormalizedPath $_.Path).StartsWith($moduleBaseWithSeparator, $script:PathComparison)
}
if ($loaded) { throw '当前进程正在使用 PSAITerminal。请运行 pwsh -NoProfile 后再执行卸载脚本。' }

Assert-PSAIModuleRemoval $moduleBase
Assert-PSAIStorageOutsideModule $moduleBase @($configHome,$dataHome)
$profileContent = $null; $profileUpdate = $null
if ($resolvedProfilePath -and (Test-Path -LiteralPath $resolvedProfilePath)) {
    [void](Assert-PSAISafeRemovalDirectory (Split-Path -Parent $resolvedProfilePath))
    if ((Get-Item -LiteralPath $resolvedProfilePath -Force).Attributes -band [IO.FileAttributes]::ReparsePoint) {
        throw '拒绝修改重解析点 Profile。'
    }
    $profileContent = Read-PSAITextFile $resolvedProfilePath
    $profileUpdate = (Get-PSAIProfileUpdate $profileContent).Content
}
$purgePlan = $null; $coreAssembly = $null
if ($PurgeUserData) {
    $purgePlan = Get-PSAIStoragePurgePlan $configHome $dataHome
    $assemblyCandidates = @((Join-Path $moduleBase 'bin\PSAITerminal.dll'), (Join-Path $PSScriptRoot 'bin\PSAITerminal.dll'))
    if (Test-Path -LiteralPath $moduleBase -PathType Container) {
        $assemblyCandidates += @(Get-ChildItem -LiteralPath $moduleBase -Directory |
            Where-Object Name -Match '^\d+\.\d+\.\d+(?:\.\d+)?$' |
            Sort-Object { [version]$_.Name } -Descending | ForEach-Object { Join-Path $_.FullName 'bin\PSAITerminal.dll' })
    }
    $coreAssembly = $assemblyCandidates | Where-Object { Test-Path -LiteralPath $_ -PathType Leaf } | Select-Object -First 1
    if ($purgePlan.CredentialTargets.Count -and -not $coreAssembly) { throw '无法定位凭据清理所需的核心程序集,原文件未删除。' }
}
if ($Force -and -not $PSBoundParameters.ContainsKey('Confirm')) { $ConfirmPreference = 'None' }
if ($PSCmdlet.ShouldProcess($moduleBase, '卸载 PSAITerminal 模块')) {
    $credentialsPurged = $false; $userDataPurged = $false
    if ($PurgeUserData) {
        Assert-PSAIStoragePurgePlan $purgePlan
        Invoke-PSAICredentialPurge $purgePlan.CredentialTargets $coreAssembly
        $credentialsPurged = $true
        Remove-PSAIStorageData $purgePlan
        $userDataPurged = $true
    }
    $profileIntegrationRemoved = $false
    if ($null -ne $profileUpdate -and $profileUpdate -ne $profileContent) {
        if ((Read-PSAITextFile $resolvedProfilePath) -cne $profileContent) { throw 'Profile 在预检后已变化,停止卸载。' }
        Write-PSAIUtf8FileAtomically $resolvedProfilePath $profileUpdate
        $profileIntegrationRemoved = $true
    }
    Assert-PSAIModuleRemoval $moduleBase
    if (Test-Path -LiteralPath $moduleBase -PathType Container) { Remove-Item -LiteralPath $moduleBase -Recurse -Force -ErrorAction Stop }
    [Environment]::SetEnvironmentVariable("__PSAI_SESSION_PID_$PID", $null, 'Process')
    [pscustomobject]@{
        RemovedPath = $moduleBase
        ProfilePath = $resolvedProfilePath
        ProfileIntegrationRemoved = $profileIntegrationRemoved
        UserDataPreserved = (-not $userDataPurged)
        UserDataPurged = $userDataPurged
        CredentialsPurged = $credentialsPurged
        ModuleRemoved = (-not (Test-Path -LiteralPath $moduleBase))
    }
}