popy.psm1

# ---- ArgumentCompleter.ps1 ----
Register-ArgumentCompleter -Native -CommandName popy -ScriptBlock {
    param($word, $ast, $cursor)
    # 每次补全时重新读取配置
    $configPath = Join-Path $env:UserProfile ".popy"
    $configFile = Join-Path $configPath 'config.json'
    if (Test-Path $configFile) {
        $cfg = Get-Content -LiteralPath $configFile -Raw | ConvertFrom-Json
    }
    $storePath = $cfg.storePath
    if (-not $storePath) { return }

    $versionsPath = Join-Path -Path $storePath -ChildPath "versions"
    $scriptsVersionPath = Join-Path -Path $storePath -ChildPath "scripts"
    $venvPath = Join-Path -Path $storePath -ChildPath "venvs"
    $downloadPath = Join-Path -Path $storePath -ChildPath "downloads"
    $requirementsPath = Join-Path -Path $storePath -ChildPath "requirements"

    $line = $ast.ToString()
    $parts = $line -split '\s+'
    switch -Regex ($parts[1]) {
        { $_ -in 'f', 'rfreeze' } {
            Get-ChildItem -Path $requirementsPath -Filter *.txt -Name |
            Where-Object { $_ -like "$word*" } |
            ForEach-Object {
                [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_)
            }
            break
        }
        { $_ -in 'a', 'activate' } {
            Get-ChildItem -Directory -Path $venvPath -Name |
            Where-Object { $_ -like "$word*" } |
            ForEach-Object {
                [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_)
            }
            break
        }
    }
}
# ---- Config.ps1 ----
function Set-Config {
    <#
    .SYNOPSIS
        写入或更新配置项。
    .EXAMPLE
        Set-Config -Key 'wix.version' -Value '3.14'
        Set-Config -Key 'mirrors' -Value @('https://a.com','https://b.com')
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [string]$Key,

        [Parameter(Mandatory)]
        [AllowNull()]
        $Value
    )

    # 1. 统一读取现有配置(没有则初始化空对象)
    $cfg = Get-Configs
    if (-not $cfg) { $cfg = [PSCustomObject]@{} }

    # 2. 支持 a.b.c 的点分深度路径
    $parts = $Key -split '\.'
    $node = $cfg
    for ($i = 0; $i -lt $parts.Count - 1; $i++) {
        $p = $parts[$i]
        if (-not $node.$p) {
            $node | Add-Member -NotePropertyName $p -NotePropertyValue ([PSCustomObject]@{}) -Force
        }
        $node = $node.$p
    }

    # 3. 设置最终值
    $leaf = $parts[-1]
    if ($node.PSObject.Properties.Name -contains $leaf) {
        $node.$leaf = $Value
    }
    else {
        $node | Add-Member -NotePropertyName $leaf -NotePropertyValue $Value -Force
    }

    # 4. 保存文件(UTF-8 无 BOM,缩进友好)
    $popyConfigPath = (Get-ConfigsInternal).ConfigDir
    $configFile = Join-Path $popyConfigPath 'config.json'
    $cfg | ConvertTo-Json -Depth 10 |
    Out-File -LiteralPath $configFile -Encoding utf8 -Force
}

# ———— 内部帮助函数,避免重复计算路径 ————
function Get-ConfigsInternal {
    $configPath = Get-ConfigPath
    $configFile = Join-Path $configPath 'config.json'
    @{
        ConfigDir  = $configPath
        ConfigFile = $configFile
    }
}

# 兼容旧接口
function Get-Configs {
    $info = Get-ConfigsInternal
    if (Test-Path $info.ConfigFile) {
        return Get-Content -LiteralPath $info.ConfigFile -Raw | ConvertFrom-Json
    }
}
# ---- PathUtils.ps1 ----
function Get-RootPath {
    return Split-Path -Parent $PSScriptRoot
}

function Get-ConfigPath {
    return Join-Path $env:UserProfile ".popy"
}

function Get-StorePath {
    $cfg = Get-Configs
    return $cfg.storePath
}

function Get-ToolsPath {
    $popyConfigPath = Get-ConfigPath
    return Join-Path $popyConfigPath 'tools'
}

function New-Directory {
    param (
        [Parameter(Mandatory = $false)]
        [string]$checkedPath,

        [Parameter(Mandatory = $false)]
        [string]$description
    )
        
    if (-not(Test-Path -Path $checkedPath)) {
        try {
            New-Item -Path $checkedPath -ItemType Directory -ErrorAction Stop | Out-Null
            # Write-Host "$description 已创建:$checkedPath" -ForegroundColor Green
        }
        catch {
            Write-Host "创建 $description 时发生错误:$($_.Exception.Message)" -ForegroundColor Red
            exit
        }
    }
}
# ---- SaveFile.ps1 ----

#新版
function Save-File {
    param(
        [string]$TargetURL,
        [string]$SavePath
    )

    # 1. 打开 TLS 1.2
    [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12

    # 2. 如果用户只给了目录,自动补文件名
    if ([IO.Path]::HasExtension($SavePath) -eq $false) {
        $fileName = [IO.Path]::GetFileName($TargetURL)
        $SavePath = Join-Path $SavePath $fileName
    }

    # 3. 用 WebClient + 事件回调显示进度
    $wc = New-Object System.Net.WebClient

    # 监听进度事件
    Register-ObjectEvent -InputObject $wc -EventName DownloadProgressChanged -SourceIdentifier DL -Action {
        $percent = $EventArgs.ProgressPercentage
        Write-Progress -Activity "Downloading" -Status "$percent %" -PercentComplete $percent
    } | Out-Null

    # 监听完成事件
    Register-ObjectEvent -InputObject $wc -EventName DownloadFileCompleted -SourceIdentifier DLC -Action {
        Write-Progress -Activity "Downloading" -Completed
    } | Out-Null

    try {
        Write-Host "正在下载 $([IO.Path]::GetFileName($TargetURL)) ..."
        $wc.DownloadFileAsync($TargetURL, $SavePath)

        # 等待异步完成
        while ($wc.IsBusy) {
            Start-Sleep -Milliseconds 100
        }
        if ((Get-Item $SavePath).Length -eq 0) {
            Remove-Item $SavePath -Force
            throw "下载文件大小为 0,可能 URL 无效。"
        }
        Write-Host "✅ 已保存到 $SavePath" -ForegroundColor Green
    }
    catch {
        Write-Host "❌ 下载失败:$($_.Exception.Message)" -ForegroundColor Red
    }
    finally {
        # 清理事件
        Unregister-Event -SourceIdentifier DL  -ErrorAction SilentlyContinue
        Unregister-Event -SourceIdentifier DLC -ErrorAction SilentlyContinue
        $wc.Dispose()
    }
}

function Expand-InPlace {
    # 1. 就地解压到与压缩包同级目录
    # Expand-InPlace -Path C:\Temp\shim.zip

    # 2. 指定输出目录
    # Expand-InPlace -Path C:\Temp\wix311-binaries.zip -Destination D:\Tools

    # 3. 覆盖已有
    # Expand-InPlace -Path C:\Temp\wix311-binaries.zip -Force
    [CmdletBinding()]
    param(
        [Parameter(Mandatory, ValueFromPipeline)]
        [ValidateScript({ Test-Path $_ -PathType Leaf })]
        [string]$Path,

        [string]$Destination,

        [switch]$Force,

        [switch]$Flatten    # ← 新增开关,控制要不要带一层默认文件夹
    )

    begin {
        Add-Type -AssemblyName System.IO.Compression.FileSystem -EA SilentlyContinue
    }


    process {
        $Path = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($Path)
        $root = if ($Destination) { $Destination } else { [IO.Path]::GetDirectoryName($Path) }
        $root = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($root)

        # 如果 -Flatten 指定,直接落在 root;否则再套一层压缩包名
        $outFolder = if ($Flatten) { $root } else {
            $baseName = [IO.Path]::GetFileNameWithoutExtension($Path)
            if ($Path -match '\.tar\.(gz|bz2|xz)$') { $baseName = [IO.Path]::GetFileNameWithoutExtension($baseName) }
            Join-Path $root $baseName
        }

        if (-not (Test-Path $outFolder)) { New-Item -Path $outFolder -ItemType Directory -Force | Out-Null }
        if ($Force -and (Test-Path $outFolder)) { Remove-Item $outFolder -Recurse -Force; New-Item -Path $outFolder -ItemType Directory -Force | Out-Null }

        # 1) .zip
        if ($Path -match '\.zip$') {
            try {
                [System.IO.Compression.ZipFile]::ExtractToDirectory($Path, $outFolder)
                Write-Host "✅ 已解压:$outFolder" -ForegroundColor Green
                return
            }
            catch {
                Write-Warning "内置 ZipFile 解压失败,尝试 7z:$($_.Exception.Message)"
                $configPath = Join-Path $env:UserProfile ".popy"
                $downloadPath = Join-Path $configPath "tools"
                Write-Warning "如下载或解压失败,可手动下载压缩包放入 $downloadPath"
            }
        }

        # 2) 其余格式(7z / tar.gz / tar.bz2 / tar.xz / exe 自解压)
        # 先找 7z.exe,再找 7z
        $sevenZ = @(
            "${env:ProgramFiles}\7-Zip\7z.exe",
            "${env:ProgramFiles(x86)}\7-Zip\7z.exe"
            '7z'   # PATH 里的
        ) | Where-Object { $_ -and (Get-Command $_ -ErrorAction SilentlyContinue) } | Select-Object -First 1

        if ($sevenZ) {
            $cmd = "& `"$sevenZ`" x `"$Path`" -o`"$outFolder`" -y"
            $null = Invoke-Expression $cmd
            if ($LASTEXITCODE -eq 0) {
                Write-Host "✅ 已解压:$outFolder" -ForegroundColor Green
                return
            }
        }

        Write-Error "无法解压 $Path,请确认格式或安装 7-Zip。"
    }
}

<#
.SYNOPSIS
  根据镜像列表尝试下载,失败给出离线说明
#>

function Save-FileWithFallback {
    param(
        [string]$SavePath,
        [uri[]]  $MirrorList,
        [string]$ExpectedHash,
        [ValidateSet('SHA256', 'SHA1', 'MD5')]
        [string]$HashAlgo = 'SHA256'
    )

    # 本地已存在且哈希正确 => 直接返回
    if (Test-Path $SavePath) {
        $localHash = (Get-FileHash $SavePath -Algorithm $HashAlgo).Hash
        if ($localHash -eq $ExpectedHash) {
            Write-Verbose "文件已存在且校验通过:$SavePath"
            return
        }
        Write-Verbose "本地文件哈希不符,将重新下载"
    }

    foreach ($url in $MirrorList) {
        try {
            Write-Host "正在从镜像下载:$url"
            Save-File -TargetURL $url -SavePath $SavePath -ErrorAction Stop
            $downloadedHash = (Get-FileHash $SavePath -Algorithm $HashAlgo).Hash
            if ($downloadedHash -eq $ExpectedHash) {
                Write-Verbose "下载完成且校验通过"
                return
            }
            Write-Warning "下载成功但哈希不符,尝试下一个镜像"
        }
        catch {
            Write-Verbose "镜像 $url 失败:$($_.Exception.Message)"
        }
    }

    # 全部失败 => 抛出友好信息
    throw @"
所有镜像均无法下载,请检查网络或代理设置。
你也可以手动把文件放到:
   $SavePath
并确保 SHA256 值为:
   $ExpectedHash
"@

}
# ---- SystemPath.ps1 ----
# param(
# [Parameter(Mandatory)]
# [string]$ShimDir
# )

# $identity = [System.Security.Principal.WindowsIdentity]::GetCurrent()
# $principal = New-Object System.Security.Principal.WindowsPrincipal($identity)

# if (-not($principal.IsInRole([System.Security.Principal.WindowsBuiltInRole]::Administrator))) {
# Write-Error "需要管理员权限"
# exit 1
# }
# Write-Host "正在写入系统PATH,请稍后" -ForegroundColor Cyan
# # 1) 先清理用户级 PATH(如果存在)
# $userPath = [Environment]::GetEnvironmentVariable('PATH', 'User') -split ';'
# $userPath = $userPath | Where-Object { $_ -notmatch '\\.popy\\shims\\?$' }
# [Environment]::SetEnvironmentVariable('PATH', ($userPath -join ';'), 'User')

# $userPath = [Environment]::GetEnvironmentVariable('PATH', 'Machine') -split ';'
# $userPath = $userPath | Where-Object { $_ -notmatch '\\.popy\\shims\\?$' }
# [Environment]::SetEnvironmentVariable('PATH', ($userPath -join ';'), 'Machine')

# # 2) 再写入系统级 PATH(置顶)
# $sysPath = [Environment]::GetEnvironmentVariable('PATH', 'Machine') -split ';'
# $sysPath = @($ShimDir) + ($sysPath | Where-Object { $_ -notmatch '\\.popy\\shims\\?$' })
# [Environment]::SetEnvironmentVariable('PATH', ($sysPath -join ';'), 'Machine')
# ---- Utils.ps1 ----
function Test-TakeOver {
    param(
    )
    $configPath = Get-ConfigPath
    $shimsPath = Join-Path -Path $configPath -ChildPath 'shims'
    $shimPath = Join-Path $shimsPath "python.shim"
    
    if (-not (Test-Path $shimPath)) {
        return $false
    }
    $content = Get-Content -Path $shimPath -Raw
    if ($null -eq $content) {
        return $false
    }
    return $true
}

function Test-IsInit {
    param(
    )
    $configPath = Get-ConfigPath
    $configFile = Join-Path $configPath 'config.json'
    if (-not (Test-Path $configFile)) {
        return $false
    }
    return $true
}
# ---- Activate.ps1 ----
function Invoke-Activate {
    param(
        [Parameter(Mandatory = $false)]
        [string]$Venv
    )


    $storePath = Get-StorePath
    $venvsPath = Join-Path -Path $storePath -ChildPath "venvs"
    
    if (-not $Venv) {
        $executeVenvPath = Join-Path -Path $PWD.Path -ChildPath ".venv"
    }
    else {
        $executeVenvPath = Join-Path -Path $venvsPath -ChildPath $Venv
    }
    
    if (-not(Test-Path $executeVenvPath)) {
        Write-Host "没有指定的虚拟环境,请使用popy create创建" -ForegroundColor Red
        return
    }
    $specifiedToActivateVenvPath = Join-Path -Path $executeVenvPath -ChildPath "Scripts\activate.ps1"
    if (-not(Test-Path $specifiedToActivateVenvPath)) {
        Write-Host "虚拟环境可能没有正确的创建,请检查虚拟环境的文件结构,必要时可以重新创建" -ForegroundColor Red
        return
    }
    & $specifiedToActivateVenvPath
    if ($?) {
        Write-Host  $envName "虚拟环境已激活" -ForegroundColor Cyan
    }
    else {
        Write-Host "激活失败" -ForegroundColor Red
    }

}
# ---- Add.ps1 ----
function Invoke-Add {
    param(
        [Parameter(Mandatory = $false)]
        [string]$Package,

        [Parameter(Mandatory = $false)]
        [switch]$Force
    )
    if (-not $Package) {
        Write-Host "没有输入包名称或者requirements.txt" -ForegroundColor Red
        return
    }
    $isVirtualEnv = [bool]($env:VIRTUAL_ENV)
    if (-not $isVirtualEnv) {
        if (-not $Force) {
            Write-Host "当前不在虚拟环境中,请使用-f参数强制安装。" -ForegroundColor Yellow
            return
        }
    }
            
    # if (Test-Path $Package) {
    # pip install -r $Package -i https://pypi.tuna.tsinghua.edu.cn/simple
    # } else {
    # pip install -i https://pypi.tuna.tsinghua.edu.cn/simple $Package
    # }

    $cfg = Get-Configs

    $pipSource = $cfg.PipSources.($cfg.PipSource)
            
    if (Test-Path $Package) {
        $pipArgs = @("install", "-r", $Package, "-i", $pipSource)
    }
    else {
        $pipArgs = @("install", "-i", $pipSource, $Package)
    }


    # # $pipArgs = @("install", "-i", $tsinghuaMirror, $Package)
    # if (-not($args.Count -eq 0)) {
    # $pipArgs = $pipArgs + $args
    # }
    Write-Host "pip $pipArgs" -ForegroundColor Red
    Start-Process -FilePath "pip" -ArgumentList $pipArgs -NoNewWindow -Wait
}
# ---- Cd.ps1 ----
function Invoke-Cd {
    param(
        [Parameter(Mandatory = $false)]
        [string]$Path


    )

    if (-not $Path) {
        Write-Host "需要指定你要去哪里" -ForegroundColor Red
        return
    }

    if ($Path -notin 'p', 'v', 'r') { 
        Write-Host "函数内部的cd命令,只是带你快速到达versions,venvs以及requirements文件夹" -ForegroundColor Green
        Write-Host "popy cd p | popy cd v | popy cd r " -ForegroundColor Green
        return 
    }


    $storePath = Get-StorePath
    $versionsPath = Join-Path -Path $storePath -ChildPath "versions"
    $venvsPath = Join-Path -Path $storePath -ChildPath "venvs"
    $requirementsPath = Join-Path -Path $storePath -ChildPath "requirements"

    New-Directory -checkedPath $versionsPath -description "versions path"
    New-Directory -checkedPath $venvsPath -description "venvs path"
    New-Directory -checkedPath $requirementsPath -description "requirements path"
    
    if ($Path -eq 'p') {
        if (Test-Path $versionsPath) {
            Set-Location -Path $versionsPath
            Write-Host "当前路径已切换到versions" -ForegroundColor Green
        }
    }
    if ($Path -eq 'v') {
        if (Test-Path $venvsPath) {
            Set-Location -Path $venvsPath
            Write-Host "当前路径已切换到venvs" -ForegroundColor Green
        }
    }
    if ($Path -eq 'r') {
        if (Test-Path $requirementsPath) {
            Set-Location -Path $requirementsPath
            Write-Host "当前路径已切换到requirements" -ForegroundColor Green
        }
    }


}
# ---- Create.ps1 ----
function Invoke-Create {
    param(
        [Parameter(Mandatory = $false)]
        [string]$venvName,
        [Parameter(Mandatory = $false)]
        [string]$Version
    )

    $storePath = Get-StorePath
    $versionsPath = Join-Path -Path $storePath -ChildPath "versions"
    $venvsPath = Join-Path -Path $storePath -ChildPath "venvs"

    $configPath = Get-ConfigPath
    $shimsPath = Join-Path -Path $configPath -ChildPath 'shims'
    # $shimPath = Join-Path $shimsPath "python.shim"
    
    
    
    if ($venvName) {
        $executeVenvPath = Join-Path -Path $venvsPath -ChildPath $venvName
        if ($Version) {
            $pythonExePath = "$versionsPath\$Version\python.exe"
            if (-not(Test-Path $pythonExePath)) {
                Write-Host "并没有安装$Version 版本的python,自动下载安装" -ForegroundColor Red
                Invoke-Install $Version
            }
            
        }
        else {
            $isTakeOver = Test-TakeOver
            if (-not $isTakeOver) {
                Write-Host "popy 还没有接管您的python环境,请使用popy use 来设置您的python版本"
                Write-Host "或使用popy create -v 参数指定python版本"
                return
            }
            $pythonExePath = Join-Path $shimsPath "python.exe"
        }
    }
    else {
        $executeVenvPath = Join-Path -Path $PWD.Path -ChildPath ".venv"
        if ($Version) {
            $pythonExePath = "$versionsPath\$Version\python.exe"
            if (-not(Test-Path $pythonExePath)) {
                Write-Host "并没有安装$Version 版本的python,自动下载安装" -ForegroundColor Red
                Invoke-Install $Version
            }
        }
        else {
            $isTakeOver = Test-TakeOver
            if (-not $isTakeOver) {
                Write-Host "popy 还没有接管您的python环境,请使用popy use 来设置您的python版本"
                Write-Host "或使用popy create -v 参数指定python版本"
                return
            }
            $pythonExePath = Join-Path $shimsPath "python.exe"
        }
    }
    
    
    $versionname = & $pythonExePath -V 2>&1        # -V 输出版本,2>&1 把可能的 stderr 合并到 stdout
    $versionname = $versionname.Trim()         # 去掉首尾空格/换行

    if (Test-Path $executeVenvPath) {
        Write-Host "$executeVenvPath 虚拟环境已存在,手动删除后再重新创建" -ForegroundColor Red
        return
    }
    else {
        Write-Host "版本 $versionname 虚拟环境正在创建" -ForegroundColor Cyan
        & $pythonExePath -m venv $executeVenvPath
        if ($?) {
            Write-Host "$executeVenvPath 虚拟环境创建完成" -ForegroundColor Cyan
        }
        else {
            Write-Host "创建虚拟环境失败,请检查 Python 解释器路径或权限问题" -ForegroundColor Red
        }
    }
}

# ---- Deactivate.ps1 ----
function Invoke-Deactivate {
    param(
        
    )
    if (Test-Path "function:deactivate") {
        deactivate
        Write-Host "虚拟环境已取消激活"
    }
    else {
        Write-Host "没有虚拟环需要取消激活" -ForegroundColor Red
    }
}
# ---- Freeze.ps1 ----
function Invoke-Freeze {
    param(
        [Parameter(Mandatory = $false)]
        [string]$TxtName,
        [Parameter(Mandatory = $false)]
        [switch]$Force
    )


    if (-not $env:VIRTUAL_ENV) {
        Write-Host "当前并没有激活的虚拟环境,请激活虚拟环境再导出虚拟环境的包名" -ForegroundColor Red
        return
    }

    $storePath = Get-StorePath
    $requirementsPath = Join-Path -Path $storePath -ChildPath "requirements"
    New-Directory -checkedPath $requirementsPath -description "requirements path"
                
    if ($TxtName) {
        $TxtName = $TxtName -replace '\.txt$', ''   # 先去掉可能多余的 .txt
        $requirementsFile = Join-Path -Path $requirementsPath -ChildPath "$TxtName.txt"

        if (Test-Path $requirementsFile) {
            if (-not $Force) {
                Write-Host "当前虚拟环境的依赖需求文件,如需覆盖请使用-f参数强制覆盖" -ForegroundColor Red
                return
            }
        }
    }
    else {
        $requirementsFile = Join-Path -Path $PWD.Path -ChildPath "requirements.txt"
    }

    pip freeze > $requirementsFile
    if ($?) {
        Write-Host "requirements 文件已导出到 $requirementsFile" -ForegroundColor Green
    }
    else {
        Write-Host "导出 requirements 文件失败。" -ForegroundColor Red
    }

}
# ---- GistPath.ps1 ----
function Invoke-Test {
    param(
        [Parameter(Mandatory = $false)]
        [string]$TxtName
    )

    if (-not $env:VIRTUAL_ENV) {
        Write-Host "需要先激活一个虚拟环境。" -ForegroundColor Red
        return
    }

    if (-not $subcommand) {
        Write-Host "请输入一个路径" -ForegroundColor Red
        return
    }

    $currentVenvPath = $env:VIRTUAL_ENV

    $sitePackagesPath = Join-Path $currentVenvPath "Lib\site-packages"
    $pthFileName = "zzz_gistpath.pth"
    $filePath = Join-Path $sitePackagesPath $pthFileName

    $fileContent = $subcommand

    if (-not (Test-Path -Path $sitePackagesPath)) {
        Write-Host "'Lib\site-packages' 文件夹在当前虚拟环境中并不存在,可以选择手动创建" -ForegroundColor Yellow
        return
    }

    if (-not (Test-Path -Path $filePath)) {
        try {
            $fileContent | Set-Content -Path $filePath -Force
            Write-Host "文件 '$pthFileName' 已经创建在: $filePath" -ForegroundColor Green
            Write-Host "路径 '$fileContent' 已写入: $filePath" -ForegroundColor Green
        }
        catch {
            Write-Host "创建写入文件失败. Error: $_" -ForegroundColor Red
        }
    }
    else {
        try {
            $fileContent | Add-Content -Path $filePath -Force
            Write-Host "路径'$fileContent'已经添加到 '$pthFileName'." -ForegroundColor Green
        }
        catch {
            Write-Host "路径添加失败. Error: $_" -ForegroundColor Red
        }
    }
}
# ---- Help.ps1 ----


<#
.SYNOPSIS
    popy – 极简 PowerShell Python 版本管理器
 
.DESCRIPTION
    popy 让你在同一台机器上轻松安装、切换、创建和删除多个 Python 解释器与虚拟环境,
    并支持通过“需求文件”在不同项目之间快速重建一致的依赖环境。
 
    所有数据默认存放在 $HOME\.popy 下,可通过环境变量 $POPPY_HOME 调整。
 
.PARAMETER Command
    要执行的主命令(init / install / use / create / activate …)。
 
.PARAMETER Rest
    命令后的所有剩余参数。
 
.PARAMETER Force
    -f 强制覆盖已存在的文件或目录。
 
.PARAMETER Version
    -v 指定 Python 版本(仅部分命令支持)。
 
.PARAMETER System
    -s 将 python/python3 等全局命令注册到系统 PATH(需要管理员权限)。
 
.EXAMPLE
    popy init
    初始化 popy 目录结构并下载版本索引。
 
    popy install 3.11.4
    下载并安装 CPython 3.11.4。
 
    popy use 3.11.4
    把 3.11.4 设为当前全局默认解释器(写入 PATH)。
 
    popy create myproj
    在当前目录创建名为 myproj 的虚拟环境(默认使用当前全局解释器)。
 
    popy create myproj -v 3.10.12
    使用 3.10.12 创建虚拟环境。
 
    popy activate myproj
    激活虚拟环境 myproj(支持 PowerShell/Command Prompt)。
 
    popy deactivate
    退出当前虚拟环境。
 
    popy freeze myproj
    将虚拟环境 myproj 的依赖锁定为 requirements.txt。
 
    popy requirements myproj
    根据当前目录下的 requirements.txt 重建虚拟环境 myproj 的依赖。
 
    popy cd v
    直接 cd 到当前虚拟环境所在目录。
 
.LINK
    https://github.com
#>


function Invoke-Help {
    Write-Host @"
init 初始化软件
update 更新可安装python版本列表
install <version> 安装指定版本python
sources <source_name=source_url> 查看设置python安装包所有下载源
pipsources <source_name=source_url> 查看设置pip所有下载源
source <sourcename> 查看设置当前安装包下载源
pipsource <sourcename> 查看设置当前pip下载源
use|global|g <version/venvname> 查看设置当前全局python环境
pythons|ps 查看当前popy安装的所有python版本
create|c <venvname> <-v version> 创建虚拟环境,默认使用当前python全局版本,可以指定一个popy已安装的版本
activate|a <venvname> 激活一个虚拟环境,无参数时尝试查找项目内的.venv虚拟环境
deactivate|d 取消激活当前虚拟环境
venvs|vs 查看所有popy管理的虚拟环境名称
removevenv|rv <venvname> 删除一个虚拟环境,无参数时,尝试删除项目内的.venv虚拟环境
requirements|rs popy导出的requirements文件的名称
freeze|rf <file_name> 导出requirements文件到当前目录,如给定文件名则保存到popystore文件夹
removerequirement|rr <file_name> 删除一个venvs内的requirements文件
cd p|v|r 切换到python版本路径,虚拟环境路径,requirements路径
 
"@
 -ForegroundColor Cyan
}
# gistpath <path> 给虚拟环境添加一个外部依赖库的位置,即给虚拟环境添加一个pth文件,以方便从系统中的一个指定位置import已有的代码
# ---- Init.ps1 ----
function Invoke-Init {
    param(
        # [Parameter(Mandatory = $false)]
        # [switch]$System,

        [Parameter(Mandatory = $false)]
        [switch]$Force
    )

    
    $popyConfigPath = Get-ConfigPath
    New-Directory -checkedPath $popyConfigPath -description "config path"
    $configFile = Join-Path $popyConfigPath 'config.json'

    # 判断是否已存在
    if (Test-Path $configFile) {
        if (-not $Force) {
            Write-Host "已存在初始化文件,无需初始化,如确定需要重置软件,请加-f参数" -ForegroundColor Yellow
            return
        }
        else {
            $choice = Read-Host "当前环境曾始化过,确定要重新初始化吗?(y/n)"
            if ($choice -notmatch '^(y|yes)$') {
                Write-Host "已取消初始化操作。" -ForegroundColor Yellow
                return
            }
        }
    }

    $cfg = Get-Configs


    $defaultStore = Join-Path $popyConfigPath 'PopyStore'

    do {
        $pathInput = Read-Host "请输入仓库路径(python安装文件、全局虚拟环境等)的存放位置,直接回车仓库将创建在用户目录 $popyConfigPath 下"

        # 1. 直接回车 → 用默认
        if ([string]::IsNullOrWhiteSpace($pathInput)) {
            $storePath = $defaultStore
            break
        }

        # 2. 必须是绝对路径
        if (-not [System.IO.Path]::IsPathRooted($pathInput)) {
            Write-Host "请输入绝对路径(如 C:\MyFolder)!"
            continue
        }

        # 3. 如果该路径已存在,则必须是个目录
        if (Test-Path -LiteralPath $pathInput -PathType Leaf) {
            Write-Host "输入的路径是一个文件路径,而不是目录路径。请重新输入!" -ForegroundColor Red
            continue
        }

        # 4. 通过校验,拼最终仓库目录
        $storePath = Join-Path $pathInput 'PopyStore'
        break
    } while ($true)

    # 5. 检测旧仓库提示
    if ($null -ne $cfg -and $storePath -ne $cfg.storePath) {
        Write-Host "仓库目录已改变,如确定不再使用请手动删除 $($cfg.storePath)"
    }

    New-Directory -checkedPath $storePath -description "仓库文件夹"

    # 生成默认配置
    $defaultConfig = @{
        storePath       = $storePath
        DownloadSource  = 'huawei'
        DownloadSources = @{
            default = 'https://www.python.org/ftp/python/'
            huawei  = 'https://mirrors.huaweicloud.com/python/'
        }
        PipSource       = 'tuna'
        PipSources      = @{
            default = 'https://pypi.org/simple'
            tuna    = 'https://pypi.tuna.tsinghua.edu.cn/simple'
            ali     = 'https://mirrors.aliyun.com/pypi/simple/'
            tencent = 'https://mirrors.cloud.tencent.com/pypi/simple'
        }
        versionName     = 'system'
        versionType     = 'version'
    } | ConvertTo-Json -Depth 3

    # 写文件
    try {
        $defaultConfig | Out-File -FilePath $configFile -Encoding utf8 -Force
        Write-Host "config.json 已生成/覆盖:$configFile" -ForegroundColor Green
    }
    catch {
        Write-Error "写入 config.json 失败:$_"
        # return
    }

    ###################
    $ShimDir = Join-Path $popyConfigPath 'shims'
    
    $identity = [Security.Principal.WindowsIdentity]::GetCurrent()
    $principal = New-Object Security.Principal.WindowsPrincipal($identity)
    # 1) 当前已是管理员 → 直接写系统 PATH
    if ($principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) {
        Write-Host "当前是管理员权限,正在写入系统PATH,请稍后" -ForegroundColor Cyan
        # 1) 先清理用户级 PATH(如果存在)
        $userPath = [Environment]::GetEnvironmentVariable('PATH', 'User') -split ';'
        $userPath = $userPath | Where-Object { $_ -notmatch '\\.popy\\shims\\?$' }
        [Environment]::SetEnvironmentVariable('PATH', ($userPath -join ';'), 'User')

        $sysPath = [Environment]::GetEnvironmentVariable('PATH', 'Machine') -split ';'
        $sysPath = $sysPath | Where-Object { $_ -notmatch '\\.popy\\shims\\?$' }
        [Environment]::SetEnvironmentVariable('PATH', ($sysPath -join ';'), 'Machine')

        # 2) 再写入系统级 PATH(置顶)
        $sysPath = [Environment]::GetEnvironmentVariable('PATH', 'Machine') -split ';'
        $sysPath = @($ShimDir) + ($sysPath | Where-Object { $_ -notmatch '\\.popy\\shims\\?$' })
        [Environment]::SetEnvironmentVariable('PATH', ($sysPath -join ';'), 'Machine')
    }
    # 2) 不是管理员 → 提权重跑当前脚本
    else {
        Write-Host "当前不是管理员权限,正在写入用户PATH,请稍后" -ForegroundColor Cyan
        $userPath = [Environment]::GetEnvironmentVariable('PATH', 'User') -split ';'
        $userPath = $userPath | Where-Object { $_ -notmatch '\\.popy\\shims\\?$' }
        [Environment]::SetEnvironmentVariable('PATH', ($userPath -join ';'), 'User')

        $ShimDir = Join-Path $popyConfigPath 'shims'
        $userPath = [Environment]::GetEnvironmentVariable('PATH', 'User') -split ';'
        $userPath = @($ShimDir) + ($userPath | Where-Object { $_ -notmatch '\\.popy\\shims\\?$' })
        [Environment]::SetEnvironmentVariable('PATH', ($userPath -join ';'), 'User')
        Write-Host "已把 $ShimDir 加到用户 PATH 最前面" -ForegroundColor Green
        Write-Host "因为系统调用顺序,如果系统全局安装过python,只是添加到用户PATH可能会影响实际使用效果。如本机无其他全局安装的python可以忽略。" -ForegroundColor Green
        Write-Host "如需重新添加到系统PATH,用管理员权限重新运行 popy init -f" -ForegroundColor Green
    }
    ##################


    
    # $toolsPath = Join-Path $popyConfigPath 'tools'
    # New-Directory -checkedPath $toolsPath -description "tools path"
    # $wixDownloadUrl = "https://github.com/wixtoolset/wix3/releases/download/wix3112rtm/wix311-binaries.zip"
    # $wixZipFile = Join-Path $toolsPath 'wix311-binaries.zip'
    # $wixExpandPath = Join-Path $toolsPath 'wix'
    
    # $shimDownloadUrl = "https://github.com/ScoopInstaller/Shim/releases/download/v1.1.0/shim-1.1.0.zip"
    # $shimZipFile = Join-Path $toolsPath 'shim-1.1.0.zip'
    # $shimExpandPath = Join-Path $toolsPath 'shim'

    # . "$PSScriptRoot\..\lib\SaveFile.ps1"
    # try {
    # if (-not (Test-Path $wixZipFile)) {
    # Save-File -TargetURL $wixDownloadUrl -SavePath $wixZipFile
    # }

    # if (-not (Test-Path $shimZipFile)) {
    # Save-File -TargetURL $shimDownloadUrl -SavePath $shimZipFile
    # }
    # Write-Host "准备解压"
    # Expand-InPlace -Path $wixZipFile -Destination $wixExpandPath -Flatten -Force
    # Expand-InPlace -Path $shimZipFile -Destination $shimExpandPath -Flatten -Force
    # }
    # catch {
    # Write-Host "步骤失败,已终止后续操作:$($_.Exception.Message)" -ForegroundColor Red
    # # 不再往下走
    # return
    # }

    # 1. 计算路径
    $toolsPath = Join-Path $popyConfigPath 'tools'
    New-Directory -checkedPath $toolsPath -description "tools path"

    # 计算哈希 Get-FileHash C:\Temp\wix311-binaries.zip -Algorithm SHA256

    $wixZipFile = Join-Path $toolsPath 'wix311-binaries.zip'
    $wixExpand = Join-Path $toolsPath 'wix'
    $wixHash = '2C1888D5D1DBA377FC7FA14444CF556963747FF9A0A289A3599CF09DA03B9E2E'  # 官方 SHA256

    $shimZipFile = Join-Path $toolsPath 'shim-1.1.0.zip'
    $shimExpand = Join-Path $toolsPath 'shim'
    $shimHash = 'C8452B3C4B8C219EDEF150CC423B0C844CB2D46381266011F6F076301E7E65D9' # 官方 SHA256

    # 2. 构造镜像列表(可随用户环境变量追加)
    $wixMirrors = @(
        'https://github.com/wixtoolset/wix3/releases/download/wix3112rtm/wix311-binaries.zip'
        'https://ghproxy.net/https://github.com/wixtoolset/wix3/releases/download/wix3112rtm/wix311-binaries.zip'
        'https://ghfast.top/https://github.com/wixtoolset/wix3/releases/download/wix3112rtm/wix311-binaries.zip'
    )
    $shimMirrors = @(
        'https://github.com/ScoopInstaller/Shim/releases/download/v1.1.0/shim-1.1.0.zip'
        'https://ghproxy.net/https://github.com/ScoopInstaller/Shim/releases/download/v1.1.0/shim-1.1.0.zip'
        'https://ghfast.top/https://github.com/ScoopInstaller/Shim/releases/download/v1.1.0/shim-1.1.0.zip'
    )

    # 3. 下载阶段
    try {
        Save-FileWithFallback -SavePath $wixZipFile  -MirrorList $wixMirrors  -ExpectedHash $wixHash
        Save-FileWithFallback -SavePath $shimZipFile -MirrorList $shimMirrors -ExpectedHash $shimHash
    }
    catch {
        Write-Host $_.Exception.Message -ForegroundColor Red
        return   # 提前终止
    }

    # 4. 解压阶段(幂等,Force 覆盖即可)
    try {
        Expand-InPlace -Path $wixZipFile  -Destination $wixExpand  -Flatten -Force
        Expand-InPlace -Path $shimZipFile -Destination $shimExpand -Flatten -Force
    }
    catch {
        Write-Host "解压失败:$($_.Exception.Message)" -ForegroundColor Red
        return
    }
}

# ---- Install.ps1 ----
# param(
# [Parameter(Mandatory = $true, Position = 0)]
# [string]$Version
# )
# Write-Host "这是 install 功能,目标版本:$Version"

function Invoke-Install {
    param(
        [Parameter(Mandatory)]
        [string]$Version,

        [Parameter(Mandatory = $false)]
        [switch]$Force
    )

    $cfg = Get-Configs
    if ($Version -eq "list") {
        $versions = $cfg.versions
        if ($null -eq $versions -or $versions.Count -eq 0) {
            Write-Host "没有任何版本信息,请使用popy update命令获取最新的可安装列表."
        }
        else {
            # 打印所有版本号
            Write-Host "可用版本:"
            foreach ($version in $versions) {
                Write-Host $version
            }
        }
        return
    }
    $requestedVersion = $Version
    $availableVersions = $cfg.versions
    if ($null -eq $availableVersions -or $availableVersions.Count -eq 0) {
        Write-Host "没有任何版本信息,请使用popy update命令获取最新的可安装列表."
        return
    }
    $url = $cfg.DownloadSources.($cfg.DownloadSource)

    # ###
    $storePath = Get-StorePath
    $downloadPath = Join-Path -Path $storePath -ChildPath "downloads"
    New-Directory -checkedPath $downloadPath -description "下载目录"

    $versionsPath = Join-Path -Path $storePath -ChildPath "versions"
    New-Directory -checkedPath $versionsPath -description "versions"
    $versionPath = Join-Path -Path $versionsPath -ChildPath $requestedVersion


    $installerPath = "$downloadPath\python-$requestedVersion-amd64.exe"
    $downloadUrl = "$url$requestedVersion/python-$requestedVersion-amd64.exe"
    if ($availableVersions -contains $requestedVersion) {
        if (-not (Test-Path -Path $installerPath)) {
            Write-Host "正在下载并安装 Python $requestedVersion..."
            Save-File -TargetURL $downloadUrl  -SavePath $installerPath
        }
        else {
            if ($Force) {
                Remove-Item -Path $installerPath -Force
                if (Test-Path -Path $installerPath) {
                    Write-Host "删除失败" -ForegroundColor Red
                    return
                }
                Write-Host "$installerPath 已删除"
                Write-Host "正在重新下载并安装 Python $requestedVersion..."
                Save-File -TargetURL $downloadUrl  -SavePath $installerPath
            }
            else {
                Write-Host "$installerPath 已存在,可以直接安装,如解压错误,可以添加参数-f强制重新下载"
            }
        }
        if (-not(Test-Path $installerPath)) {
            Write-Host "下载出错,请确认版本,或者您所下载的版本$requestedVersion,没有适合当前系统环境的安装包,请尝试降小版本,或安装其他版本" -ForegroundColor Red
            return
        }
        #####提取安装
        Write-Host "Python $requestedVersion 正在解压!" -ForegroundColor Green
        $extractPath = Join-Path -Path $downloadPath -ChildPath $requestedVersion

        $toolPath = Get-ToolsPath
        $strDirWiX = Join-Path -Path $toolPath -ChildPath 'wix'
        $darkExe = Join-Path -Path $strDirWiX -ChildPath 'dark.exe'
        New-Directory -checkedPath $extractPath -description "msi目录"

        $extractCommand = Start-Process -FilePath $darkExe -ArgumentList "-x", "`"$extractPath`"", "`"$installerPath`"" -Wait -PassThru -WindowStyle Hidden
        # 如果还弹窗 可以使用$command = "$darkExe -x `"$cachePath`" `"$installFile`""
        #Invoke-Expression $command
        if ($extractCommand.ExitCode -ne 0) {
            Write-Error "Error extracting the embedded portion from the installer."
            return -1
        }

        $extractMsiPath = Join-Path -Path $extractPath -ChildPath "AttachedContainer\*.msi"
                
        Get-ChildItem -Path $extractMsiPath | Move-Item -Destination $extractPath -Force

        Get-ChildItem -Path $extractPath -File | ForEach-Object {
            if ($_.Extension -ne ".msi" -or $_.BaseName -in @("appendpath", "launcher", "path", "pip")) {
                Remove-Item -Path $_.FullName -Force
            }
        }

        Get-ChildItem -Path $extractPath -Directory | ForEach-Object {
            Remove-Item -Path $_.FullName -Force -Recurse
        }
        
        New-Directory -checkedPath $versionPath -description "版本目录"


        $installJobs = @()
        Get-ChildItem -Path $extractPath -Filter *.msi | ForEach-Object {
            $msiFilePath = $_.FullName
            $job = Start-Job -ScriptBlock {
                param($msiFilePath, $versionPath)
                Start-Process -FilePath "msiexec" -ArgumentList "/quiet", "/a", "`"$msiFilePath`"", "TARGETDIR=`"$versionPath`"" -NoNewWindow -Wait
                if ($LASTEXITCODE -ne 0) {
                    Write-Error "Error installing $($_.BaseName) component MSI. Exit code: $LASTEXITCODE"
                }
            } -ArgumentList $msiFilePath, $versionPath
            $installJobs += $job
        }
        Write-Host "Python $requestedVersion 正在安装!" -ForegroundColor Green
        # 等待所有安装任务完成
        
        $total = $installJobs.Count
        $completed = 0

        while ($completed -lt $total) {
            $running = Get-Job -State Running
            $completed = $total - $running.Count
            $percent = [int]($completed / $total * 100)

            Write-Progress -Activity "Installing packages" `
                -Status "$completed / $total finished" `
                -PercentComplete $percent

            Start-Sleep -Milliseconds 300   # 避免 CPU 空转
        }

        # 清理作业
        $installJobs | Remove-Job -Force

        # 删除安装路径中的重复 MSI 文件
        Get-ChildItem -Path $extractPath -Filter *.msi | ForEach-Object {
            $msiPath = Join-Path -Path $versionPath -ChildPath $_.Name
            if (Test-Path -Path $msiPath) {
                Remove-Item -Path $msiPath -Force
            }
        }

        $ensurepipPath = Join-Path -Path $versionPath -ChildPath "Lib\ensurepip"
        if (Test-Path -Path $ensurepipPath) {
            # Write-Host "pip 正在安装!" -ForegroundColor Green
            $pythonExe = Join-Path -Path $versionPath -ChildPath "python.exe"
            & $pythonExe -E -s -m ensurepip -U --default-pip 2>&1 | Out-Null
            if ($LASTEXITCODE -ne 0) {
                Write-Error "Error installing pip."
                return -1
            }
        }
        #####

        Write-Host "Python $requestedVersion 安装完成!" -ForegroundColor Green
    }
}
# ---- PipSource.ps1 ----
function Invoke-PipSource {
    param(
        [Parameter(Mandatory = $false)]
        [string]$pipSource

    )
    $cfg = Get-Configs
    if (-not $pipSource) {
        $pipSource = $cfg.PipSource
        Write-Host "当前python安装源为 $pipSource"
        return
    }
    else {
        $exists = $cfg.PipSources.PSObject.Properties.Name -contains $pipSource
        if (-not $exists) {
            Write-Host "运行popy pipsources查看所有可用源,popy pipsource 设置的源必须是在pipsources内的" -ForegroundColor Red
            return
        }

        Set-Config -Key 'PipSource' -Value $pipSource
        $cfg = Get-Configs
        $pipSource = $cfg.PipSource
        Write-Host "python安装源切换为 $pipSource"
    }


}
# ---- PipSources.ps1 ----
function Invoke-PipSources {
    param(
        [Parameter(Mandatory = $false)]
        [string]$pipSourceKeyValue
    )
    $cfg = Get-Configs
    if (-not $pipSourceKeyValue) {
        $pipSources = $cfg.PipSources
        
        if ($null -eq $pipSources -or $pipSources.Count -eq 0) {
            Write-Host "没有任何安装源,请重新init,或者手动设置安装源如:popy pipsources default=https://pypi.org/simple"
        }
        else {
            # 打印所有版本号
            Write-Host "可用安装源:"
            foreach ($name in $pipSources.PSObject.Properties.Name) {
                $url = $pipSources.$name
                Write-Host "$name=$url"
            }
        }
        return
    }
    else {
        if ($pipSourceKeyValue -match "^(.+?)=(.+)$") {
            $key = $matches[1].Trim()
            $value = $matches[2].Trim()
            # 检查是否已存在键,然后更新或添加
            $old = $cfg.PipSources
            $ht = [ordered]@{}
            $old.PSObject.Properties.Name |
            Sort-Object -Descending |
            ForEach-Object { $ht[$_] = $old.$_ }

            # 2. 新增键自动排在最后
            $ht[$key] = $value

            # 3. 再转回 PSCustomObject
            $sourcesUpdated = [PSCustomObject]$ht
            Set-Config -Key 'PipSources' -Value $sourcesUpdated

            $cfg = Get-Configs
            $pipSources = $cfg.PipSources
            Write-Host "可用安装源:"
            foreach ($name in $pipSources.PSObject.Properties.Name) {
                $url = $pipSources.$name
                Write-Host "$name=$url"
            }
        }
        else {
            Write-Host "无效的子命令格式。请使用 'pipsources <key>=<value>'。" -ForegroundColor Red
        }
    }


}
# ---- RemoveRequirement.ps1 ----
function Invoke-RemoveRequirement {
    param(
        [Parameter(Mandatory = $false)]
        [string]$TxtName
    )

    $storePath = Get-StorePath
    $requirementsPath = Join-Path -Path $storePath -ChildPath "requirements"
    New-Directory -checkedPath $requirementsPath -description "requirements path"


    if ($TxtName) {
        $TxtName = $TxtName -replace '\.txt$', ''   # 先去掉可能多余的 .txt
        $requirementsFile = Join-Path -Path $requirementsPath -ChildPath "$TxtName.txt"
    }
    else {
        $requirementsFile = Join-Path -Path $PWD.Path -ChildPath "requirements.txt"
    }

            
    if (Test-Path $requirementsFile) {
        # $confirm = Read-Host "确定要删除$filename?(Y/N)"
        # if ($confirm -ne 'Y' -and $confirm -ne 'y') {
        # Write-Host "删除操作已取消!" -ForegroundColor Yellow
        # return
        # }
        Remove-Item -Path $requirementsFile -Force
        if ($?) {
            Write-Host "$requirementsFile 依赖包文件列表删除完毕。" -ForegroundColor Green
        }
        else {
            Write-Host "依赖包文件列表删除失败。" -ForegroundColor Red
        }
    }
    else {
        Write-Host "$TxtName.txt不存,在无需删除" -ForegroundColor Yellow
        Write-Host "使用 popy rs 查看所有存档的 requirements.txt 文件" -ForegroundColor Green
    }
}
# ---- RemoveVenv.ps1 ----
function Invoke-RemoveVenv {
    param(
        [Parameter(Mandatory = $false)]
        [string]$Venv
    )

    $storePath = Get-StorePath
    $venvsPath = Join-Path -Path $storePath -ChildPath "venvs"
    
    if (-not $Venv) {
        $executeVenvPath = Join-Path -Path $PWD.Path -ChildPath ".venv"
    }
    else {
        $executeVenvPath = Join-Path -Path $venvsPath -ChildPath $Venv
    }

    
            
    if (Test-Path $executeVenvPath) {
        if ($env:VIRTUAL_ENV -eq $executeVenvPath) {
            Write-Host "当前激活的虚拟环境与要删除的虚拟环境相同,先停用再删除。" -ForegroundColor Yellow
            if (Test-Path "function:deactivate") {
                deactivate
                Write-Host "虚拟环境已停用。" -ForegroundColor Green
            }
            else {
                Write-Host "无法停用虚拟环境,请检查环境变量或手动停用。" -ForegroundColor Red
                return
            }
        }
        $confirm = Read-Host "确定要删除虚拟环境(Y/N)"
        if ($confirm -ne 'Y' -and $confirm -ne 'y') {
            Write-Host "删除操作已取消!" -ForegroundColor Yellow
            return
        }
        Remove-Item -Path $executeVenvPath -Recurse -Force
        if ($?) {
            Write-Host "$executeVenvPath 虚拟环境已成功删除。" -ForegroundColor Green
        }
        else {
            Write-Host "删除虚拟环境失败。" -ForegroundColor Red
        }
    }
    else {
        Write-Host "$subcommand 虚拟环境不存在,无需删除。" -ForegroundColor Yellow
    }


}
# ---- Requirements.ps1 ----
function Invoke-Requirements {
    param(
        [Parameter(Mandatory = $false)]
        [string]$Pattern
    )


    $storePath = Get-StorePath
    $requirementsPath = Join-Path -Path $storePath -ChildPath "requirements"
    New-Directory -checkedPath $requirementsPath -description "requirements path"

    $requirementsFiles = Get-ChildItem -Path $requirementsPath -Filter "*.txt"
    if ($requirementsFiles.Count -eq 0) {
        Write-Host "当前没有任何已导出的requirement.txt文件" -ForegroundColor Red
        return
    }

    if ($Pattern) {
        $filteredRequirements = $requirementsFiles | Where-Object { $_.Name -like "*$Pattern*" }
        if ($filteredRequirements.Count -eq 0) {
            Write-Host "没有找到匹配$Pattern 的requirement.txt文件" -ForegroundColor Yellow
        }
        else {
            $filteredRequirements | ForEach-Object {
                Write-Host $_.Name -ForegroundColor Cyan
            }
        }

    }
    else {
        $requirementsFiles | ForEach-Object {
            Write-Host $_.Name -ForegroundColor Cyan
        }
        Write-Host "通过添加额外的参数可以过滤所带有需要的字样的结果,如popy rs main 就会查找所有带有main字样的文件" -ForegroundColor Yellow
    }
}
# ---- Source.ps1 ----
function Invoke-Source {
    param(
        [Parameter(Mandatory = $false)]
        [string]$source

    )
    $cfg = Get-Configs
    if (-not $source) {
        $downloadSource = $cfg.DownloadSource
        Write-Host "当前python安装源为 $downloadSource"
        return
    }
    else {
        $exists = $cfg.DownloadSources.PSObject.Properties.Name -contains $source
        if (-not $exists) {
            Write-Host "运行popy sources查看所有可用源,popy source 设置的源必须是在sources内的" -ForegroundColor Red
            return
        }

        Set-Config -Key 'DownloadSource' -Value $source
        $cfg = Get-Configs
        $downloadSource = $cfg.DownloadSource
        Write-Host "python安装源切换为 $downloadSource"
    }


}
# ---- Sources.ps1 ----
function Invoke-Sources {
    param(
        [Parameter(Mandatory = $false)]
        [string]$sourceKeyValue
    )
    $cfg = Get-Configs
    if (-not $sourceKeyValue) {
        $sources = $cfg.DownloadSources
        
        if ($null -eq $sources -or $sources.Count -eq 0) {
            Write-Host "没有任何安装源,请重新下载,或者手动设置安装源如:popy sources default=https://www.python.org/ftp/python/"
        }
        else {
            # 打印所有版本号
            Write-Host "可用安装源:"
            foreach ($name in $sources.PSObject.Properties.Name) {
                $url = $sources.$name
                Write-Host "$name=$url"
            }
        }
        return
    }
    else {
        if ($sourceKeyValue -match "^(.+?)=(.+)$") {
            $key = $matches[1].Trim()
            $value = $matches[2].Trim()
            # 检查是否已存在键,然后更新或添加
            $old = $cfg.DownloadSources
            $ht = [ordered]@{}
            $old.PSObject.Properties.Name |
            Sort-Object -Descending |
            ForEach-Object { $ht[$_] = $old.$_ }

            # 2. 新增键自动排在最后
            $ht[$key] = $value

            # 3. 再转回 PSCustomObject
            $sourcesUpdated = [PSCustomObject]$ht
            Set-Config -Key 'DownloadSources' -Value $sourcesUpdated

            $cfg = Get-Configs
            $sources = $cfg.DownloadSources
            Write-Host "可用安装源:"
            foreach ($name in $sources.PSObject.Properties.Name) {
                $url = $sources.$name
                Write-Host "$name=$url"
            }
        }
        else {
            Write-Host "无效的子命令格式。请使用 'sources <key>=<value>'。" -ForegroundColor Red
        }
    }


}
# ---- test.ps1 ----
function Invoke-Test {
    param(
        [Parameter(Mandatory = $false)]
        [switch]$System,

        [Parameter(Mandatory = $false)]
        [switch]$Force
    )

}
# ---- Update.ps1 ----
function Invoke-Update {
    param(
        # [Parameter(Mandatory = $false)]
        # [switch]$System,

        # [Parameter(Mandatory = $false)]
        # [switch]$Force
    )
    $cfg = Get-Configs
    # $sourceName = $cfg.DownloadSource
    $url = $cfg.DownloadSources.($cfg.DownloadSource)
    Write-Host $url
            

    # 获取网页内容
    if ($PSVersionTable.PSVersion.Major -le 5) {
        [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
    }
    $response = Invoke-WebRequest -Uri $url -UseBasicParsing

    # Write-Host $response
    # 提取版本信息

    $versions = $response.Links |
    Where-Object { $_.href -match '^\d+\.\d+(?:\.\d+)?/$' } |
    ForEach-Object { $_.href.TrimEnd('/') } |
    Where-Object { [version]$_ -ge [version]'3.7' } |
    Sort-Object { [version]$_ } -Descending

    if ($null -eq $versions -or $versions.Count -eq 0) {
        Write-Host "没有找到任何python版本,请检查网址是否正确."
    }

    # 打印版本列表到控制台
    Write-Host "发现可用版本:"
    foreach ($version in $versions) {
        Write-Host $version
    }

    # # 保存版本列表到文件
    Set-Config -Key 'versions' -Value $versions

    Write-Host "安装列表已保存到 $pythonVersionsFile"
}
# ---- Use.ps1 ----
function Invoke-Use {
    param(
        [Parameter(Mandatory = $false)]
        [string]$UseVersion
    )
    $cfg = Get-Configs
    $versionType = $cfg.versionType
    $versionName = $cfg.versionName

    


    
    if (-not $UseVersion) {
        $isTakeOver = Test-TakeOver
        if (-not $isTakeOver) {
            Write-Host "popy 还没有接管您的python环境,请使用popy use <version|venv>来设置您的python版本"
            Write-Host "或使用popy create -v 参数指定python版本"
            return
        }
        $configPath = Get-ConfigPath
        $shimsPath = Join-Path -Path $configPath -ChildPath 'shims'
        $pythonExePath = Join-Path $shimsPath "python.exe"

        $version = & $pythonExePath -V 2>&1        # -V 输出版本,2>&1 把可能的 stderr 合并到 stdout
        $version = $version.Trim()         # 去掉首尾空格/换行
        if ($versionName -eq "system") {
            Write-Host "全局版本为系统默认版本" -ForegroundColor Green
        }
        elseif ($versionType -eq "version") {
            Write-Host "全局版本为versions $versionName ,python版本$version " -ForegroundColor Green
        }
        elseif ($versionType -eq "venv") {
            Write-Host "全局版本为venv $versionName ,python版本$version " -ForegroundColor Green
        }
        return
    }

    $configPath = Get-ConfigPath
    $toolsPath = Join-Path -Path $configPath -ChildPath 'tools'
    $scriptsVersionPath = Join-Path -Path $configPath -ChildPath 'shims'

    $binPath = Join-Path -Path $toolsPath -ChildPath 'shim'
    # 检查$scriptsVersionPath下有没有python.exe和python.shim
    # exe没有就从$binPath里复制一份过来
            
            
    $exePath = Join-Path $scriptsVersionPath "python.exe"
    $shimPath = Join-Path $scriptsVersionPath "python.shim"
    $pipexePath = Join-Path $scriptsVersionPath "pip.exe"
    $pipshimPath = Join-Path $scriptsVersionPath "pip.shim"



    $storePath = Get-StorePath
    $versionsPath = Join-Path -Path $storePath -ChildPath "versions"
    $venvPath = Join-Path -Path $storePath -ChildPath "venvs"

    New-Directory -checkedPath $scriptsVersionPath -description "shims path"
    New-Directory -checkedPath $versionsPath -description "versions path"
    New-Directory -checkedPath $venvPath -description "venv path"


    if (-not (Test-Path $exePath)) {
        $sourceExe = Join-Path $binPath "shim.exe"
        if (Test-Path $sourceExe) {
            Copy-Item -Path $sourceExe -Destination $exePath -Force
            Write-Host "已复制 python.exe 到 $exePath"
        }
        else {
            Write-Warning "源文件不存在:$sourceExe ,请手动下载,或popy init自动下载"
        }
    }

    if (-not (Test-Path $shimPath)) {
        New-ShimFile $shimPath
    }

    if (-not (Test-Path $pipexePath)) {
        $sourceExe = Join-Path $binPath "shim.exe"
        if (Test-Path $sourceExe) {
            Copy-Item -Path $sourceExe -Destination $pipexePath -Force
            Write-Host "已复制 pip.exe 到 $pipexePath"
        }
        else {
            Write-Warning "源文件不存在:$sourceExe"
        }
    }

    if (-not (Test-Path $pipshimPath)) {
        New-ShimFile $pipshimPath
    }



    $wexePath = Join-Path $scriptsVersionPath "pythonw.exe"
    $wshimPath = Join-Path $scriptsVersionPath "pythonw.shim"

    if (-not (Test-Path $wexePath)) {
        $sourceExe = Join-Path $binPath "shim.exe"
        if (Test-Path $sourceExe) {
            Copy-Item -Path $sourceExe -Destination $wexePath -Force
            Write-Host "已复制 python.exe 到 $wexePath"
        }
        else {
            Write-Warning "源文件不存在:$sourceExe ,请手动下载,或popy init自动下载"
        }
    }

    if (-not (Test-Path $wshimPath)) {
        New-ShimFile $wshimPath
    }


    $requestedVersion = $UseVersion
    $globalVersionPath = Join-Path -Path $versionsPath -ChildPath $requestedVersion
    $globalVenvPath = Join-Path -Path $venvPath -ChildPath $requestedVersion

            

    if ($requestedVersion -eq "system") {
        $systemPython = (where.exe python) -split "`r`n" | Where-Object {
            $_ -notmatch '\\.popy\\shims\\python\.exe$'
        } | Select-Object -First 1
                
        if ($systemPython) {
            # 写入 .shim 文件
            $shimContent = "path = `"$systemPython`""
            Set-Content -Path $shimPath -Value $shimContent
            $wshimContent = $shimContent -replace 'python\.exe', 'pythonw.exe'
            Set-Content -Path $wshimPath -Value $wshimContent
            Write-Host "全局版本已切换到系统 Python:$systemPython" -ForegroundColor Green
            $pipPath = Join-Path (Split-Path $systemPython -Parent) "Scripts\pip.exe"

            if (Test-Path $pipPath) {
                $pipShimContent = "path = `"$pipPath`""
                Set-Content -Path $pipshimPath -Value $pipShimContent
                Write-Host "pip 路径已写入:$pipPath" -ForegroundColor Cyan
            }
            else {
                Write-Warning "未找到 pip.exe,跳过写入 pip 的 .shim 文件"
            }
        }
        Set-ConfigValue -FilePath $versionFile -Key "name" -Value "system"
        return
    }
    else {
        if (-not (Test-Path -Path $globalVersionPath)) {
            if (-not (Test-Path -Path $globalVenvPath)) {
                Write-Host "没有找到设置的python版本或者已经安装的虚拟环境" -ForegroundColor Red
                return
            }
        }
    }

    $checkedVersionPath = Join-Path -Path $versionsPath -ChildPath $requestedVersion
            
    if (Test-Path -Path $checkedVersionPath) {
                
        $pythonPath = Join-Path -Path $checkedVersionPath -ChildPath "python.exe"
                
        $pipPath = Join-Path -Path $checkedVersionPath -ChildPath "Scripts\pip.exe"
        $wpythonPath = Join-Path -Path $checkedVersionPath -ChildPath "pythonw.exe"

        if (Test-Path $pythonPath) {
            $pythonShimContent = "path = `"$pythonPath`""
            Set-Content -Path $shimPath -Value $pythonShimContent
            Write-Host "python 路径已写入:$pythonPath" -ForegroundColor Cyan
        }

        if (Test-Path $pipPath) {
            $pipShimContent = "path = `"$pipPath`""
            Set-Content -Path $pipshimPath -Value $pipShimContent
            Write-Host "pip 路径已写入:$pipPath" -ForegroundColor Cyan
        }

        if (Test-Path $wpythonPath) {
            $wshimContent = "path = `"$wpythonPath`""
            Set-Content -Path $wshimPath -Value $wshimContent
            Write-Host "pythonw 路径已写入:$wpythonPath" -ForegroundColor Cyan
        }
        
        Set-Config -Key "versionType" -Value "version"
        Set-Config -Key "versionName" -Value $requestedVersion
        
        return
    }
            
    $checkedVenvPath = Join-Path -Path $venvPath -ChildPath $requestedVersion
    if (Test-Path -Path $checkedVenvPath) {
                
        $pythonPath = Join-Path -Path $checkedVenvPath -ChildPath "Scripts\python.exe"
                
        $pipPath = Join-Path -Path $checkedVenvPath -ChildPath "Scripts\pip.exe"
        $wpythonPath = Join-Path -Path $checkedVenvPath -ChildPath "Scripts\pythonw.exe"
        if (Test-Path $pythonPath) {
            $pythonShimContent = "path = `"$pythonPath`""
            Set-Content -Path $shimPath -Value $pythonShimContent
            Write-Host "python 路径已写入:$pythonPath" -ForegroundColor Cyan
        }

        if (Test-Path $pipPath) {
            $pipShimContent = "path = `"$pipPath`""
            Set-Content -Path $pipshimPath -Value $pipShimContent
            Write-Host "pip 路径已写入:$pipPath" -ForegroundColor Cyan
        }

        if (Test-Path $wpythonPath) {
            $wshimContent = "path = `"$wpythonPath`""
            Set-Content -Path $wshimPath -Value $wshimContent
            Write-Host "pythonw 路径已写入:$wpythonPath" -ForegroundColor Cyan
        }

        Set-Config -Key "versionType" -Value "venv"
        Set-Config -Key "versionName" -Value $requestedVersion
        return
    }

    Write-Host "全局版本已切换到 $requestedVersion " -ForegroundColor Green
            
}

function New-ShimFile {
    param (
        [Parameter(Mandatory)]
        [string]$shimPath
    )
    if (-not (Test-Path $shimPath)) {
        New-Item -Path $shimPath -ItemType File -Force | Out-Null
        Write-Host "已创建空 shim 文件:$shimPath"
    }
}
# ---- Venvs.ps1 ----
function Invoke-Venvs {
    param(
        [Parameter(Mandatory = $false)]
        [string]$Pattern
    )
    $storePath = Get-StorePath
    $venvsPath = Join-Path -Path $storePath -ChildPath "venvs"


    $venvList = Get-ChildItem -Path $venvsPath -Directory 
    if ($venvList.Count -eq 0) {
        Write-Host "当前没有任何虚拟环境" -ForegroundColor Red
        return
    }
    if ($Pattern) {
        $filteredVenvs = $venvList | Where-Object { $_.Name -like "*$Pattern*" }
        if ($filteredVenvs.Count -eq 0) {
            Write-Host "没有找到匹配$Pattern 的虚拟环境" -ForegroundColor Yellow
        }
        else {
            $filteredVenvs | ForEach-Object {
                Write-Host $_.Name -ForegroundColor Cyan
            }
        }
    }
    else {
        $venvList | ForEach-Object {
            Write-Host $_.Name -ForegroundColor Cyan
        }
        Write-Host "通过第二个参数可以搜索带有某些字样的虚拟环境,如popy vs main 就会查找所有带有main字样的文件夹" -ForegroundColor Yellow
    }

}
# ---- Versions.ps1 ----
function Invoke-Versions {
    param(
        [Parameter(Mandatory = $false)]
        [string]$Pattern

    )


    $storePath = Get-StorePath
    $versionsPath = Join-Path -Path $storePath -ChildPath "versions"
    New-Directory -checkedPath $versionsPath -description "versions"

    $versionsList = Get-ChildItem -Path $versionsPath -Directory
    if ($versionsList.Count -eq 0) {
        Write-Host "当前没有安装任何python版本" -ForegroundColor Red
        return
    }
    if ($Pattern) {
        $filteredVersions = $versionsList | Where-Object { $_.Name -like "*$Pattern*" }
        if ($filteredVersions.Count -eq 0) {
            Write-Host "没有找到匹配$Pattern 的Python版本" -ForegroundColor Yellow
        }
        else {
            $filteredVersions | ForEach-Object {
                Write-Host $_.Name -ForegroundColor Cyan
            }
        }
    }
    else {
        $versionsList | ForEach-Object {
            Write-Host $_.Name -ForegroundColor Cyan
        }
        Write-Host "通过第二个参数可以搜索带有某些字样的虚拟环境,如popy ps 3.9 就会查找所有带有3.9字样的文件夹" -ForegroundColor Yellow
    }
}
function popy {
    param(
        [Parameter(Mandatory = $false, Position = 0)]
        [string]$Command,

        [Parameter(ValueFromRemainingArguments)]
        [string[]]$Rest,

        [Parameter(Mandatory = $false)]
        [Alias('f')]
        [switch]$Force,

        [Parameter(Mandatory = $false)]
        [Alias('v')]
        [string]$Version

        # [Parameter(Mandatory = $false)]
        # [switch]$System

        # [Parameter(Mandatory = $false)]
        # [string]$NowPath
    )
    $isInit = Test-IsInit
    if ((-not $isInit) -and -not ($Command -eq 'init')) {

        Write-Host "使用前请先使用popy init 来初始化程序,做基础配置和依赖下载" -ForegroundColor Red
        return
    }

    $ForceTable = @{ Force = $Force.IsPresent }
    
    # $SystemTable = @{ System = $System.IsPresent }


    switch ($Command.ToLower()) {
        'init' {
            Invoke-Init @ForceTable
        }

        { 'install', 'i' -contains $_ } {
            if ($Rest.Count -eq 0) { 
                Write-Host '缺少参数:popy install <version>' -ForegroundColor Red
                return 
            }
            # 按需加载
            Invoke-Install $Rest[0] @ForceTable
        }
        'update' {
            Invoke-Update
        }
        'sources' {
            if ($Rest.Count -eq 0) { 
                Invoke-Sources
            }
            else {
                Invoke-Sources $Rest[0]
            }
        }
        'source' {
            if ($Rest.Count -eq 0) { 
                Invoke-Source
            }
            else {
                Invoke-Source $Rest[0]
            }
        }
        'pipsources' {
            if ($Rest.Count -eq 0) { 
                Invoke-PipSources
            }
            else {
                Invoke-PipSources $Rest[0]
            }
        }
        'pipsource' {
            if ($Rest.Count -eq 0) { 
                Invoke-PipSource
            }
            else {
                Invoke-PipSource $Rest[0]
            }
        }
        { 'use', 'u', 'global', 'g' -contains $_ } {
            if ($Rest.Count -eq 0) { 
                # Write-Host '缺少参数:popy use <version>' -ForegroundColor Red
                # return
                Invoke-Use
            }
            else {
                Invoke-Use $Rest[0]
            }
            
        }
        { 'create', 'c', 'venv', 'v' -contains $_ } {
            if ($Rest.Count -eq 0) { 
                Invoke-Create -Version $Version
            }
            else {
                Invoke-Create $Rest[0] -Version $Version
            }
            
        }
        { 'versions', 'ps' -contains $_ } {
            if ($Rest.Count -eq 0) { 
                Invoke-Versions
            }
            else {
                Invoke-Versions $Rest[0]
            }
        }
        { 'venvs', 'vs' -contains $_ } {
            if ($Rest.Count -eq 0) { 
                Invoke-Venvs
            }
            else {
                Invoke-Venvs $Rest[0]
            }
        }
        { 'requirements', 'rs' -contains $_ } {
            if ($Rest.Count -eq 0) { 
                Invoke-Requirements
            }
            else {
                Invoke-Requirements $Rest[0]
            }
        }
        { 'activate', 'a' -contains $_ } {
            if ($Rest.Count -eq 0) { 
                Invoke-Activate
            }
            else {
                Invoke-Activate $Rest[0]
            }
            
        }
        { 'deactivate', 'd' -contains $_ } {
            Invoke-Deactivate 
        }
        { 'removevenv', 'rv' -contains $_ } {
            if ($Rest.Count -eq 0) { 
                Invoke-RemoveVenv
            }
            else {
                Invoke-RemoveVenv $Rest[0]
            }
            
        }
        { 'freeze', 'f' -contains $_ } {
            if ($Rest.Count -eq 0) { 
                Invoke-Freeze
            }
            else {
                Invoke-Freeze $Rest[0] @ForceTable
            }
        }
        { 'removerequirement', 'rr' -contains $_ } {
            if ($Rest.Count -eq 0) { 
                Invoke-RemoveRequirement
            }
            else {
                Invoke-RemoveRequirement $Rest[0]
            }
            
        }
        'cd' {
            if ($Rest.Count -eq 0) { 
                Write-Host '缺少参数:popy cd <p|v|r>' -ForegroundColor Red
                return 
            }
            Invoke-Cd $Rest[0]
        }
        'add' {
            if ($Rest.Count -eq 0) { 
                Write-Host '缺少参数:popy add <package|requirements.txt>' -ForegroundColor Red
                return 
            }
            Invoke-Add $Rest[0] @ForceTable
        }
        default { 
            Invoke-Help
        }
    }
    
}





Export-ModuleMember -Function popy