Param(
  [string] $GuardianCliLocation,
  [string] $WorkingDirectory,
  [string] $TargetDirectory,
  [string] $GdnFolder,
  # The list of Guardian tools to configure. For each object in the array:
  # - If the item is a [hashtable], it must contain these entries:
  #   - Name = The tool name as Guardian knows it.
  #   - Scenario = (Optional) Scenario-specific name for this configuration entry. It must be unique
  #     among all tool entries with the same Name.
  #   - Args = (Optional) Array of Guardian tool configuration args, like '@("Target > C:\temp")'
  # - If the item is a [string] $v, it is treated as '@{ Name="$v" }'
  [object[]] $ToolsList,
  [string] $GuardianLoggerLevel='Standard',
  # Optional: Additional params to add to any tool using CredScan.
  [string[]] $CrScanAdditionalRunConfigParams,
  # Optional: Additional params to add to any tool using PoliCheck.
  [string[]] $PoliCheckAdditionalRunConfigParams,
  # Optional: Additional params to add to any tool using CodeQL/Semmle.
  [string[]] $CodeQLAdditionalRunConfigParams,
  # Optional: Additional params to add to any tool using Binskim.
  [string[]] $BinskimAdditionalRunConfigParams
)

$ErrorActionPreference = 'Stop'
Set-StrictMode -Version 2.0
$disableConfigureToolsetImport = $true
$global:LASTEXITCODE = 0

try {
  # `tools.ps1` checks $ci to perform some actions. Since the SDL
  # scripts don't necessarily execute in the same agent that run the
  # build.ps1/sh script this variable isn't automatically set.
  $ci = $true
  . $PSScriptRoot\..\tools.ps1

  # Normalize tools list: all in [hashtable] form with defined values for each key.
  $ToolsList = $ToolsList |
    ForEach-Object {
      if ($_ -is [string]) {
        $_ = @{ Name = $_ }
      }

      if (-not ($_['Scenario'])) { $_.Scenario = "" }
      if (-not ($_['Args'])) { $_.Args = @() }
      $_
    }
  
  Write-Host "List of tools to configure:"
  $ToolsList | ForEach-Object { $_ | Out-String | Write-Host }

  # We store config files in the r directory of .gdn
  $gdnConfigPath = Join-Path $GdnFolder 'r'
  $ValidPath = Test-Path $GuardianCliLocation

  if ($ValidPath -eq $False)
  {
    Write-PipelineTelemetryError -Force -Category 'Sdl' -Message "Invalid Guardian CLI Location."
    ExitWithExitCode 1
  }

  foreach ($tool in $ToolsList) {
    # Put together the name and scenario to make a unique key.
    $toolConfigName = $tool.Name
    if ($tool.Scenario) {
      $toolConfigName += "_" + $tool.Scenario
    }

    Write-Host "=== Configuring $toolConfigName..."

    $gdnConfigFile = Join-Path $gdnConfigPath "$toolConfigName-configure.gdnconfig"

    # For some tools, add default and automatic args.
    switch -Exact ($tool.Name) {
      'credscan' {
        if ($targetDirectory) {
          $tool.Args += "`"TargetDirectory < $TargetDirectory`""
        }
        $tool.Args += "`"OutputType < pre`""
        $tool.Args += $CrScanAdditionalRunConfigParams
      }
      'policheck' {
        if ($targetDirectory) {
          $tool.Args += "`"Target < $TargetDirectory`""
        }
        $tool.Args += $PoliCheckAdditionalRunConfigParams
      }
      {$_ -in 'semmle', 'codeql'} {
        if ($targetDirectory) {
          $tool.Args += "`"SourceCodeDirectory < $TargetDirectory`""
        }
        $tool.Args += $CodeQLAdditionalRunConfigParams
      }
      'binskim' {
        if ($targetDirectory) {
          # Binskim crashes due to specific PDBs. GitHub issue: https://github.com/microsoft/binskim/issues/924.
          # We are excluding all `_.pdb` files from the scan.
          $tool.Args += "`"Target < $TargetDirectory\**;-:file|$TargetDirectory\**\_.pdb`""
        }
        $tool.Args += $BinskimAdditionalRunConfigParams
      }
    }

    # Create variable pointing to the args array directly so we can use splat syntax later.
    $toolArgs = $tool.Args

    # Configure the tool. If args array is provided or the current tool has some default arguments
    # defined, add "--args" and splat each element on the end. Arg format is "{Arg id} < {Value}",
    # one per parameter. Doc page for "guardian configure":
    # https://dev.azure.com/securitytools/SecurityIntegration/_wiki/wikis/Guardian/1395/configure
    Exec-BlockVerbosely {
      & $GuardianCliLocation configure `
        --working-directory $WorkingDirectory `
        --tool $tool.Name `
        --output-path $gdnConfigFile `
        --logger-level $GuardianLoggerLevel `
        --noninteractive `
        --force `
        $(if ($toolArgs) { "--args" }) @toolArgs
      Exit-IfNZEC "Sdl"
    }

    Write-Host "Created '$toolConfigName' configuration file: $gdnConfigFile"
  }
}
catch {
  Write-Host $_.ScriptStackTrace
  Write-PipelineTelemetryError -Force -Category 'Sdl' -Message $_
  ExitWithExitCode 1
}
