<# 
 .Synopsis
  Common module

 .Description
  Common module to give core funtionality for IIS scaffoldong.
#>

#region Private variables

$loggingDirectory = "C:\Temp"
$loggingFile = "Install.log"
$logFile = Join-Path $loggingDirectory $loggingFile

#endregion

#region Public methods

function Backup-Database($sqlInstaceName, $databaseName, $userName, $securePassword, $backupPath)
{
    if(!(Test-Path $backupPath))
    {
        New-Item $backupPath -type directory
    }

    $timestamp = Get-Date -Format yyyyMMddHHmmss

    $credential = New-Object System.Management.Automation.PSCredential ($userName, $securePassword)

    Backup-SqlDatabase -ServerInstance $sqlInstaceName -Database $databaseName -BackupFile ( Join-Path $backupPath "$($databaseName)_db_$($timestamp).bak") -Verbose -Credential $credential -ErrorAction Continue
}

function Drop-Database($sqlInstaceName, $databaseName, $userName, $securePassword)
{
    $server = New-Object -TypeName Microsoft.SqlServer.Management.Smo.Server -ArgumentList $sqlInstaceName

    $server.ConnectionContext.LoginSecure = $false
    $server.ConnectionContext.set_Login($userName)

    $server.ConnectionContext.set_SecurePassword($securePassword)

    $dbObject = $server.Databases[$databaseName]

    if ($dbObject)
    {
        $server.KillDatabase($databaseName)
    }
}

function Restore-Database($sqlInstaceName, $databaseName, $userName, $securePassword, $backupFileToRestore)
{
    $credential = New-Object System.Management.Automation.PSCredential ($userName, $securePassword)

    $server = New-Object -TypeName Microsoft.SqlServer.Management.Smo.Server -ArgumentList $sqlInstaceName

    $server.ConnectionContext.LoginSecure = $false
    $server.ConnectionContext.set_Login($userName)

    $server.ConnectionContext.set_SecurePassword($securePassword)

    $dataFolder = $server.Settings.DefaultFile;
    $logFolder = $server.Settings.DefaultLog;

    if ($dataFolder.Length -eq 0)
    {
        $dataFolder = $server.Information.MasterDBPath;
    }

    if ($logFolder.Length -eq 0) 
    {
        $logFolder = $server.Information.MasterDBLogPath;
    }

    $backupDeviceItem = new-object Microsoft.SqlServer.Management.Smo.BackupDeviceItem $backupFileToRestore, 'File';

    $restore = new-object 'Microsoft.SqlServer.Management.Smo.Restore';
    $restore.Database = $databaseName;
    $restore.Devices.Add($backupDeviceItem);

    $dataFileNumber = 0;

    foreach ($file in $restore.ReadFileList($server)) 
    {
        $relocateFile = new-object 'Microsoft.SqlServer.Management.Smo.RelocateFile';
        $relocateFile.LogicalFileName = $file.LogicalName;

        if ($file.Type -eq 'D'){
            if($dataFileNumber -ge 1)
            {
                $suffix = "_$dataFileNumber";
            }
            else
            {
                $suffix = $null;
            }

            $relocateFile.PhysicalFileName = "$dataFolder\$DatabaseName$suffix.mdf";

            $dataFileNumber ++;
        }
        else 
        {
            $relocateFile.PhysicalFileName = "$logFolder\$DatabaseName.ldf";
        }

        $restore.RelocateFiles.Add($relocateFile) | out-null;
    }    

    $restore.SqlRestore($server);
}

function CheckCredentials
{
    If (-NOT ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole(`
        [Security.Principal.WindowsBuiltInRole] “Administrator”))
    {
        Write-Warning “You do not have Administrator rights to run this script!`nPlease re-run this script as an Administrator!”
        return $false
    }

    Write-Host "Done."
    return $true
}

function GrantPermissionToApplicationPool($iisApplicationPoolName, $containerDirectoryPath)
{
    Write-Host "Grant RW directory level permission for" $iisApplicationPoolName "(" $containerDirectoryPath ")"
    $fullApplicationPoolName = "IIS AppPool\" + $iisApplicationPoolName

    GrantModifyPermissionToFolder $fullApplicationPoolName $containerDirectoryPath "Modify"

    Write-Host "Grant R directory level permission for IUSR" "(" $containerDirectoryPath ")"
    GrantModifyPermissionToFolder "IUSR" $containerDirectoryPath "Read"
}

function GrantModifyPermissionToFolder($userName, $path, $accessLevel)
{
    $acl = Get-Acl $path
    $ar = New-Object System.Security.AccessControl.FileSystemAccessRule($userName, $accessLevel, "ContainerInherit,ObjectInherit", "None", "Allow")

    $acl.SetAccessRule($ar)
    Set-Acl $path $acl
}

function ScaffoldIIS($hostName, $hostIp, $iisApplicationPoolName, $iisApplicationPoolDotNetVersion, $iisApplicationName, $containerDirectoryPath)
{
    SetHostsFile $hostName $hostIp

    RemoveDefaultWebSiteIfNeeded

    CreateOrUpdateApplicationPool $iisApplicationPoolName $iisApplicationPoolDotNetVersion
    CreateOrUpdateSite $iisApplicationName $iisApplicationPoolName $containerDirectoryPath $hostName

    GrantPermissionToApplicationPool $iisApplicationPoolName $containerDirectoryPath

    #foreach($application in $targetWebApplications)
    #{
    #    CreateOrUpdateApplication "$iisApplicationName\Kreta.Web.$application" (Join-Path $projectBasePath "$application\KretaWeb")
    #}

    Write-Host "IIS scaffolding has been successfully completed!"
}

function StartLogging
{
    ScaffoldLoggingContainer

    try { Stop-Transcript } catch {}
    Start-Transcript -path $logFile -append
}

function StopLogging
{
    try { Stop-Transcript } catch {}
}

function EnableEmbeddedIISFeatures
{
    cd $PSScriptRoot

    $featureList = @("IIS-WebServerRole",
    "IIS-WebServer",
    "IIS-CommonHttpFeatures",
    "IIS-HttpErrors",
    "IIS-HttpRedirect",
    "IIS-ApplicationDevelopment",
    "IIS-NetFxExtensibility",
    "IIS-NetFxExtensibility45",
    "IIS-HealthAndDiagnostics",
    "IIS-HttpLogging",
    "IIS-LoggingLibraries",
    "IIS-RequestMonitor",
    "IIS-HttpTracing",
    "IIS-Security",
    "IIS-URLAuthorization",
    "IIS-RequestFiltering",
    "IIS-IPSecurity",
    "IIS-Performance",
    "IIS-HttpCompressionDynamic",
    "IIS-WebServerManagementTools",
    "IIS-ManagementScriptingTools",
    "IIS-IIS6ManagementCompatibility",
    "IIS-Metabase",
    "IIS-CertProvider",
    "IIS-WindowsAuthentication",
    "IIS-DigestAuthentication",
    "IIS-ClientCertificateMappingAuthentication",
    "IIS-IISCertificateMappingAuthentication",
    "IIS-StaticContent",
    "IIS-DefaultDocument",
    "IIS-DirectoryBrowsing",
    "IIS-WebSockets",
    "IIS-ApplicationInit",
    "IIS-ASPNET",
    "IIS-ASPNET45",
    "IIS-ASP",
    "IIS-CGI",
    "IIS-ISAPIExtensions",
    "IIS-ISAPIFilter",
    "IIS-ServerSideIncludes",
    "IIS-CustomLogging",
    "IIS-BasicAuthentication",
    "IIS-HttpCompressionStatic",
    "IIS-ManagementConsole",
    "IIS-ManagementService")

    Enable-WindowsOptionalFeature -FeatureName $featureList -Online > $null

    Write-Host "Done."
}

function ResetEnvironment
{
    cd $PSScriptRoot
    Write-Host "Done."
}

function ResetIIS
{
    & {iisreset} > $null
    Write-Host "Done."
}

function AddSslBindingToIdpAndAddAsTrustedCert($siteName, $dnsName)
{
    $newCert = New-SelfSignedCertificate -DnsName $dnsName -CertStoreLocation cert:\LocalMachine\My

    New-WebBinding -Name $siteName -HostHeader $dnsName -Port 443 -Protocol https

    $binding = Get-WebBinding -Name $siteName -Protocol "https"

    $binding.AddSslCertificate($newCert.GetCertHashString(), "my")

    $tempCert = Join-Path "c:\Temp" $dnsName

    Export-Certificate -Cert $newCert -FilePath $tempCert -Type SST

    Import-Certificate -CertStoreLocation cert:\CurrentUser\Root -FilePath $tempCert

    Remove-Item $tempCert

    Write-Host "Done."
}

#endregion

#region Private methods

function SetHostsFile($hostName, $ip)
{
    $hostsPath = "$env:windir\System32\drivers\etc\hosts"
    $measure = Get-Content $hostsPath | Select-String $hostName | measure | select -Property Count

    if($measure.Count -eq 0)
    {
        "`r`n" + $ip + "`t" + $hostName | Out-File -encoding ASCII -append $hostsPath
        Write-Host $hostsPath "Host file has been successfully updated!"
    }
}

function CreateOrUpdateApplicationPool($iisAppPoolName, $iisAppPoolDotNetVersion)
{
    #navigate to the app pools root
    cd IIS:\AppPools\

    #check if the app pool exists
    if (!(Test-Path $iisAppPoolName -pathType container))
    {
        #create the app pool
        $appPool = New-Item $iisAppPoolName
        $appPool | Set-ItemProperty -Name "managedRuntimeVersion" -Value $iisAppPoolDotNetVersion
        Write-Host "$iisAppPoolName (application pool) has been successfully created!"
    }
}

function RemoveDefaultWebSiteIfNeeded
{
    cd IIS:\Sites\

    if (!(Test-Path "DefaultWebSite" -pathType container))
    {
        return
    }

    Remove-Item IIS:\Sites\DefaultWebSite -Recurse

    Write-Host "DefaultWebSite has been successfully deleted!"
}

function CreateOrUpdateSite($iisAppName, $iisAppPoolName, $directoryPath, $listenerHost)
{
    #navigate to the sites root
    cd IIS:\Sites\

    #check if the site exists
    if (Test-Path $iisAppName -pathType container)
    {
        $targetApplication = "IIS:\Sites\$iisAppName"
        $previousSite = Get-Item $targetApplication
        $previousSiteLocation = $previousSite.physicalPath

        Remove-Item $targetApplication -Recurse -Force > $null

        Write-Host "Previous $iisAppName (site) has been successfully deleted! Physical location: $previousSiteLocation"
    }

    #create the site
    $iisApp = New-Item $iisAppName -bindings @{protocol="http";bindingInformation=":80:" + $listenerHost} -physicalPath $directoryPath
    $iisApp | Set-ItemProperty -Name "applicationPool" -Value $iisAppPoolName

    $newSite = Get-Item "IIS:\Sites\$iisAppName"
    $newSiteLocation = $newSite.physicalPath

    Write-Host "$iisAppName (site) has been successfully created! Physical location: $newSiteLocation"
}

function CreateOrUpdateApplication($applicationLocation, $directoryPath)
{
    $location = "IIS:\Sites\$applicationLocation"

    $measure = Get-Item $location -ErrorAction Ignore | measure | Select -Property Count 

    if($measure.Count -eq 0)
    {
        New-Item $location -Type Application -PhysicalPath $directoryPath > $null
        Write-Host "$location (application) has been successfully created!"
    }
}

function ScaffoldLoggingContainer
{
    if(!(Test-Path $loggingDirectory))
    {
        New-Item $loggingDirectory -ItemType Directory > $null
    }

    if((Test-Path $logFile))
    {
        Remove-Item $logFile -Force > $null
    }

    New-Item $logFile -ItemType File > $null
}

function CheckLocalDbAvailable
{
    $commandAvailable = Get-Command sqllocaldb -ErrorAction Ignore
    
    if([string]::IsNullOrWhiteSpace($commandAvailable))
    {
        return $false
    }

    return $true
}

function CheckDbInstance($databaseName)
{
    $result = SqlLocalDB.exe info | select-string -pattern $databaseName | measure

    if($result.Count -eq 2)
    {
        return $true
    }

    return $false
}

function CheckAndInstallLocalDb($databaseName)
{
    if(!(CheckLocalDbAvailable))
    {
        Write-Error "LocalDb is not available on your Computer!"
        exit 1
    }

    if(!(CheckDbInstance $databaseName))
    {
        Write-Host "Creating LocalDb..." $databaseName
        SqlLocalDB.exe create $databaseName
        SqlLocalDB.exe share $databaseName $databaseName
        Write-Host "Starting LocalDb..." $databaseName
        SqlLocalDB.exe start $databaseName
        Write-Host "Grant access to IIS APPPOOL\Kreta.IdentityProvider for LocalDb..." $databaseName
        GrantIdpAppPoolForLocalDb $databaseName
    }
    else
    {
        Write-Host $databaseName " is already exist!"
    }
}

function GrantIdpAppPoolForLocalDb($databaseName)
{
    $server = "(localdb)\.\" + $databaseName
    sqlcmd -S $server -i GrantIdpForLocalDb.sql
}

#endregion

#region Exports
export-modulemember -function CheckAndInstallLocalDb
export-modulemember -function CheckCredentials
export-modulemember -function ResetIIS
export-modulemember -function ResetEnvironment
export-modulemember -function StartLogging
export-modulemember -function StopLogging
export-modulemember -function ScaffoldIIS
export-modulemember -function EnableEmbeddedIISFeatures
export-modulemember -function Backup-Database
export-modulemember -function Drop-Database
export-modulemember -function Restore-Database
export-modulemember -function AddSslBindingToIdpAndAddAsTrustedCert
#endregion