# 共享函数库，由 admin-init.ps1 点源（dot-source）加载。

function Write-InitLog {
    param([Parameter(Mandatory)][string]$Message)
    $ts = Get-Date -Format 'yyyy-MM-dd HH:mm:ss'
    Write-Host "[$ts] INFO  $Message" -ForegroundColor Green
}

function Write-InitWarn {
    param([Parameter(Mandatory)][string]$Message)
    $ts = Get-Date -Format 'yyyy-MM-dd HH:mm:ss'
    Write-Host "[$ts] WARN  $Message" -ForegroundColor Yellow
}

function Write-InitError {
    param([Parameter(Mandatory)][string]$Message)
    $ts = Get-Date -Format 'yyyy-MM-dd HH:mm:ss'
    Write-Host "[$ts] ERROR $Message" -ForegroundColor Red
}

function Test-InitIsAdmin {
    $identity = [System.Security.Principal.WindowsIdentity]::GetCurrent()
    $principal = New-Object System.Security.Principal.WindowsPrincipal($identity)
    return $principal.IsInRole([System.Security.Principal.WindowsBuiltinRole]::Administrator)
}

function Assert-InitIsAdmin {
    if (-not (Test-InitIsAdmin)) {
        Write-InitError '必须以管理员身份运行 PowerShell（右键 -> 以管理员身份运行）'
        exit 1
    }
}

function Test-InitLocalUserExists {
    param([Parameter(Mandatory)][string]$Name)
    return [bool](Get-LocalUser -Name $Name -ErrorAction SilentlyContinue)
}

# 幂等判断用，Name 形如 "计算机名\用户名"，只比较最后一段
function Test-InitLocalGroupMember {
    param(
        [Parameter(Mandatory)][string]$GroupName,
        [Parameter(Mandatory)][string]$UserName
    )
    $members = Get-LocalGroupMember -Group $GroupName -ErrorAction SilentlyContinue
    if (-not $members) { return $false }
    foreach ($m in $members) {
        $short = $m.Name -replace '^.*\\', ''
        if ($short -eq $UserName) { return $true }
    }
    return $false
}

# Get-LocalUser 的 PasswordNeverExpires 在部分 Windows 版本上读到 $null（模块已知缺陷，
# 账号实际状态是对的），$null 时 fallback 到 ADSI 查 UserFlags，详见 .claude/design.md
function Test-InitPasswordNeverExpires {
    param([Parameter(Mandatory)][string]$UserName)

    $viaModule = (Get-LocalUser -Name $UserName -ErrorAction Stop).PasswordNeverExpires
    if ($null -ne $viaModule) {
        return $viaModule
    }

    $adsiUser = [ADSI]"WinNT://$env:COMPUTERNAME/$UserName,user"
    $flags = $adsiUser.UserFlags[0]
    return (($flags -band 0x10000) -ne 0)
}

# 读 Galaxy 的运维通用白名单（RDP/WinRM 加白共用），这个接口不需要 API Key。返回 $null
# 表示请求本身失败（连不上/未配置/Galaxy 报错），调用方要拿 $FallbackWhitelistIPs 兜底；
# 返回 @() 表示请求成功但列表本来就是空，是管理员的真实配置，不能被兜底覆盖
function Get-InitOpsWhitelist {
    if ($GalaxyApiBaseUrl -eq 'CHANGE_ME_BEFORE_RUNNING') {
        Write-InitWarn 'GalaxyApiBaseUrl in config\settings.ps1 is still the placeholder value - skipping ops whitelist fetch'
        return $null
    }

    $url = "$GalaxyApiBaseUrl/api/public/ops-whitelist"
    try {
        $resp = Invoke-RestMethod -Uri $url -Method Get -TimeoutSec 10 -ErrorAction Stop
    } catch {
        Write-InitWarn "Failed to fetch ops whitelist from Galaxy ($url): $($_.Exception.Message)"
        return $null
    }

    if ($resp.status -ne 'ok') {
        Write-InitWarn "Galaxy returned an error for ops whitelist (status=$($resp.status))"
        return $null
    }

    if (-not $resp.data.ips) {
        Write-InitLog 'Galaxy ops whitelist request succeeded but the list is currently empty'
        return @()
    }

    Write-InitLog "Fetched $($resp.data.ips.Count) ops whitelist IP(s) from Galaxy"
    return @($resp.data.ips)
}

# 读这台机器在 Galaxy 里登记的专属白名单（RDP 加白用，不给 WinRM 用，见 .claude/design.md），
# 同样不需要 API Key。$Hostname 留空、Galaxy 连不上/查无此机器时都返回空数组，不硬停
function Get-InitHostWhitelist {
    param([Parameter(Mandatory)][string]$Hostname)

    if ($GalaxyApiBaseUrl -eq 'CHANGE_ME_BEFORE_RUNNING') {
        Write-InitWarn 'GalaxyApiBaseUrl in config\settings.ps1 is still the placeholder value - skipping host whitelist fetch'
        return @()
    }

    $url = "$GalaxyApiBaseUrl/api/public/win-hosts/$Hostname/whitelist"
    try {
        $resp = Invoke-RestMethod -Uri $url -Method Get -TimeoutSec 10 -ErrorAction Stop
    } catch {
        Write-InitWarn "Failed to fetch host whitelist from Galaxy ($url): $($_.Exception.Message)"
        return @()
    }

    if ($resp.status -ne 'ok' -or -not $resp.data.own_ips) {
        Write-InitWarn "Galaxy returned no host whitelist IPs for '$Hostname' (status=$($resp.status))"
        return @()
    }

    Write-InitLog "Fetched $($resp.data.own_ips.Count) host whitelist IP(s) for '$Hostname' from Galaxy"
    return @($resp.data.own_ips)
}

# 用完立即释放临时明文指针，不在变量里长期保留明文
function Test-InitSecureStringEqual {
    param(
        [Parameter(Mandatory)][System.Security.SecureString]$First,
        [Parameter(Mandatory)][System.Security.SecureString]$Second
    )
    $bstr1 = [IntPtr]::Zero
    $bstr2 = [IntPtr]::Zero
    try {
        $bstr1 = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($First)
        $bstr2 = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($Second)
        $plain1 = [System.Runtime.InteropServices.Marshal]::PtrToStringBSTR($bstr1)
        $plain2 = [System.Runtime.InteropServices.Marshal]::PtrToStringBSTR($bstr2)
        return $plain1 -ceq $plain2
    } finally {
        if ($bstr1 -ne [IntPtr]::Zero) { [System.Runtime.InteropServices.Marshal]::ZeroFreeBSTR($bstr1) }
        if ($bstr2 -ne [IntPtr]::Zero) { [System.Runtime.InteropServices.Marshal]::ZeroFreeBSTR($bstr2) }
    }
}
