#requires -Version 7.0
$ErrorActionPreference='Stop'
$root=Split-Path $PSScriptRoot
$errorsFound=@()
Get-ChildItem $root -Filter *.ps1 -Recurse | ForEach-Object {
    $tokens=$null;$parseErrors=$null
    $null=[Management.Automation.Language.Parser]::ParseFile($_.FullName,[ref]$tokens,[ref]$parseErrors)
    $errorsFound+=@($parseErrors)
}
if ($errorsFound.Count) { $errorsFound | Format-List;throw 'AST parsing failed.' }
$article=Get-Content (Join-Path $root 'article.md') -Raw
foreach ($snippet in [regex]::Matches($article,'(?ms)^```powershell\s*\r?\n(.*?)^```')) {
    $tokens=$null;$parseErrors=$null
    $null=[Management.Automation.Language.Parser]::ParseInput($snippet.Groups[1].Value,[ref]$tokens,[ref]$parseErrors)
    if ($parseErrors.Count) { throw 'Article PowerShell code block failed AST parsing.' }
}
. "$root/MonitorPrivateLink.Common.ps1"
foreach ($case in @(
    @{status=403;body=@{error=@{message='Public network access is disabled.'}};expected=$true},
    @{status=403;body=@{error=@{code='PrivateLinkIdNotAllowed';message='Private link Id mismatch.'}};expected=$true},
    @{status=403;body=@{error=@{message='Insufficient permissions.'}};expected=$false},
    @{status=403;body=@{error=@{code='InsufficientAccessError';innererror=@{code='NspValidationFailedError';message='Access to workspace from this address is denied. To allow access from public networks, change the workspace Networking settings.'}}};expected=$true},
    @{status=403;body=@{error=@{code='InsufficientAccessError';message='The provided credentials have insufficient access.'}};expected=$false},
    @{status=401;body=@{error=@{message='Unauthorized.'}};expected=$false},
    @{status=0;body=$null;expected=$false},
    @{status=204;body=$null;expected=$false}
)) { if ((Test-NetworkDenial $case) -ne $case.expected) { throw 'Network-denial classifier failed.' } }
if ((Get-ServiceErrorCode @{body=@{error=@{code='PrivateLinkIdNotAllowed'}}}) -ne 'PrivateLinkIdNotAllowed') { throw 'Service code extraction failed.' }
if ((Get-ServiceErrorCode @{body=@{error=@{code='unsafe/value'}}}) -ne 'NoStructuredCode') { throw 'Service code allowlist failed.' }
$probe='{"query":{"status":200,"body":{"tables":[{"columns":[{"name":"Marker"},{"name":"Records"}],"rows":[["example",2]]}]}}}' | ConvertFrom-Json -AsHashtable
$rows=Get-QueryRows $probe
if ($rows[0].Marker -ne 'example' -or $rows[0].Records -ne 2) { throw 'Column-name response parsing failed.' }
# Execute the complete preflight against a read-only mock transport. Any write fails.
Initialize-Context $root '' 'westeurope' 'Standard_B1s' 30
$script:State=$null
$script:ReadCalls=[Collections.Generic.List[string]]::new()
function Invoke-Az { param([string[]]$Arguments)
    $allowed=@('account show','account get-access-token','group exists','provider show','vm list-skus','vm list-usage','network list-usages','vm image','rest --method')
    $prefix=($Arguments[0..1] -join ' ')
    if ($prefix -notin $allowed) { throw "Unexpected preflight operation: $prefix" }
    if ($Arguments[0] -eq 'rest' -and $Arguments[2] -ne 'GET') { throw 'Mutating REST operation in preflight.' }
    $script:ReadCalls.Add($prefix)
    switch ($prefix) {
        'account show' { return @{id='test-subscription';state='Enabled'} }
        'account get-access-token' { return @{} }
        'group exists' { return $false }
        'provider show' {
            $types=@('virtualNetworks','privateEndpoints','natGateways','publicIPAddresses','privateDnsZones','virtualMachines','userAssignedIdentities','workspaces','dataCollectionEndpoints','dataCollectionRules','privateLinkScopes')
            return @{registrationState='Registered';resourceTypes=@($types | ForEach-Object { @{resourceType=$_;apiVersions=@($script:Api.Values)} })}
        }
        'vm list-skus' { return ,@{name='Standard_B1s';family='standardBSFamily';restrictions=@();capabilities=@(@{name='vCPUs';value='1'})} }
        'vm list-usage' { return @(@{name=@{value='cores'};limit=32;currentValue=0},@{name=@{value='standardBSFamily'};limit=20;currentValue=0}) }
        'network list-usages' { return ,@{name=@{value='PublicIPAddresses'};limit=10;currentValue=0} }
        'vm image' { return @{} }
        default { throw "Unhandled mock command: $prefix" }
    }
}
function Invoke-Arm($Method,$Id,$Version,$Body=$null) {
    if ($Method -ne 'GET' -or $Body) { throw 'Preflight attempted an ARM mutation.' }
    $script:ReadCalls.Add('ARM GET')
    return @{value=@(@{actions=@('*');notActions=@()})}
}
Invoke-Preflight
if ($script:ReadCalls.Count -lt 12) { throw 'Preflight did not exercise its complete transport path.' }
$script:RequestedSubscription='different-subscription'
try { Invoke-Preflight;throw 'Expected subscription rejection.' } catch { if ($_ -notmatch 'Requested subscription is not active') { throw } }
$script:RequestedSubscription=''
$script:State=@{Locked=$true;Completed=@{Foundation='passed';PrivateLink='passed'};Evidence=@{PublicBaseline=@{Passed=$true}}}
$before=$script:ReadCalls.Count
Invoke-PublicBaseline
if ($script:ReadCalls.Count -ne $before) { throw 'Locked public-baseline rerun made an Azure call.' }
$script:State.Evidence=@{}
try { Invoke-PublicBaseline;throw 'Expected fresh-deployment rejection.' } catch { if ($_ -notmatch 'Fresh deployment required') { throw } }
$script:State=@{Session='test-session';Subscription='test-subscription';Location='westeurope';VmSize='Standard_B1s';Resources=[Collections.Generic.List[string]]::new()}
function Invoke-Arm($Method,$Id,$Version,$Body=$null) { return @{tags=@{session='someone-else'};properties=@{provisioningState='Succeeded'}} }
try { Ensure-Resource '/test/resource' 'test-api' @{};throw 'Expected collision rejection.' } catch { if ($_ -notmatch 'Unexpected existing resource') { throw } }
$script:State.Resources.Add('/test/resource')
try { Ensure-Resource '/test/resource' 'test-api' @{};throw 'Expected tag rejection.' } catch { if ($_ -notmatch 'ownership tag mismatch') { throw } }
Write-Host 'PASS: AST, denial classification, named-column parsing, complete read-only preflight, subscription mismatch, no-reopen baseline rerun, resource collision and ownership-tag rejection.'
