
# 定义全局变量
$ROOT_PATH =  $env:BT_PANEL -replace "\\", "/"
$SOFT_PATH = $env:BT_SETUP -replace "\\", "/"
$ConfigFile =  "$ROOT_PATH/config/config.json"

$DownNode = "http://download.bt.cn"  # 下载地址

function Get-File-Content {
    param (
        [string]$FilePath
    )
    try {
        return Get-Content -Path $FilePath -Raw -Encoding UTF8 -ErrorAction Stop
    } catch {    
        return Get-Content $FilePath 
    }   
}

$bodyStr = Get-File-Content -FilePath $ConfigFile
# 加载配置文件
$conf = $bodyStr | ConvertFrom-Json
if (-not $conf.language -or $conf.language -eq "" -or $conf.language -eq "Simplified_Chinese") {
    $conf.language = "zh"  # 默认语言
}

# 加载语言文件
$LangFile = "$ROOT_PATH/languages/$($conf.language)/server.json"
if (-not (Test-Path $LangFile)) {
    Write-Host "Language file not found: $LangFile"
    exit
}
$langBody = Get-File-Content -FilePath $LangFile
$langObject = $langBody | ConvertFrom-Json

function Lang-Print {
    param (
        [string]$Msg,           # 消息内容
        [array]$Arguments       # 参数数组
    )
	
    $res = Lang-Ctx -Msg $Msg -Arguments $Arguments
    Write-Host "|-- $res"
}

function Is-MD5 {
    param (
        [string]$InputString  # 要检测的字符串
    )
    # 定义 MD5 正则表达式
    $md5Regex = '^[a-fA-F0-9]{32}$'
    # 检测字符串是否匹配 MD5 格式
    if ($InputString -match $md5Regex) {
        return $true
    } else {
        return $false
    }
}

# 定义函数：获取语言内容
function Lang-Ctx {
    param (
        [string]$Msg,           # 消息内容
        [array]$Arguments            # 参数数组
    )

    if (Is-MD5 -InputString $Msg) {
        $Md5Hash = $Msg
    } else {
        $Md5Hash = Get-MD5Hash -InputString ($Msg -replace " ", "")
    }  

    $value = $langObject.$Md5Hash 
    if (-not $value) {
        $value = $Msg
    }
    return Replace-LangVar -Msg $value -Arguments $Arguments
}

function Replace-LangVar {
    param (
        [string]$msg,
        [array]$Arguments
    )

    if ($Arguments.Count -eq 0) {
        return $msg
    }

    # 兼容低版本 PowerShell
    $sb = New-Object System.Text.StringBuilder($msg)
    $replaceCount = 0

    foreach ($arg in $Arguments) {
        $index = $sb.ToString().IndexOf('{}')
        if ($index -eq -1) { break }
        [void]$sb.Remove($index, 2)
        [void]$sb.Insert($index, $arg)
        $replaceCount++
    }

    return $sb.ToString()
}


function Get-MD5Hash {
    param (
        [string]$InputString  
    )
    $md5 = [System.Security.Cryptography.MD5]::Create()
    $bytes = [System.Text.Encoding]::UTF8.GetBytes($InputString)
    $hashBytes = $md5.ComputeHash($bytes)
    $hashString = [BitConverter]::ToString($hashBytes) -replace "-", ""
    return $hashString.ToLower()
}


function Invoke-Cmd {
    param([string]$Command)
    cmd /c "$Command" 
}

function Invoke-Cmd-NoOut {
    param([string]$Command)
  
    cmd /c "$Command" > $null 2>&1
}

function Invoke-Cmd-WithOutput {
    param([string]$Command)

    $out_file = "$ROOT_PATH/data/ps1_out.txt"
    #删除旧的输出文件
    if (Test-Path $out_file) {
        Remove-Item $out_file -Force
    }    
    cmd /c "$Command" > $out_file 2>&1

    $bodyStr = Get-Content $out_file -Raw

    $utf8Bytes = [System.Text.Encoding]::GetEncoding("GB2312").GetBytes($bodyStr)
    $utf8Str = [System.Text.Encoding]::UTF8.GetString($utf8Bytes)
    Write-Host $utf8Str
}

function Install-Service {
    param (
        [string]$ServiceName,  # 服务名称
        [string]$DisplayName,   # 显示名称
        [string]$BinaryPathName,      # 可执行文件路径
        [string]$Description = ""  # 服务描述（可选）
    )
  
    Lang-Print -Msg "正在安装 {} 服务" -Arguments @($ServiceName)
    New-Service -Name $ServiceName -BinaryPathName $BinaryPathName -DisplayName $DisplayName -Description $Description -StartupType Automatic

    # 检查服务是否安装成功
    $service = Get-Service -Name $ServiceName -ErrorAction SilentlyContinue
    if ($null -eq $service) {
        Lang-Print -Msg "服务 {} 安装失败" -Arguments @($ServiceName)
        return $false
    }
    Lang-Print -Msg "服务 {} 安装成功" -Arguments @($ServiceName)
    return $true
}

#下载文件
function Download-File {
    param (
        [string]$Url,          # 下载链接
        [string]$OutputPath    # 输出路径
    )
    try {
        #判断文件是否存在，如果存在需要删除
        if (Test-Path $OutputPath) {
            Remove-Item $OutputPath -Force
        }
        $wget_file =  "$ROOT_PATH/script/wget.exe"
        if (-not (Test-Path $wget_file)) {
            Invoke-WebRequest -Uri $Url -OutFile $OutputPath -ErrorAction Stop          
        }else{
            Invoke-Cmd -Command "$wget_file -c -O `"$OutputPath`" `"$Url`""
        }       
       
        if (-not (Test-Path $OutputPath)) {
            Lang-Print -Msg "下载失败，文件未找到: {}" -Arguments @($OutputPath)
            return $false
        }
        # 检查文件大小是否大于0
        $fileInfo = Get-Item $OutputPath
        if ($fileInfo.Length -le 0) {
            Lang-Print -Msg "下载失败，文件大小为0: {}" -Arguments @($OutputPath)
            Remove-Item $OutputPath -Force
            return $false
        }
        Lang-Print -Msg "下载成功: {}" -Arguments @($OutputPath)
        return $true
    } catch {
        Lang-Print -Msg "下载失败: {}" -Arguments @("$_")
        return $false
    }
}

function Generate-RandomString {
    param(
        [int]$Length = 16  # 字符串长度
    )
    # 生成 GUID 并去除 "-"
    $guid = ([guid]::NewGuid().ToString() -replace "-")

    # 截取指定长度
    return $guid.Substring(0, $Length)
}

#解压zip文件
function Un-Zip {
    param (
        [Parameter(Mandatory=$true)]
        [string]$FilePath,          # ZIP文件路径
        
        [Parameter(Mandatory=$true)]
        [string]$DestinationPath,   # 解压目标路径
        
        [switch]$Quiet,              # 静默模式（不显示进度）
        [switch]$Force              # 强制覆盖文件       
    )
    # 查找unzip.exe
    $unzip_exe =  "$SOFT_PATH/panel/script/unzip.exe"
    if (Test-Path $unzip_exe) {     
        #unzip解压文件到指定目录，解压时覆盖
        $arguments = "-o `"$FilePath`" -d `"$DestinationPath`""       
        Invoke-Cmd "$unzip_exe $arguments"
        return $true
    }
    try {
        # 创建目标目录（如果不存在）
        if (-not (Test-Path -Path $DestinationPath)) {
            New-Item -ItemType Directory -Path $DestinationPath -Force | Out-Null
        }

        # 创建Shell COM对象
        $shell = New-Object -ComObject Shell.Application
        
        # 获取ZIP文件和目标文件夹对象
        $zipFile = $shell.NameSpace((Get-Item $FilePath).FullName)
        $destFolder = $shell.NameSpace((Get-Item $DestinationPath).FullName)

        # 设置解压选项（位掩码）
        $options = 0
        
        # 4 = 不显示覆盖确认对话框
        if ($Force) { $options += 4 }
        
        # 16 = 显示进度对话框（默认显示）
        if (-not $Quiet) { $options += 16 }
                 
        # 1024 = 不显示错误对话框
        if ($Quiet) { $options += 1024 }

        # 执行解压操作
        $destFolder.CopyHere($zipFile.Items(), $options)

        # 释放COM对象资源
        [System.Runtime.Interopservices.Marshal]::ReleaseComObject($destFolder) | Out-Null
        [System.Runtime.Interopservices.Marshal]::ReleaseComObject($zipFile) | Out-Null
        [System.Runtime.Interopservices.Marshal]::ReleaseComObject($shell) | Out-Null
        [System.GC]::Collect()
        [System.GC]::WaitForPendingFinalizers()
                
    }
    catch {
      
        return $false
    }    
    return $true
}

function Get-AppSetup {
    param (  
        [array]$SearchList   # 软件关键词
    )
    $uninstallPath = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall"
    if ([Environment]::Is64BitOperatingSystem) {
        $uninstallPath = "HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall"
    }
    try {
        $key = Get-ChildItem -Path $uninstallPath
        foreach ($subKey in $key) {
            try {
                # 读取 DisplayName 值
                $displayName = (Get-ItemProperty -Path $subKey.PSPath -Name "DisplayName" -ErrorAction SilentlyContinue).DisplayName
                if (-not $displayName) { continue }

                # 检查软件名称是否匹配
                $matchCount = 0
                foreach ($name in $SearchList) {
                    if ($displayName -like "*$name*") {
                        $matchCount++
                    } else {
                        $matchCount = 0
                    }
                }
                # 如果所有软件名称都匹配，则返回 True
                if ($matchCount -eq $SearchList.Count) {
                    return $true
                }
            } catch {
                continue
            }
        }
    } catch {
        Lang-Print -Msg "无法访问注册表路径: {}" -Arguments @("$uninstallPath")
    }
    return $false
}

function Setup-VCRedist {
    param (
        [string]$VcVersion # VC++ 版本，例如 "2008", "2010", "2012", "2013", "2015", "2017", "2019", "2022"
    )
    $bit = if ([Environment]::Is64BitOperatingSystem) { "x64" } else { "x86" }
    $vc_list = @("2015","2017", "2019","2022")
    if ($vc_list -contains $VcVersion) {
      
        if (@("2015","2017", "2019") -contains $VcVersion) {
            if  (Get-AppSetup -SearchList @("Visual C++", "2015-2019",$bit)) {
                Lang-Print -Msg "{} 运行库已安装" -Arguments @("VC++ 2015-2019")
                return $true
            }
        }
        if  (Get-AppSetup -SearchList @("Visual C++", "2015-2022",$bit)) {
            Lang-Print -Msg "{} 运行库已安装" -Arguments @("VC++ 2015-2022")
            return $true
        }
    }

    if  (Get-AppSetup -SearchList @("Visual C++", $VcVersion,$bit)) {
        Lang-Print -Msg "{} 运行库已安装" -Arguments @("VC++ $VcVersion")
        return $true
    }

    # 下载并安装 VC++ 运行库
    $installerPath = "$SOFT_PATH/temp/vcredist_$VcVersion.exe"  
    Lang-Print -Msg "下载 {} 运行库" -Arguments @("VC++ $VcVersion")

    Write-Host $bit
    $DownUrl = "$DownNode/win/vc/vcredist_$VcVersion" + "_$bit.exe"
    if (-not (Download-File -Url $DownUrl -OutputPath $installerPath)) {
        Lang-Print -Msg "下载 {} 运行库失败" -Arguments @("VC++ $VcVersion")
        return $false
    }
    Lang-Print -Msg "正在安装 {} 运行库，请稍候..." -Arguments @("VC++ $VcVersion")

    Write-Host "$installerPath /quiet /norestart"
    Invoke-Cmd -Command "$installerPath /quiet /norestart"

    #重新检测
    if  (Get-AppSetup -SearchList @("Visual C++", $VcVersion,$bit)) {
        return $true
    }
    Lang-Print -Msg "{} 运行库安装失败,请手动安装,路径:{}" -Arguments @("VC++ $VcVersion", $installerPath)
    return $false
}

#删除所有版本标记文件
function Remove-VersionFiles {
    param (
        [string]$Path
    )
    $default_list = @('version.pl', 'version.txt', 'version.json', 'version.xml')
    foreach ($file in $default_list) {
        $file_path = "$Path/$file"
        if (Test-Path $file_path) {
            Remove-Item -Path $file_path -Force -ErrorAction SilentlyContinue          
        }
    }
}


function Get-PanelFile {
    $default_list = @('btPanel.exe', "aaPanel.exe")
    foreach ($file in $default_list) {
        $file_path = "$ROOT_PATH/$file"
        if (Test-Path $file_path) {
            return $file_path
        }
    }
    return ""
}

function Set-FileAccess {
    [CmdletBinding(SupportsShouldProcess = $true)]
    param (
        [Parameter(Mandatory = $true)]
        [ValidateScript({ Test-Path $_ -PathType Any })]
        [string]$Path,          # 文件或目录路径
        
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [string]$User,          # 用户或用户组名称
        
        [Parameter(Mandatory = $true)]
        [ValidateSet("Modify", "Read", "Full")]
        [string]$Permission,    # 权限类型
        
        [bool]$Recursive = $false  # 是否递归设置权限
    )

    # 权限类型映射
    $permissionMap = @{
        "Modify" = "M"
        "Read"   = "R"
        "Full"   = "F"
    }

    # 获取权限标志
    $permissionFlag = $permissionMap[$Permission]
    
    # 构建递归参数
    $recursiveFlag = if ($Recursive) { "/T" } else { $null }
    
    # 构建icacls命令参数
    $icaclsArgs = @(
        "`"$Path`""
        $recursiveFlag
        "/grant"
        "`"${User}:${permissionFlag}`""
        "/Q"  # 安静模式，减少输出
        "/C"  # 继续操作即使发生错误
    ) | Where-Object { $_ -ne $null }

    if ($PSCmdlet.ShouldProcess($Path, "设置 $Permission 权限给 $User")) {
        try {
            # 使用Start-Process代替Invoke-Expression，更安全
            $process = Start-Process "icacls" -ArgumentList $icaclsArgs -NoNewWindow -Wait -PassThru -RedirectStandardOutput "nul"
            
            if ($process.ExitCode -eq 0) {
                Lang-Ctx -Msg "成功设置 {} 对 {} 的 {} 权限" -Arguments @($User, $Path, $Permission)
                return $true
            } 
            else {
                $errorMsg = switch ($process.ExitCode) {
                    1 { Lang-Ctx -Msg "无效参数或权限不足" -Arguments @() }
                    2 { Lang-Ctx -Msg "文件未找到" -Arguments @() }
                    4 { Lang-Ctx -Msg "磁盘空间不足" -Arguments @() }
                    5 { Lang-Ctx -Msg "访问被拒绝" -Arguments @() }
                    default { Lang-Ctx -Msg "未知错误 " -Arguments @() }
                }
                throw Lang-Ctx -Msg "设置权限失败: {}" -Arguments @($errorMsg)
            }
        }
        catch {
            Lang-Ctx -Msg "权限设置操作失败: {}" -Arguments @($_.Exception.Message)
            return $false
        }
    }
}

function Back-ConfigFile {
    param (
        [string]$FilePath
    )
    if (Test-Path $FilePath) {
        $backupFile = "$FilePath.backup"
        Copy-Item -Path $FilePath -Destination $backupFile -Force
        Lang-Ctx -Msg "已备份配置文件到: {}" -Arguments @($backupFile)
    } else {
        Lang-Ctx -Msg "配置文件不存在: {}" -Arguments @($FilePath)
    }
}
#还原
function Restore-ConfigFile {
    param (
        [string]$FilePath
    )
    $backupFile = "$FilePath.backup"
    if (Test-Path $backupFile) {
        Copy-Item -Path $backupFile -Destination $FilePath -Force
        Remove-Item -Path $backupFile -Force
        Lang-Ctx -Msg "已还原配置文件: {}" -Arguments @($FilePath)
    } else {
        Lang-Ctx -Msg "备份文件不存在: {}" -Arguments @($backupFile)
    }
}

function Set-WebServer-Status {
    
    param (
        [string]$Status
    )
    $process_list = @("httpd.exe", "nginx_server.exe","nginx.exe","w3wp.exe","php-cgi.exe")
    foreach ($process_name in $process_list) {    
        #强制结束进程
        Invoke-Cmd-NoOut -Command "taskkill /f /im $process_name"      
    }    
    $webServer = ""
    #检查web服务是否安装,包含nginx/apache/iis
    if (Get-Service -Name nginx -ErrorAction SilentlyContinue){
        $webServer = "nginx"
    }elseif (Get-Service -Name apache -ErrorAction SilentlyContinue){
        $webServer = "apache"
    }elseif (Get-Service -Name w3svc -ErrorAction SilentlyContinue){
        $webServer = "iis"
    }

    if ($Status -eq "start") {
        Start-Service -Name $webServer     
    } elseif ($Status -eq "stop") {
        Stop-Service -Name $webServer
    }
    elseif ($Status -eq "restart") {
        Restart-Service -Name $webServer
    } 
}

function Delete-Panel-Service {
    param (
        [string]$ServiceName
    )
    try {
        if (Get-Service -Name $ServiceName -ErrorAction SilentlyContinue) {
            Stop-Service -Name $ServiceName -Force
            Invoke-Cmd -Command "sc delete $ServiceName"
            Remove-Item -Path "HKLM:\SYSTEM\CurrentControlSet\Services\$ServiceName" -Recurse -Force -ErrorAction SilentlyContinue
            Remove-Item -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$ServiceName" -Recurse -Force -ErrorAction SilentlyContinue
            Remove-Item -Path "HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\$ServiceName" -Recurse -Force -ErrorAction SilentlyContinue
            Remove-Item -Path "HKLM:\SYSTEM\CurrentControlSet\Services\EventLog\Application \$ServiceName" -Recurse -Force -ErrorAction SilentlyContinue
            Remove-Item -Path "HKLM:\SYSTEM\CurrentControlSet\Services\EventLog\Application\$ServiceName" -Recurse -Force -ErrorAction SilentlyContinue
            Lang-Print -Msg "{} 服务已删除" -Arguments @($ServiceName)
        } else {
            Lang-Print -Msg "{} 服务不存在" -Arguments @($ServiceName)
        }
    } catch {
        Lang-Print -Msg "删除 {} 服务失败: {}" -Arguments @($ServiceName, $_.Exception.Message)
    }
}