$ErrorActionPreference = 'Stop';
Set-StrictMode -Version '2.0'

<#
.SYNOPSIS
Creates a set of files under a base folder for file set testing purposes.
#>
function New-TestFiles([string]$BaseFolder, [string[]]$pathDefinitions) {

	if (-not (Test-Path $BaseFolder)) {
		mkdir $BaseFolder -Force | Out-Null
	}

	$BaseFolder = Resolve-Path $BaseFolder

	foreach ($pathDef in $pathDefinitions) {
		if ((-not $pathDef) -or $pathDef -eq '\') {
			continue
		}

		$fullPathDef = Join-Path $BaseFolder $pathDef

		if ($pathDef.EndsWith('\')) {
			mkdir $fullPathDef -Force | Out-Null
		} else {
			$parentDir = Split-Path $fullPathDef -Parent
			mkdir $parentDir -Force | Out-Null
			Set-Content -Path $fullPathDef -Value $fullPathDef -Encoding 'utf8'

			Write-Output $fullPathDef
		}
	}
}

#
# custom Pester assertions
#

$pesterModule = Import-Module Pester -PassThru

. $pesterModule {
	$script:PesterContainAllFilesFrom_missingFiles = $null
	$script:PesterContainFilesFrom_missingFiles = $null
	$script:PesterContainFilesFrom_incorrectFiles = $null

	function PesterContainAllFilesFrom($ActualTargetPath, $ExpectedSourcePath) {
		$sourceFiles = Get-FileSetSourceTree $ExpectedSourcePath

		$script:PesterContainAllFilesFrom_missingFiles = @(
			$sourceFiles | 
			ForEach-Object { Join-Path $ActualTargetPath $_ } | 
				Where-Object { -not (& $SafeCommands['Test-Path'] $_) }
		)

		# fail when missing files found
		return $script:PesterContainAllFilesFrom_missingFiles.Count -eq 0;
	}

	function PesterContainAllFilesFromFailureMessage($ActualTargetPath, $ExpectedSourcePath) {
		$missingFilesBullets = ($script:PesterContainAllFilesFrom_missingFiles | ForEach-Object { "  * $_"}) -join "`r`n"

		return @"
Expected all files from: $ExpectedSourcePath
To exist in:             $ActualTargetPath
But the following files were missing:
$missingFilesBullets
"@
	}

	function NotPesterContainFilesFromFailureMessage($ActualTargetPath, $ExpectedSourcePath) {
		throw 'Not isn''t supported for this assertion'
	}

	function PesterContainFilesFrom($ActualTargetPath, [object[]]$ExpectedArgs) {

		# the content of the files should be their full original path

		if ($ExpectedArgs.Count -ne 2) {
			throw "The ExpectedArgs parameter should be an array of 2 containing the ExpectedSourcePath and ExpectedSubPaths"
		}
		$ExpectedSourcePath = [string]$ExpectedArgs[0]
		$ExpectedSubPaths = [string[]]$ExpectedArgs[1]

		$script:PesterContainFilesFrom_missingFiles = @()
		$script:PesterContainFilesFrom_incorrectFiles = @()

		foreach ($path in $ExpectedSubPaths) {
			$actualFullPath = (Join-Path $ActualTargetPath $path)
			$expectedFullPath = (Join-Path $ExpectedSourcePath $path) # this path should be the content of the file

			if (-not (& $SafeCommands['Test-Path'] $actualFullPath)) {
				$script:PesterContainFilesFrom_missingFiles += $actualFullPath
			} else {
				$fileContent = (Get-Content -Path $actualFullPath -Encoding utf8 | Select-Object -First 1)

				if ($fileContent -ne $expectedFullPath) {
					$script:PesterContainFilesFrom_incorrectFiles += $actualFullPath
				}
			}
		}

		# fail when missing or incorrect files found
		return $script:PesterContainFilesFrom_missingFiles.Count -eq 0 `
			-and $script:PesterContainFilesFrom_incorrectFiles.Count -eq 0;
	}

	function PesterContainFilesFromFailureMessage($ActualTargetPath, [object[]]$ExpectedArgs) {
		if ($ExpectedArgs.Count -ne 2) {
			throw "The ExpectedArgs parameter should be an array of 2 containing the ExpectedSourcePath and ExpectedSubPaths"
		}
		$ExpectedSourcePath = [string]$ExpectedArgs[0]
		$ExpectedSubPaths = [string[]]$ExpectedArgs[1]

		$missingFilesBullets = ($script:PesterContainFilesFrom_missingFiles | ForEach-Object { "  * $_"}) -join "`r`n"
		$incorrectFilesBullets = ($script:PesterContainFilesFrom_incorrectFiles | ForEach-Object { "  * $_"}) -join "`r`n"

		$message = @"
Expected sub-paths: $ExpectedSubPaths
From:               $ExpectedSourcePath
To be copied to:    $ActualTargetPath
But...
"@

		if (($script:PesterContainFilesFrom_missingFiles.Count) -gt 0) {
			$message += @"

- the following files were missing:
$missingFilesBullets
"@
		}

		if ($script:PesterContainFilesFrom_incorrectFiles.Count -gt 0) {
			$message += @"

- the following files were present, but the file content didn't indicate that they were copied from the source:
$incorrectFilesBullets
"@
		}

		return $message
	}

	function NotPesterContainAllFilesFromFailureMessage($ActualTargetPath, [object[]]$ExpectedArgs) {
		throw 'Not isn''t supported for this assertion'
	}
}

