Private/PivTool/Write-PWSHYBKPIVPinRetryWarning.ps1

function Write-PWSHYBKPIVPinRetryWarning {
    <#
    .SYNOPSIS
        Emits a console warning when a caught PIN/PUK verification failure indicates the retry
        counter dropped to zero or is running low.
    .DESCRIPTION
        Test-PWSHYBKPIVPin, Test-PWSHYBKPIVBio, Test-PWSHYBKPIVSignature, and
        Test-PWSHYBKPIVDecryption catch yubico-piv-tool.exe failures and return $false rather than
        throwing, per Test- verb convention. That convention correctly hides an ordinary wrong PIN
        as a plain negative result, but it also silently hid the one case that is not routine: the
        PIV application's PIN counter reaching zero, which locks the PIV application until
        Unblock-PWSHYBKPIVPin (with the PUK) or Reset-PWSHYBKPIVDevice is run. Callers who only
        check the boolean return value had no way to notice that transition.

        Inspects the failure text (yubico-piv-tool.exe's own output, as included in the caught
        exception's message) for its two known verify-pin/verify-bio phrasings and writes a
        Write-Warning accordingly:
        - "Pin code blocked, use unblock-pin action to unblock." -> blocked warning.
        - "Pin verification failed, N tries left before pin is blocked." -> low-tries warning
          with the remaining count.
        Any other failure text (wrong slot, tool not found, unrelated error) produces no warning
        here - the caller's own error handling/logging already covers those.

        Uses Write-Warning rather than Write-PWSHYBKPIVLog so the signal is always visible on the
        console, independent of this module's opt-in Logging configuration.
    .PARAMETER Message
        The caught failure's text to inspect, typically $_.Exception.Message from the calling
        cmdlet's catch block.
    .OUTPUTS
        None. Writes to the warning stream when a match is found; otherwise produces no output.
    .EXAMPLE
        Write-PWSHYBKPIVPinRetryWarning -Message $_.Exception.Message
        Called from a Test- cmdlet's catch block before returning $false.
    #>

    [CmdletBinding()]
    [OutputType([void])]
    param(
        [Parameter(Mandatory)]
        [AllowEmptyString()]
        [string] $Message
    )

    if ($Message -match 'Pin code blocked') {
        Write-Warning -Message 'The PIN is now blocked (0 tries left). Use Unblock-PWSHYBKPIVPin with the PUK, or Reset-PWSHYBKPIVDevice, to recover.'
    } elseif ($Message -match '(\d+)\s+tries left before pin is blocked') {
        Write-Warning -Message "PIN verification failed: $($Matches[1]) tries left before the PIN is blocked."
    }
}