Import-LocalizedData LocalizedData -FileName Microsoft.PowerShell.ODataUtilsStrings.psd1

function Assert-ODataUtilsPathIsSecured ($path)
{
    # Validate the ps1 filepath under the specified module installation directory
    $f = Get-item -path $path -ErrorAction Ignore
    if ($null -eq $f)
    {
        throw "$path not found"
    }

    # OS powershell modules under ${env:systemroot}\System32\WindowsPowershell\v1.0\modules
    $secured_modules = "${env:systemroot}\system32\WindowsPowershell\v1.0\Modules"

    if($path -notlike "$secured_modules\Microsoft.PowerShell.ODataUtils\*")
    {
        throw "$path is not under $secured_modules\Microsoft.PowerShell.ODataUtils"
    }
}

# This module doesn't support Arm because of Add-Type cmdlet
$ProcessorArchitecture = (Get-WmiObject -query "Select Architecture from Win32_Processor").Architecture

# 0 = x86
# 1 = MIPS
# 2 = Alpha
# 3 = PowerPC
# 5 = ARM
# 6 = Itanium
# 9 = x64
if ($ProcessorArchitecture -eq 5)
{
    throw $LocalizedData.ArchitectureNotSupported -f "ARM"
}

Assert-ODataUtilsPathIsSecured -path "$PSScriptRoot\Microsoft.PowerShell.ODataUtilsHelper.ps1"
. "$PSScriptRoot\Microsoft.PowerShell.ODataUtilsHelper.ps1"

#########################################################
# Generates PowerShell module containing client side 
# proxy cmdlets that can be used to interact with an 
# OData based server side endpoint.
######################################################### 
function Export-ODataEndpointProxy 
{
    [CmdletBinding(
    DefaultParameterSetName='CDXML',
    SupportsShouldProcess=$true,
    HelpUri="https://go.microsoft.com/fwlink/?LinkId=510069")]
    [OutputType([System.IO.FileInfo])]
    param
    (
        [Parameter(Position=0, Mandatory=$true, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true)]
        [ValidateNotNullOrEmpty()]
        [string] $Uri,

        [Parameter(Position=1, Mandatory=$true, ValueFromPipelineByPropertyName=$true)]
        [ValidateNotNullOrEmpty()]
        [string] $OutputModule,

        [Parameter(Position=2, ValueFromPipelineByPropertyName=$true)]
        [ValidateNotNullOrEmpty()]
        [string] $MetadataUri,

        [Parameter(Position=3, ValueFromPipelineByPropertyName=$true)]
        [PSCredential] $Credential,
	
        [Parameter(Position=4, ValueFromPipelineByPropertyName=$true)]
        [ValidateSet('Put', 'Post', 'Patch')]
        [string] $CreateRequestMethod='Post',
	
        [Parameter(Position=5, ValueFromPipelineByPropertyName=$true)]
        [ValidateSet('Put', 'Post', 'Patch')]
        [string] $UpdateRequestMethod='Patch',

        [Parameter(Position=6, ValueFromPipelineByPropertyName=$true)]
        [ValidateSet('ODataAdapter', 'NetworkControllerAdapter', 'ODataV4Adapter')]
        [string] $CmdletAdapter='ODataAdapter',

        [Parameter(Position=7, ValueFromPipelineByPropertyName=$true)]
        [Hashtable] $ResourceNameMapping,

        [parameter (Position=8,ValueFromPipelineByPropertyName=$true)]
        [switch] $Force,

        [Parameter(Position=9, ValueFromPipelineByPropertyName=$true)]
        [Hashtable] $CustomData,

        [parameter (Position=10,ValueFromPipelineByPropertyName=$true)]
        [switch] $AllowClobber,

        [parameter (Position=11,ValueFromPipelineByPropertyName=$true)]
        [switch] $AllowUnsecureConnection,

        [parameter (Position=12,ValueFromPipelineByPropertyName=$true)]
        [ValidateNotNull()]
        [Hashtable] $Headers
    )

    BEGIN 
    {
        if (!$MetadataUri)
        {
            $Uri = $Uri.TrimEnd('/')
            $MetadataUri = $Uri + '/$metadata'
            $PSBoundParameters["MetadataUri"] = $MetadataUri
        }

        # Validate to make sure that a valid URI is supplied as input.
        try
        {
            $connectionUri = [System.Uri]::new($Uri)
        }
        catch
        {
            $errorMessage = ($LocalizedData.InValidUri -f $Uri)
            $exception = [System.InvalidOperationException]::new($errorMessage, $_.Exception)
            $errorRecord = CreateErrorRecordHelper "ODataEndpointProxyInvalidUriFormat" $null ([System.Management.Automation.ErrorCategory]::InvalidArgument) $exception $Uri
            $PSCmdlet.ThrowTerminatingError($errorRecord)
        }

        # Block Redfish-support for in-box version of the module and advise user to use a version from PS Gallery instead.
        # According to Redfish specification DSP0266v1.0.1 Redfish Service Metadata Document and Redfish Service Root URIs (used by Export-ODataEndpointProxy) are required to start with '/redfish/v1' (section "6.3 Redfish-Defined URIs and Relative URI Rules").
        # We use this as indicator of whether Export-ODataEndpointProxy was attempted against a Redfish endpoint.
        if($connectionUri.AbsolutePath.StartsWith('/redfish/',[StringComparison]::OrdinalIgnoreCase))
        {
            $errorMessage = $LocalizedData.RedfishNotEnabled
            $exception = [System.InvalidOperationException]::new($errorMessage)
            $errorRecord = CreateErrorRecordHelper "ODataEndpointProxyRedfishNotEnabled" $errorMessage ([System.Management.Automation.ErrorCategory]::NotEnabled) $exception $Uri
            $PSCmdlet.ThrowTerminatingError($errorRecord)
        }

        if($connectionUri.Scheme -eq "http" -and !$AllowUnsecureConnection.IsPresent)
        {
            $errorMessage = ($LocalizedData.AllowUnsecureConnectionMessage -f $PSCmdlet.MyInvocation.MyCommand.Name, $Uri, "Uri")
            $exception = [System.InvalidOperationException]::new($errorMessage, $_.Exception)
            $errorRecord = CreateErrorRecordHelper "ODataEndpointProxyUnSecureConnection" $null ([System.Management.Automation.ErrorCategory]::InvalidArgument) $exception $Uri
            $PSCmdlet.ThrowTerminatingError($errorRecord)
        }

        $OutputModuleExists = Test-Path -Path $OutputModule -PathType Container

        if($OutputModuleExists -and ($Force -eq $false))
        {
            $errorMessage = ($LocalizedData.ModuleAlreadyExistsAndForceParameterIsNotSpecified -f $OutputModule)
            $errorRecord = CreateErrorRecordHelper "ODataEndpointProxyOutputModuleExists" $errorMessage ([System.Management.Automation.ErrorCategory]::ResourceExists) $null $OutputModule
            $PSCmdlet.ThrowTerminatingError($errorRecord)
        }
        
        $isWhatIf = $psboundparameters.ContainsKey("WhatIf")

        if(!$OutputModuleExists)
        {
            if(!$isWhatIf)
            {
                $OutputModule = (New-Item -Path $OutputModule -ItemType Directory).FullName
            }
        }
        else
        {
            $resolvedOutputModulePath = Resolve-Path -Path $OutputModule -ErrorAction Stop -Verbose
            if($resolvedOutputModulePath.Count -gt 1)
            {    
                $errorMessage = ($LocalizedData.OutputModulePathIsNotUnique -f $OutputModule)
                $errorRecord = CreateErrorRecordHelper "ODataEndpointProxyOutputModulePathIsNotUnique" $errorMessage ([System.Management.Automation.ErrorCategory]::InvalidArgument) $null $OutputModule
                $PSCmdlet.ThrowTerminatingError($errorRecord)
            }
            
            # Make sure that the path specified is a valid file system directory path.
            if([system.IO.Directory]::Exists($resolvedOutputModulePath))
            {
                $OutputModule = $resolvedOutputModulePath
            }
            else
            {
                $errorMessage = ($LocalizedData.OutputModulePathIsNotFileSystemPath -f $OutputModule)
                $errorRecord = CreateErrorRecordHelper "ODataEndpointProxyPathIsNotFileSystemPath" $errorMessage ([System.Management.Automation.ErrorCategory]::InvalidArgument) $null $OutputModule
                $PSCmdlet.ThrowTerminatingError($errorRecord)
            } 
        }

        $rootDir = [System.IO.Directory]::GetDirectoryRoot($OutputModule)

        if($rootDir -eq $OutputModule)
        {
            $errorMessage = ($LocalizedData.InvalidOutputModulePath -f $OutputModule)
            $errorRecord = CreateErrorRecordHelper "ODataEndpointProxyInvalidOutputModulePath" $errorMessage ([System.Management.Automation.ErrorCategory]::InvalidArgument) $null $OutputModule
            $PSCmdlet.ThrowTerminatingError($errorRecord)
        }

        if(!$isWhatIf)
        {
            $progressBarStatus = ($LocalizedData.ProgressBarMessage -f $Uri)
            ProgressBarHelper "Export-ODataEndpointProxy" $progressBarStatus 0 100 100 1
        }

        # Add parameters to $PSBoundParameters, which were not passed by user, but the default value is set
        $parametersWithDefaultValue = @("CreateRequestMethod", "UpdateRequestMethod", "CmdletAdapter")

        foreach ($parameterWithDefaultValue in $parametersWithDefaultValue)
        {
            if (!$PSBoundParameters.ContainsKey($parameterWithDefaultValue))
            {
                $PSBoundParameters.Add($parameterWithDefaultValue, (Get-Variable $parameterWithDefaultValue).Value)
            }
        }
    }

    END 
    {
        if($pscmdlet.ShouldProcess($Uri))
        {
            try
            {
                $PSBoundParameters.Add("ProgressBarStatus", $progressBarStatus)
                $PSBoundParameters.Add("PSCmdlet", $PSCmdlet)

                # Import module based on selected CmdletAdapter
                $adapterToImport = $CmdletAdapter
                
                # NetworkControllerAdapter relies on ODataAdapter
                if ($CmdletAdapter -eq 'NetworkControllerAdapter')
                {
                    $adapterToImport = 'ODataAdapter'
                }
 
                Write-Debug ($LocalizedData.SelectedAdapter -f $adapterPSScript)

                $adapterPSScript = "$PSScriptRoot\Microsoft.PowerShell." + $adapterToImport + ".ps1"

                Assert-ODataUtilsPathIsSecured -path $adapterPSScript
                . $adapterPSScript
                ExportODataEndpointProxy @PSBoundParameters
            }
            catch
            {
                $errorMessage = ($LocalizedData.InValidMetadata -f $Uri)
                $exception = [System.InvalidOperationException]::new($errorMessage, $_.Exception)
                $errorRecord = CreateErrorRecordHelper "ODataEndpointProxyInvalidInput" $null ([System.Management.Automation.ErrorCategory]::InvalidData) $exception $Uri
                $PSCmdlet.ThrowTerminatingError($errorRecord)
            }
            finally
            {
                Write-Progress -Activity "Export-ODataEndpointProxy" -Completed
            }
        }
    }
}

Export-ModuleMember -Function @('Export-ODataEndpointProxy')
