param(
    [Parameter(Mandatory = $true)]
    [string]$Url,

    [string]$OutDir = "probe_out",

    [switch]$SaveResponse,

    [switch]$DecodeResponse,

    [int]$MaxAssets = 50
)

$ErrorActionPreference = "Stop"

function Get-Tool {
    $curlExe = Get-Command "curl.exe" -ErrorAction SilentlyContinue
    if ($curlExe) {
        return @{ Name = "curl"; Path = $curlExe.Path }
    }

    $wgetExe = Get-Command "wget.exe" -ErrorAction SilentlyContinue
    if ($wgetExe) {
        return @{ Name = "wget"; Path = $wgetExe.Path }
    }

    $curlAlias = Get-Command "curl" -ErrorAction SilentlyContinue
    if ($curlAlias) {
        return @{ Name = "curl"; Path = "curl" }
    }

    $wgetAlias = Get-Command "wget" -ErrorAction SilentlyContinue
    if ($wgetAlias) {
        return @{ Name = "wget"; Path = "wget" }
    }

    throw "Neither curl nor wget is available on PATH."
}
function Fetch-Url {
    param(
        [string]$TargetUrl,
        [string]$ToolName,
        [string]$ToolPath
    )

    $tmpFile = Join-Path $env:TEMP ("probe_" + [guid]::NewGuid().ToString() + ".bin")
    try {
        if ($ToolName -eq "curl") {
            & $ToolPath -L -s -S $TargetUrl -o $tmpFile | Out-Null
        } else {
            & $ToolPath -q -O $tmpFile $TargetUrl | Out-Null
        }

        if ($LASTEXITCODE -ne 0) {
            throw "$ToolName exited with code $LASTEXITCODE"
        }

        return [IO.File]::ReadAllBytes($tmpFile)
    }
    finally {
        if (Test-Path $tmpFile) {
            Remove-Item -Force $tmpFile
        }
    }
}
function Get-FileExtFromMime {
    param([string]$Mime)

    $map = @{
        "image/png" = ".png"
        "image/jpeg" = ".jpg"
        "image/jpg" = ".jpg"
        "image/gif" = ".gif"
        "image/svg+xml" = ".svg"
        "font/woff2" = ".woff2"
        "font/woff" = ".woff"
        "application/pdf" = ".pdf"
    }

    $key = $Mime.ToLowerInvariant().Trim()
    if ($map.ContainsKey($key)) {
        return $map[$key]
    }
    return ".bin"
}

function Get-Sha256Short {
    param([byte[]]$Bytes)

    $sha = [System.Security.Cryptography.SHA256]::Create()
    try {
        $hash = $sha.ComputeHash($Bytes)
    }
    finally {
        $sha.Dispose()
    }

    $hex = ($hash | ForEach-Object { $_.ToString("x2") }) -join ""
    return $hex.Substring(0, 12)
}
function Decode-AnyBase64 {
    param([byte[]]$Bytes)

    $text = [Text.Encoding]::ASCII.GetString($Bytes)
    $filtered = [regex]::Replace($text, "[^A-Za-z0-9+/=]", "")
    if ([string]::IsNullOrEmpty($filtered)) {
        return @()
    }
    $pad = 4 - ($filtered.Length % 4)
    if ($pad -lt 4) {
        $filtered += "=" * $pad
    }
    try {
        return [Convert]::FromBase64String($filtered)
    }
    catch {
        return @()
    }
}

function Ensure-Dir {
    param([string]$Path)
    if (-not (Test-Path $Path)) {
        New-Item -ItemType Directory -Path $Path | Out-Null
    }
}
$tool = Get-Tool
$bytes = Fetch-Url -TargetUrl $Url -ToolName $tool.Name -ToolPath $tool.Path
Write-Host "[ok] fetched via $($tool.Name): $($bytes.Length) bytes"

Ensure-Dir $OutDir

if ($SaveResponse) {
    $respPath = Join-Path $OutDir "response.bin"
    [IO.File]::WriteAllBytes($respPath, $bytes)
    Write-Host "[ok] saved response.bin"
}

$text = [Text.Encoding]::UTF8.GetString($bytes)
$matches = [regex]::Matches($text, "data:(?<mime>[^;]+);base64,(?<data>[A-Za-z0-9+/=]+)", "IgnoreCase")

if ($matches.Count -eq 0) {
    Write-Host "[info] no inline base64 assets found"
} else {
    Write-Host "[ok] found $($matches.Count) inline base64 assets"
}

$assetDir = Join-Path $OutDir "assets"
Ensure-Dir $assetDir

$count = 0
foreach ($m in $matches) {
    if ($count -ge $MaxAssets) {
        break
    }
    $mime = $m.Groups["mime"].Value
    $data = $m.Groups["data"].Value
    try {
        $blob = [Convert]::FromBase64String($data)
    }
    catch {
        continue
    }
    $ext = Get-FileExtFromMime -Mime $mime
    $sha = (Get-Sha256Short -Bytes $blob)
    $name = "asset_{0:D3}_{1}{2}" -f $count, $sha, $ext
    $path = Join-Path $assetDir $name
    [IO.File]::WriteAllBytes($path, $blob)
    $count++
}

if ($count -gt 0) {
    Write-Host "[ok] extracted $count assets to $assetDir"
}

if ($DecodeResponse) {
    $decoded = Decode-AnyBase64 -Bytes $bytes
    if ($decoded.Length -gt 0) {
        $decPath = Join-Path $OutDir "decoded.bin"
        [IO.File]::WriteAllBytes($decPath, $decoded)
        Write-Host "[ok] best-effort decoded response -> decoded.bin"
    } else {
        Write-Host "[warn] best-effort decode produced empty output"
    }
}
