ValidateURLs.ps1

param([string]$filename)

if (-not (Test-Path $filename)) {Write-Host -f red "File not found: $filename"; return}

$urls = Get-Content $filename | Where-Object {$_.Trim() -ne ""}; $total = $urls.Count; $validated = 0; $invalid = 0; $retryList = @(); $count2xx = 0; $count401 = 0; $count403 = 0; $count429 = 0; $count500 = 0; $count503 = 0; $countOtherInvalid = 0; $validList = @(); $invalidList = @(); $startTime = Get-Date; $timings = [System.Collections.Generic.Queue[double]]::new(); $windowSize = 20

function Format-TimeSpan($ts) {return "{0:00}:{1:00}" -f $ts.Minutes, $ts.Seconds}

function Show-Status {$elapsed = (Get-Date) - $startTime; $processed = $validated + $invalid; $remaining = $total - $processed

if ($processed -gt 0) {$average = ($timings | Measure-Object -Average).Average; $estimated = [timespan]::FromSeconds($average * $remaining)}
else {$estimated = [timespan]::Zero}

cls; Write-Host -f cyan "URL Validator:"
Write-Host -f yellow ("-" * 50)
Write-Host ("Time elapsed:`t`t$(Format-TimeSpan $elapsed)")
Write-Host ("Time remaining:`t`t$(Format-TimeSpan $estimated)")
Write-Host ("Current URL:`t`t$($currentURL)")
Write-Host ("Total URLs to validate:`t$total")
Write-Host ("Validated URLs:`t`t$validated")
Write-Host ("Invalid URLs:`t`t$invalid")
Write-Host ("URLs remaining:`t`t$remaining`n")
Write-Host -f cyan "Response Codes:"
Write-Host -f yellow ("-" * 50)
Write-Host ("2##: Success:`t`t $count2xx")
Write-Host ("401: Unauthorized:`t $count401")
Write-Host ("403: Forbidden:`t`t $count403")
Write-Host ("429: Too Many Requests: $count429")
Write-Host ("500: Server Error:`t $count500")
Write-Host ("503: Service Unavailable: $count503")
Write-Host ("Other Invalid:`t`t $countOtherInvalid")}

function Test-Url($url) {$usedGet = $false; try {$response = Invoke-WebRequest -Uri $url -Method Head -UseBasicParsing -TimeoutSec 5 -ErrorAction Stop}
catch {$usedGet = $true; try {$response = Invoke-WebRequest -Uri $url -Method Get -UseBasicParsing -TimeoutSec 5 -ErrorAction Stop}
catch {return @{Valid=$false; Status=0}}}
return @{Valid=$true; Status=[int]$response.StatusCode}}

function Process-Result($url, $status) {switch ($status) {{$_ -ge 200 -and $_ -lt 300} {$script:validated++; $script:count2xx++; $script:validList += $url; break}
401 {$script:validated++; $script:count401++; $script:validList += $url; break}
403 {$script:validated++; $script:count403++; $script:validList += $url; break}
429 {$script:count429++; $script:retryList += $url; break}
500 {$script:count500++; $script:retryList += $url; break}
503 {$script:count503++; $script:retryList += $url; break}
default {$script:invalid++; $script:countOtherInvalid++; $script:invalidList += $url; break}}}

# Main loop
foreach ($url in $urls) {$currentURL = $url; $result = Test-Url $url; Process-Result $url $result.Status; Show-Status; Start-Sleep -Milliseconds 100}

# Retry 429/500/503 once
if ($retryList.Count -gt 0) {$retrying = $retryList; $retryList = @()
foreach ($url in $retrying) {$currentURL = $url; $result = Test-Url $url; Process-Result $url $result.Status; Show-Status; Start-Sleep -Milliseconds 100}}

# Export results
$directory = split-path (resolve-path $filename); $timestamp = Get-Date -Format "yyyy-MM-dd @ HH-mm-ss"
$validList | Set-Content "$directory\validatedurls, $timestamp.txt"; $invalidList | Set-Content "$directory\expiredurls, $timestamp.txt"

Write-Host -f green "`nValidation complete. Results saved to " -n; Write-Host -f white "validatedurls.txt" -n; Write-Host -f green " and " -n; Write-Host -f white "expiredurls.txt" -n; Write-Host -f green ".`n"