using namespace System.Diagnostics
using namespace System.Collections
using namespace System.Collections.Generic
using namespace System.IO.Compression

param ( [switch]$NoRun )

function Assert( [bool]$condition, [string]$message = "Assertion failed" ){
  if( -not $condition ) {
    Get-PSCallStack | ForEach-Object { Write-Host $_.FunctionName $_.Location $_.Arguments }
    throw $message
  }
}

function CreateExampleConfigs {

  Write-Warning "Creating test files ExampleConfigs\Web.config and Web.UnitTest.config..."
  Write-Host "Creating test files ExampleConfigs\Web.config and Web.UnitTest.config..."
  mkdir ExampleConfigs -ErrorAction SilentlyContinue
  @'
<?xml version="1.0" encoding="utf-8"?>
<configuration>
    <someSetting name="someSetting" from="Web.config" to="OriginalValueHere" subject="Test variable substitution" />
    <appSettings>
        <add key="TestKey" value="TestValue" />
    </appSettings>
</configuration>
'@ | Out-File .\ExampleConfigs\Web.config

@'
<?xml version="1.0" encoding="utf-8"?>
<configuration>
    <someSetting name="someSetting" from="Web.UnitTest.config" to="$(SomeSetting.EmailTo)" subject="Test variable substitution" />
    <appSettings>
        <add key="TestKey" value="TestValue" />
    </appSettings>
</configuration>
'@ | Out-File .\ExampleConfigs\Web.UnitTest.config

}

function Setup {

  $env:SomeSetting_EmailTo = "ParametersEmailTo@replaced.com"
  $env:RELEASE_ENVIRONMENTNAME = "UnitTest"

  if (-not (Test-Path ExampleConfigs\Web.config) -or -not (Test-Path ExampleConfigs\Web.UnitTest.config)) { 
    CreateExampleConfigs 
  }

  Remove-Item TempExpanded -Recurse -ErrorAction SilentlyContinue

  $primaryArtifactSourceAlias = "TestArtifactSourceAlias"
  function Release.PrimaryArtifactSourceAlias { $primaryArtifactSourceAlias }

  if (Test-Path $primaryArtifactSourceAlias\Drop.bak.zip) {
    Remove-Item $primaryArtifactSourceAlias\Drop.zip -ErrorAction SilentlyContinue
    Rename-Item $primaryArtifactSourceAlias\Drop.bak.zip Drop.zip
  }
  else {
    mkdir $primaryArtifactSourceAlias -ErrorAction SilentlyContinue
    Compress-Archive ExampleConfigs $primaryArtifactSourceAlias\drop.zip -Force
  }
}

function TestTextTransform {

  $content = " to=""`$(SomeSetting.EmailTo)"" subject=""ELMAH [TEST]"""
  $replacements =  @( @{"SomeSetting_EmailTo"="Replaced"}.GetEnumerator() )

  $expected = " to=""Replaced"" subject=""ELMAH [TEST]"""

  $actual = TextTransform $content $replacements "[TestTextTransform]"

  Assert ($actual -eq $expected) "Failed matching case Replace: Expected: $expected, Actual: $actual"
  "TestTextTransform:With Exact Case Match:Passed"


  $replacementsUpperCase =  @( @{"SomeSetting_EmailTo"="Replaced"}.GetEnumerator() )
  $actual2 = TextTransform $content $replacementsUpperCase "[TestTextTransform Case Insensitive]"

  Assert ($actual2 -eq $expected) "Failed Case Insensitive Replace: Expected: $expected, Actual: $actual2"
  "TestTextTransform:Case Insensitive:Passed"
}

function TestTransformFile {

  #Arrange
  mkdir ExampleConfigsCopy -ErrorAction SilentlyContinue

  try{

    #Arrange
    Copy-Item ExampleConfigs\Web.config , .\ExampleConfigs\Web.UnitTest.config ExampleConfigsCopy
    $files= Get-ChildItem ExampleConfigsCopy\*  
    $original0 = Get-Content ExampleConfigsCopy\Web.UnitTest.config -Raw

    # Act
    TransformFiles $files

    # Assert

    $actual0 = Get-Content ExampleConfigsCopy\Web.UnitTest.config -Raw

    Assert ($original0 -ne $actual0)  "Original: $original0, Actual: $actual0"
    Assert ($actual0 -match $env:SomeSetting_EmailTo) "$files should contain `$env:SomeSetting_EmailTo = $env:SomeSetting_EmailTo but doesn't"

    $appSettingsNode= (Select-Xml -Path $files[0] -XPath "/configuration/appSettings").
                        Node
    Assert ($null -ne $appSettingsNode) "Failed to parse appSettings note in $files[0] after transform, got null for /configuration/appSettings"
    "TestTranformFiles: Passed"
  }
  finally { Remove-Item .\ExampleConfigsCopy -Recurse -ErrorAction SilentlyContinue }
}

function TestExpandAndRecreateArtifactZipFile {

  $artifactZipPath = "$primaryArtifactSourceAlias\drop.zip"
  $backupfileName = $artifactZipPath -replace ".zip$",".bak.zip"

  ExpandArtifactZipToTempExpandedFolder $artifactZipPath

  Push-Location TempExpanded
    try
    { 
      BackupAndReCreateArtifactZipFile $zipfile    
    } 
    finally { Pop-Location }


  Assert (Test-Path $artifactZipPath) "Expected replacement zip file $artifactZipPath not found"

  Assert (Test-Path $backupfileName) "Expected Backup file $backupfileName not found"

  "TestExpandAndRecreateArtifactZipFile:Zip file and bak.zip file exist:Passed"

  "TestExpandAndRecreateArtifactZipFile:Compare: $artifactZipPath, $backupfileName"
  # Resolve-Path $artifactZipPath 
  # Resolve-Path $backupfileName
  # Get-Location

  $zip1 = [ZipFile]::OpenRead( (Resolve-Path $artifactZipPath) )
  $zip2 = [ZipFile]::OpenRead( (Resolve-Path $backupfileName) )

  $toTuple = [Func[ZipArchiveEntry, ZipArchiveEntry, [Tuple[ZipArchiveEntry,ZipArchiveEntry]]]]{
      param ($entry1, $entry2)
      return [tuple]::Create( $entry1, $entry2 )
  }

  $dirEntries= [System.Linq.Enumerable]::Zip($zip1.Entries, $zip2.Entries, $toTuple )
  $zip1.Dispose()
  $zip2.Dispose()

  $dirEntries | ForEach-Object {
    $entry1 = [ZipArchiveEntry]$_.Item1
    $entry2 = [ZipArchiveEntry]$_.Item2
    Assert ($entry1.FullName -eq $entry2.FullName) "Files in $artifactZipPath and $primaryArtifactSourceAlias\drop.bak.zip do not match"
    Assert ($entry1.Length -eq $entry2.Length) "Files in $artifactZipPath and $primaryArtifactSourceAlias\drop.bak.zip do not match"
  }

  "TestExpandAndRecreateArtifactZipFile:Zip file and bak.zip file have same directory entries:Passed"
}

function TestGetConfigFilesToEdit {

  "Expected:"
  Get-ChildItem TempExpanded -Recurse -Include "Web.config","Web.$env:RELEASE_ENVIRONMENTNAME.config"

  $actual = Get-ConfigFilesInExpandedFolder

  Assert ($null -ne $actual) "Expected `$actual to be set, but found null"
  Assert ($actual.Count -eq 2) "Expected to transform exactly 2 config files, but found:`n----------`n$($actual)`n---------"

  $configFilesToTransformIncludesWebConfig = $actual -match "TempExpanded(\\|/).+(\\|/)Web.config$"
  Assert $configFilesToTransformIncludesWebConfig.Count "Expected to transform web.config file but found:`n----------`n$($actual)`n---------"

  $configFilesToTransformIncludesWebTestConfig = $actual -match "TempExpanded(\\|/).+(\\|/)Web.$env:RELEASE_ENVIRONMENTNAME.config$"
  Assert $configFilesToTransformIncludesWebTestConfig.Count "Expected to transform web.$env:RELEASE_ENVIRONMENTNAME.config file but found:`n----------`n$($actual)`n---------"

  "TestGetConfigFilesToEdit: Passed"
}

# -----------------------------------------------------
# 
if($NoRun){ return }
# -----------------------------------------------------

$DebugPreferenceWas = $DebugPreference
$DebugPreference = "Continue"
try{
  . Setup

  $noRun=$true ; . .\DevAzureReleaseVariableTransform.ps1

  ShowDebuggingInformation

  TestTextTransform

  TestTransformFile

  TestExpandAndRecreateArtifactZipFile

  TestGetConfigFilesToEdit

}
finally { $DebugPreference = $DebugPreferenceWas }