param(
  [Parameter(Mandatory = $true)] [string] $Worker,
  [Parameter(Mandatory = $true)] [string] $Gui,
  [Parameter(Mandatory = $true)] [string] $Source,
  [Parameter(Mandatory = $true)] [string] $WorkDir,
  [Parameter(Mandatory = $true)] [string] $MediaBin,
  [Parameter(Mandatory = $true)] [string] $QtBin,
  [Parameter(Mandatory = $true)] [string] $MingwBin
)

$ErrorActionPreference = 'Stop'
New-Item -ItemType Directory -Force -Path $WorkDir | Out-Null
$previousPath = $env:PATH
$previousQpa = $env:QT_QPA_PLATFORM
$env:PATH = "$(Split-Path -Parent $Worker);$MediaBin;$QtBin;$MingwBin;$env:SystemRoot\System32;$env:SystemRoot"
$env:QT_QPA_PLATFORM = 'offscreen'
$ErrorActionPreference = 'Continue'
try {
  $random = [Random]::new(640013)
  $rejected = 0
  for ($index = 0; $index -lt 64; ++$index) {
    $length = 1 + (($index * 37) % 4096)
    $bytes = [byte[]]::new($length)
    $random.NextBytes($bytes)
    $path = Join-Path $WorkDir "malformed-$index.bin"
    [IO.File]::WriteAllBytes($path, $bytes)
    & $Worker --probe --input $path 2>$null | Out-Null
    if ($LASTEXITCODE -eq 0) { throw "Malformed input was accepted: $path" }
    if ($LASTEXITCODE -lt 0) { throw "Worker crashed on malformed input $path with $LASTEXITCODE" }
    ++$rejected
  }

  $sourceBytes = [IO.File]::ReadAllBytes((Resolve-Path -LiteralPath $Source))
  foreach ($length in @(1, 16, [Math]::Max(32, [int]($sourceBytes.Length / 2)))) {
    $path = Join-Path $WorkDir "truncated-$length.mkv"
    $slice = [byte[]]::new([Math]::Min($length, $sourceBytes.Length))
    [Array]::Copy($sourceBytes, $slice, $slice.Length)
    [IO.File]::WriteAllBytes($path, $slice)
    & $Worker --probe --input $path 2>$null | Out-Null
    if ($LASTEXITCODE -lt 0) { throw "Truncated input crashed the worker: $path ($LASTEXITCODE)" }
    ++$rejected
  }

  $missingOutput = Join-Path $WorkDir 'missing-source-output.mkv'
  & $Worker --export-range --input (Join-Path $WorkDir 'disconnected-source.mkv') --output $missingOutput --first-frame 0 --end-frame 3 2>$null | Out-Null
  if ($LASTEXITCODE -eq 0 -or (Test-Path -LiteralPath $missingOutput)) { throw 'Unavailable source did not fail cleanly' }

  $fullDiskOutput = Join-Path $WorkDir 'simulated-full-disk.mkv'
  $env:SMARTCUT_TEST_FULL_DISK = '1'
  try {
    $fullDiskReport = (& $Worker --export-range --input $Source --output $fullDiskOutput --first-frame 0 --end-frame 3 2>$null) -join "`n"
    if ($LASTEXITCODE -eq 0 -or (Test-Path -LiteralPath $fullDiskOutput) -or $fullDiskReport -notmatch '"partial_removed":true') { throw 'Simulated full disk did not fail and clean up atomically' }
  } finally { Remove-Item Env:SMARTCUT_TEST_FULL_DISK -ErrorAction SilentlyContinue }

  $readOnlySource = Join-Path $WorkDir 'read-only-source.mkv'
  $readOnlyOutput = Join-Path $WorkDir 'read-only-source-output.mkv'
  Copy-Item -LiteralPath $Source -Destination $readOnlySource -Force
  & attrib.exe +R $readOnlySource
  try {
    & $Worker --export-range --input $readOnlySource --output $readOnlyOutput --first-frame 0 --end-frame 3 2>$null | Out-Null
    if ($LASTEXITCODE -ne 0 -or -not (Test-Path -LiteralPath $readOnlyOutput)) { throw 'Read-only source export failed' }
  } finally { & attrib.exe -R $readOnlySource }

  $lockedOutput = Join-Path $WorkDir 'read-only-output.mkv'
  [IO.File]::WriteAllText($lockedOutput, 'sentinel')
  & attrib.exe +R $lockedOutput
  try {
    & $Worker --export-range --input $Source --output $lockedOutput --first-frame 0 --end-frame 3 2>$null | Out-Null
    if ($LASTEXITCODE -eq 0 -or [IO.File]::ReadAllText($lockedOutput) -ne 'sentinel') { throw 'Read-only output was overwritten or reported success' }
  } finally { & attrib.exe -R $lockedOutput }

  $partial = Join-Path $WorkDir 'interrupted-export.mkv'
  $cancelReport = Join-Path $WorkDir 'cancel-report.json'
  $guiRun = Start-Process -FilePath $Gui -ArgumentList @('--automation-cancel', $partial, $cancelReport) -Wait -PassThru -WindowStyle Hidden
  if ($guiRun.ExitCode -ne 0 -or (Test-Path -LiteralPath $partial)) { throw 'GUI cancellation left a partial output' }
  $cancel = Get-Content -Raw -LiteralPath $cancelReport | ConvertFrom-Json
  if (-not $cancel.partial_removed) { throw 'GUI cancellation report did not confirm cleanup' }

  $report = [ordered]@{
    schema = 'm13-failure-fuzz-v1'
    status = 'PASSED'
    malformed_inputs_rejected = $rejected
    unavailable_source = 'PASSED'
    full_disk_cleanup = 'PASSED'
    read_only_source = 'PASSED'
    read_only_output = 'PASSED'
    interrupted_export_cleanup = 'PASSED'
  }
  $report | ConvertTo-Json -Compress | Set-Content -Encoding utf8 -LiteralPath (Join-Path $WorkDir 'm13-failure-fuzz.json')
} finally {
  $env:PATH = $previousPath
  $env:QT_QPA_PLATFORM = $previousQpa
}
