[CmdletBinding()]
param(
    [Parameter(Mandatory)]
    [ValidatePattern('^[0-9a-fA-F-]{36}$')]
    [string] $SubscriptionId
)

. (Join-Path $PSScriptRoot 'NetworkSecurityPerimeterWorkshop.Common.ps1')

Assert-WorkshopSubscription -SubscriptionId $SubscriptionId | Out-Null
$runtime = Get-WorkshopRuntime
$outputs = Get-WorkshopTerraformOutputs -SubscriptionId $SubscriptionId
$work = Get-WorkshopWorkDirectory
$evidenceDirectory = Join-Path $PSScriptRoot 'evidence'
$resourceGroup = [string]$outputs.resource_group_name
$manifest = $outputs.workshop_manifest

$resources = Invoke-AzureCliJson -Arguments @(
    'resource', 'list', '--subscription', $SubscriptionId,
    '--resource-group', $resourceGroup
)
$storage = Invoke-AzureCliJson -Arguments @(
    'storage', 'account', 'show', '--subscription', $SubscriptionId,
    '--resource-group', $resourceGroup, '--name', [string]$outputs.storage_account_name
)
$vault = Invoke-AzureCliJson -Arguments @(
    'keyvault', 'show', '--subscription', $SubscriptionId,
    '--resource-group', $resourceGroup, '--name', [string]$outputs.key_vault_name
)
$nsgRules = Invoke-AzureCliJson -Arguments @(
    'network', 'nsg', 'rule', 'list', '--subscription', $SubscriptionId,
    '--resource-group', $resourceGroup, '--nsg-name', 'nsg-awz-nsp-test'
)
$roles = Invoke-AzureCliJson -Arguments @(
    'role', 'assignment', 'list', '--subscription', $SubscriptionId,
    '--assignee-object-id', [string]$outputs.test_identity_principal_id, '--all'
)
$diagnostic = Invoke-AzureCliJson -Arguments @(
    'monitor', 'diagnostic-settings', 'list', '--resource', [string]$manifest.perimeter_id
)
$diagnosticSettings = if ($diagnostic.PSObject.Properties.Name -contains 'value') {
    @($diagnostic.value)
}
else {
    @($diagnostic)
}

$associationResponse = Invoke-AzureCliJson -Arguments @(
    'rest', '--method', 'get',
    '--url', "https://management.azure.com$($manifest.perimeter_id)/resourceAssociations?api-version=2024-07-01"
)
$ruleResponse = Invoke-AzureCliJson -Arguments @(
    'rest', '--method', 'get',
    '--url', "https://management.azure.com$($manifest.profile_id)/accessRules?api-version=2024-07-01"
)
$associations = @($associationResponse.value)
$rules = @($ruleResponse.value)

$transitionResult = Get-Content -Raw -LiteralPath (Join-Path $work 'result-transition-seed.json') | ConvertFrom-Json -Depth 100
$deniedResult = Get-Content -Raw -LiteralPath (Join-Path $work 'result-enforced-denied.json') | ConvertFrom-Json -Depth 100
$allowedResult = Get-Content -Raw -LiteralPath (Join-Path $work 'result-enforced-allowed.json') | ConvertFrom-Json -Depth 100
$logResult = Get-Content -Raw -LiteralPath (Join-Path $work 'result-querylogs.json') | ConvertFrom-Json -Depth 100

$roleNames = @($roles.roleDefinitionName | Sort-Object -Unique)
$logCategories = @($logResult.rows.category | Sort-Object -Unique)
$privateEvidence = [ordered]@{
    capturedAtUtc = [DateTime]::UtcNow.ToString('o')
    resources = $resources
    storage = $storage
    vault = $vault
    nsgRules = $nsgRules
    roleAssignments = $roles
    diagnosticSettings = $diagnosticSettings
    perimeterAssociations = $associations
    profileAccessRules = $rules
    testResults = [ordered]@{
        transition = $transitionResult
        denied = $deniedResult
        allowed = $allowedResult
        logs = $logResult
    }
}
Write-WorkshopJson -Path (Join-Path $work 'validation-private.json') -Value $privateEvidence

$publicEvidence = [ordered]@{
    capturedAtUtc = [DateTime]::UtcNow.ToString('o')
    validation = 'PASS'
    resourceGroupIsolated = $true
    associatedServices = [ordered]@{
        expected = 3
        observed = $associations.Count
        finalMode = [string]$outputs.association_access_mode
    }
    explicitSubscriptionRule = [ordered]@{
        expected = 1
        observed = $rules.Count
        enabled = [bool]$outputs.subscription_rule_enabled
    }
    storage = [ordered]@{
        sharedKeyAccessDisabled = -not [bool]$storage.allowSharedKeyAccess
        publicEndpointAvailableToPerimeter = ([string]$storage.publicNetworkAccess -eq 'Enabled')
        minimumTls = [string]$storage.minimumTlsVersion
    }
    keyVault = [ordered]@{
        azureRbacEnabled = [bool]$vault.properties.enableRbacAuthorization
        publicEndpointAvailableToPerimeter = ([string]$vault.properties.publicNetworkAccess -eq 'Enabled')
    }
    testClient = [ordered]@{
        customInboundNsgRules = @($nsgRules).Count
        requiredDataRolesPresent = @(
            'Storage Blob Data Contributor',
            'Key Vault Secrets Officer',
            'Log Analytics Reader'
        ) | ForEach-Object { $_ -in $roleNames } | Where-Object { $_ -eq $true } | Measure-Object | Select-Object -ExpandProperty Count
    }
    diagnosticRouting = [ordered]@{
        settings = $diagnosticSettings.Count
        allLogsEnabled = @($diagnosticSettings.logs | Where-Object { $_.enabled -and $_.categoryGroup -eq 'allLogs' }).Count -gt 0
    }
    dataPlaneProof = [ordered]@{
        transitionStorageHttp = [int]$transitionResult.storageHttp
        transitionKeyVaultHttp = [int]$transitionResult.keyVaultHttp
        enforcedStorageHttp = [int]$deniedResult.storageHttp
        enforcedKeyVaultHttp = [int]$deniedResult.keyVaultHttp
        recoveredStorageHttp = [int]$allowedResult.storageHttp
        recoveredKeyVaultHttp = [int]$allowedResult.keyVaultHttp
        recoveredStorageDigestMatch = [bool]$allowedResult.storageContentMatch
        recoveredSecretValueMatch = [bool]$allowedResult.keyVaultValueMatch
    }
    logCategories = $logCategories
}

$checks = [ordered]@{
    associationCount = ($associations.Count -eq 3)
    finalAccessMode = ([string]$outputs.association_access_mode -eq 'Enforced')
    accessRuleCount = ($rules.Count -eq 1)
    storageSharedKeyDisabled = (-not [bool]$storage.allowSharedKeyAccess)
    keyVaultRbacEnabled = [bool]$vault.properties.enableRbacAuthorization
    noCustomInboundRules = (@($nsgRules).Count -eq 0)
    diagnosticSettingCount = ($diagnosticSettings.Count -eq 1)
    transitionStorageAllowed = ([int]$transitionResult.storageHttp -eq 200)
    transitionKeyVaultAllowed = ([int]$transitionResult.keyVaultHttp -eq 200)
    enforcedStorageDenied = ([int]$deniedResult.storageHttp -eq 403)
    enforcedKeyVaultDenied = ([int]$deniedResult.keyVaultHttp -eq 403)
    recoveredStorageAllowed = ([int]$allowedResult.storageHttp -eq 200)
    recoveredKeyVaultAllowed = ([int]$allowedResult.keyVaultHttp -eq 200)
    storageDigestMatched = [bool]$allowedResult.storageContentMatch
    secretValueMatched = [bool]$allowedResult.keyVaultValueMatch
    transitionLogSeen = ('NspPublicInboundResourceRulesAllowed' -in $logCategories)
    denialLogSeen = ('NspPublicInboundPerimeterRulesDenied' -in $logCategories)
    perimeterAllowLogSeen = ('NspPublicInboundPerimeterRulesAllowed' -in $logCategories)
}
$publicEvidence.checks = $checks
if (@($checks.Values | Where-Object { -not $_ }).Count -gt 0) {
    $publicEvidence.validation = 'FAIL'
}

Write-WorkshopJson -Path (Join-Path $evidenceDirectory 'live-validation.json') -Value $publicEvidence
$safeOutput = Protect-WorkshopText -Text ($publicEvidence | ConvertTo-Json -Depth 30) -Runtime $runtime -Outputs $outputs
Write-Output $safeOutput
if ($publicEvidence.validation -ne 'PASS') { throw 'One or more live evidence checks failed.' }
