Src/Public/New-AsBuiltReport.ps1

function New-AsBuiltReport {
    <#
    .SYNOPSIS
        Documents the configuration of IT infrastructure in Word/HTML/Text formats using PScribo.
    .DESCRIPTION
        Documents the configuration of IT infrastructure in Word/HTML/Text formats using PScribo.
    .PARAMETER Report
        Specifies the type of report that will be generated.
    .PARAMETER Target
        Specifies the IP/FQDN of the system to connect.
        Multiple targets may be specified, separated by a comma.
    .PARAMETER Credential
        Specifies the stored credential of the target system.
    .PARAMETER Username
        Specifies the username for the target system.
    .PARAMETER Password
        Specifies the password for the target system.
    .PARAMETER Token
        Specifies an API token to authenticate to the target system.
    .PARAMETER UseInteractiveAuth
        Use interactive authentication (via 3rd party identity provider) to authenticate to the target system.
        This parameter has an alias 'MFA' for backwards compatibility.
    .PARAMETER Format
        Specifies the output format of the report.
        The supported output formats are WORD, HTML & TEXT.
        Multiple output formats may be specified, separated by a comma.
    .PARAMETER ReportLanguage
        Specifies the language for report content generated by the report module.
        Available languages are dynamically determined based on language folders in the selected report module.
        By default, the language will be set to en-US.
    .PARAMETER Orientation
        Sets the page orientation of the report to Portrait or Landscape.
        By default, page orientation will be set to Portrait.
    .PARAMETER StyleFilePath
        Specifies the file path to a custom style .ps1 script for the report to use.
    .PARAMETER OutputFolderPath
        Specifies the folder path to save the report.
    .PARAMETER Filename
        Specifies a filename for the report.
    .PARAMETER Timestamp
        Specifies whether to append a timestamp string to the report filename.
        By default, the timestamp string is not added to the report filename.
    .PARAMETER EnableHealthCheck
        Performs a health check of the target environment and highlights known issues within the report.
        Not all reports may provide this functionality.
    .PARAMETER SendEmail
        Sends report to specified recipients as email attachments.
    .PARAMETER AsBuiltConfigFilePath
        Enter the full file path to the As Built Report configuration JSON file.
        If this parameter is not specified, the user will be prompted for this configuration information on first
        run, with the option to save the configuration to a file.
    .PARAMETER ReportConfigFilePath
        Enter the full file path to a report JSON configuration file
        If this parameter is not specified, a default report configuration JSON is copied to the specifed user folder.
        If this parameter is specified and the path to a JSON file is invalid, the script will terminate.
    .EXAMPLE
        New-AsBuiltReport -Report VMware.vSphere -Target 192.168.1.100 -Username admin -Password admin -Format HTML,Word -EnableHealthCheck -OutputFolderPath 'C:\Reports'
 
        Creates a VMware vSphere As Built Report in HTML & Word formats. The document will highlight particular issues which exist within the environment.
        The report will be saved to C:\Reports.
    .EXAMPLE
        $Creds = Get-Credential
        New-AsBuiltReport -Report PureStorage.FlashArray -Target 192.168.1.100 -Credential $Creds -Format Text -Timestamp -OutputFolderPath 'C:\Reports'
 
        Creates a Pure Storage FlashArray As Built Report in Text format and appends a timestamp to the filename.
        Stored credentials are used to connect to the system.
        The report will be saved to C:\Reports.
    .EXAMPLE
        New-AsBuiltReport -Report Veeam.VBR -Target veeam01.corp.local -Username admin -Password admin -Format HTML -Filename 'Veeam-Backup-Report' -OutputFolderPath 'C:\Reports'
 
        Creates a Veeam Backup & Replication As Built Report in HTML format with a custom filename.
        The report will be saved as 'Veeam-Backup-Report.html' in C:\Reports.
    .EXAMPLE
        New-AsBuiltReport -Report Rubrik.CDM -Target 192.168.1.100 -Token 'eyJ0eXAiOiJKV1QiLCJhbGc...' -Format HTML -OutputFolderPath 'C:\Reports'
 
        Creates a Rubrik CDM As Built Report in HTML format.
        An API token is used to connect to the system.
        The report will be saved to C:\Reports.
    .EXAMPLE
        New-AsBuiltReport -Report Microsoft.Azure -Target 'tenant.onmicrosoft.com' -UseInteractiveAuth -Format Word -OutputFolderPath 'C:\Reports'
 
        Creates a Microsoft Azure As Built Report in Word format.
        Interactive authentication (via web browser) is used to authenticate to Azure.
        The report will be saved to C:\Reports.
    .EXAMPLE
        New-AsBuiltReport -Report NetApp.ONTAP -Target ontap.corp.local -Username admin -Password admin -Format HTML -Orientation Landscape -OutputFolderPath 'C:\Reports'
 
        Creates a NetApp ONTAP As Built Report in HTML format with Landscape page orientation.
        The report will be saved to C:\Reports.
    .EXAMPLE
        New-AsBuiltReport -Report VMware.vSphere -Target vcenter.corp.local -Username admin -Password admin -Format Word -StyleFilePath 'C:\Styles\Corporate-Style.ps1' -OutputFolderPath 'C:\Reports'
 
        Creates a VMware vSphere As Built Report in Word format using a custom corporate style.
        The report will be saved to C:\Reports.
    .EXAMPLE
        New-AsBuiltReport -Report Nutanix.PrismElement -Target 192.168.1.100 -Username admin -Password admin -Format Word -SendEmail -OutputFolderPath 'C:\Reports'
 
        Creates a Nutanix Prism Element As Built Report in Word format. Report will be attached and sent via email.
        Email settings must be configured using New-AsBuiltConfig or -AsBuiltConfigFilePath parameter.
        The report will be saved to C:\Reports.
    .EXAMPLE
        New-AsBuiltReport -Report VMware.vSphere -Target vcenter01.corp.local,vcenter02.corp.local -Username admin -Password admin -Format HTML -OutputFolderPath 'C:\Reports'
 
        Creates VMware vSphere As Built Reports in HTML format for multiple vCenter servers.
        Separate reports will be generated for each target.
        The reports will be saved to C:\Reports.
    .EXAMPLE
        New-AsBuiltReport -Report Microsoft.AD -Target dc01.corp.local -Username 'CORP\admin' -Password admin -Format HTML -ReportLanguage 'es-ES' -OutputFolderPath 'C:\Reports'
 
        Creates a Microsoft Active Directory As Built Report in HTML format with Spanish language content.
        The report will be saved to C:\Reports.
    .EXAMPLE
        New-AsBuiltReport -Report DellEMC.VxRail -Target vxrail-mgr.corp.local -Username admin -Password admin -Format Word -AsBuiltConfigFilePath 'C:\Config\asbuiltreport.json' -OutputFolderPath 'C:\Reports'
 
        Creates a Dell EMC VxRail As Built Report in Word format, using the configuration in the asbuiltreport.json file.
        The report will be saved to C:\Reports.
    .EXAMPLE
        New-AsBuiltReport -Report VMware.Horizon -Target horizon-cs.corp.local -Username admin -Password admin -Format HTML -ReportConfigFilePath 'C:\Config\VMware.Horizon.json' -OutputFolderPath 'C:\Reports'
 
        Creates a VMware Horizon As Built Report in HTML format, using a custom report configuration file.
        The custom configuration allows you to control report depth and detail levels.
        The report will be saved to C:\Reports.
    .EXAMPLE
        New-AsBuiltReport -Report Aruba.ClearPass -Target aruba.corp.local -Username admin -Password admin -Format HTML,Word,Text -Timestamp -EnableHealthCheck -OutputFolderPath 'C:\Reports'
 
        Creates an Aruba ClearPass As Built Report in HTML, Word, and Text formats with timestamp appended to the filename.
        Health checks are enabled to highlight any configuration issues.
        The reports will be saved to C:\Reports.
    .LINK
        https://github.com/AsBuiltReport/AsBuiltReport.Core
    .LINK
        https://www.asbuiltreport.com/user-guide/new-asbuiltreport/
    #>


    #region Script Parameters
    [CmdletBinding(
        PositionalBinding = $false,
        DefaultParameterSetName = 'Credential'
    )]
    param (
        [Parameter(
            Position = 0,
            Mandatory = $true,
            HelpMessage = 'Please specify which report type you wish to run.'
        )]
        [ValidateScript( {
                $InstalledReportModules = Get-Module -Name "AsBuiltReport.*" -ListAvailable | Where-Object { $_.name -ne 'AsBuiltReport.Core' } | Sort-Object -Property Version -Descending | Select-Object -Unique
                $ValidReports = foreach ($InstalledReportModule in $InstalledReportModules) {
                    $NameArray = $InstalledReportModule.Name.Split('.')
                    "$($NameArray[-2]).$($NameArray[-1])"
                }
                if ($ValidReports -contains $_) {
                    $true
                } else {
                    throw "Invalid report type specified. Please use one of the following [$($ValidReports -Join ', ')]"
                }
            })]
        [String] $Report,

        [Parameter(
            Position = 1,
            Mandatory = $true,
            HelpMessage = 'Please provide the IP/FQDN of the system'
        )]
        [ValidateNotNullOrEmpty()]
        [Alias('Cluster', 'Server', 'IP')]
        [String[]] $Target,
        [Parameter(
            Position = 2,
            Mandatory = $true,
            HelpMessage = 'Please provide credentials to connect to the system',
            ParameterSetName = 'Credential'
        )]
        [ValidateNotNullOrEmpty()]
        [PSCredential] $Credential,

        [Parameter(
            Position = 2,
            Mandatory = $true,
            HelpMessage = 'Please provide the username to connect to the target system',
            ParameterSetName = 'UsernameAndPassword'
        )]
        [ValidateNotNullOrEmpty()]
        [String] $Username,

        [Parameter(
            Position = 3,
            Mandatory = $false,
            HelpMessage = 'Please provide the password to connect to the target system',
            ParameterSetName = 'UsernameAndPassword'
        )]
        [ValidateNotNullOrEmpty()]
        [String] $Password,

        [Parameter(
            Position = 3,
            Mandatory = $true,
            HelpMessage = 'Please provide an API token to connect to the target system',
            ParameterSetName = 'APIToken'
        )]
        [ValidateNotNullOrEmpty()]
        [String] $Token,

        [Parameter(
            Position = 3,
            Mandatory = $true,
            ParameterSetName = 'InteractiveAuth'
        )]
        [ValidateNotNullOrEmpty()]
        [Alias('MFA')]
        [Switch] $UseInteractiveAuth,

        [Parameter(
            Position = 4,
            Mandatory = $false,
            HelpMessage = 'Please provide the document output format'
        )]
        [ValidateNotNullOrEmpty()]
        [ValidateSet('Word', 'HTML', 'Text')]
        [Array] $Format = 'Word',

        [Parameter(
            Mandatory = $false,
            HelpMessage = 'Please specify the language for the report content. Available languages are determined by language folders in the selected report module (default is en-US)'
        )]
        [ValidateNotNullOrEmpty()]
        [ValidateScript({
            if ($Report) {
                $ReportModuleName = "AsBuiltReport.$Report"
                $ReportModule = Get-Module -Name $ReportModuleName -ListAvailable | Sort-Object -Property Version -Descending | Select-Object -First 1
                if ($ReportModule) {
                    $LanguagePath = Join-Path -Path $ReportModule.ModuleBase -ChildPath 'Language'
                    $AvailableLanguages = @()
                    if (Test-Path $LanguagePath) {
                        $AvailableLanguages = Get-ChildItem -Path $LanguagePath -Directory | Select-Object -ExpandProperty Name
                    }
                    if ($AvailableLanguages.Count -eq 0) {
                        $AvailableLanguages = @('en-US')
                    }
                    if ($_ -in $AvailableLanguages) {
                        $true
                    } else {
                        throw "Report Language '$_' is not supported for module '$ReportModuleName'. Available report languages are: $($AvailableLanguages -join ', ')"
                    }
                } else {
                    # If report module not found, default to en-US validation
                    if ($_ -eq 'en-US') {
                        $true
                    } else {
                        throw "Report module '$ReportModuleName' not found. Defaulting to 'en-US' language."
                    }
                }
            } else {
                # If no Report specified yet, allow any language for now
                $true
            }
        })]
        [String] $ReportLanguage,

        [Parameter(
            Mandatory = $false,
            HelpMessage = 'Determines the document page orientation'
        )]
        [ValidateNotNullOrEmpty()]
        [ValidateSet('Portrait', 'Landscape')]
        [String] $Orientation = 'Portrait',

        [Parameter(
            Mandatory = $true,
            HelpMessage = 'Please provide the path to the document output file'
        )]
        [ValidateNotNullOrEmpty()]
        [Alias('OutputPath')]
        [String] $OutputFolderPath,

        [Parameter(
            Mandatory = $false,
            HelpMessage = 'Please provide the path to the custom style script'
        )]
        [ValidateNotNullOrEmpty()]
        [Alias('StylePath')]
        [String] $StyleFilePath,

        [Parameter(
            Mandatory = $false,
            HelpMessage = 'Provide the file path to an existing report JSON Configuration file'
        )]
        [ValidateNotNullOrEmpty()]
        [Alias('ReportConfigPath')]
        [String] $ReportConfigFilePath,

        [Parameter(
            Mandatory = $false,
            HelpMessage = 'Provide the file path to an existing As Built JSON Configuration file'
        )]
        [ValidateNotNullOrEmpty()]
        [Alias('AsBuiltConfigPath')]
        [String] $AsBuiltConfigFilePath,

        [Parameter(
            Mandatory = $false,
            HelpMessage = 'Specify the As Built Report filename'
        )]
        [ValidateNotNullOrEmpty()]
        [String] $Filename,

        [Parameter(
            Mandatory = $false,
            HelpMessage = 'Specify whether to append a timestamp to the document filename'
        )]
        [Switch] $Timestamp = $false,

        [Parameter(
            Mandatory = $false,
            HelpMessage = 'Specify whether to highlight any configuration issues within the document'
        )]
        [Switch] $EnableHealthCheck = $false,

        [Parameter(
            Mandatory = $false,
            HelpMessage = 'Specify whether to send report via Email'
        )]
        [Switch] $SendEmail = $false
    )
    #endregion Script Parameters

    # Initialize core module localization with user's OS language
    Initialize-LocalizedData -ModuleBasePath (Split-Path (Split-Path $PSScriptRoot -Parent) -Parent) -LanguageFile 'New-AsBuiltReport' -ModuleType 'Core'

    try {

        if ($psISE) {
            Write-Error ($translate.PwshISE) -ErrorAction Stop
        }

        # If Username and Password parameters used, convert specified Password to secure string and store in $Credential
        if ($Username) {
            if (-not $Password) {
                # If the Password parameter is not provided, prompt for it securely
                $SecurePassword = Read-Host ($translate.password -f $Username) -AsSecureString
            } else {
                # If the Password parameter is provided, convert it to secure string
                $SecurePassword = ConvertTo-SecureString $Password -AsPlainText -Force
            }
            $Credential = New-Object System.Management.Automation.PSCredential ($Username, $SecurePassword)
        }

        if (-not (Test-Path $OutputFolderPath)) {
            Write-Error ($translate.OutputFolderPath -f $OutputFolderPath) -ErrorAction Stop
        }
        #region Variable config

        # Import the AsBuiltReport JSON configuration file
        # If no path was specified, or the specified file doesn't exist, call New-AsBuiltConfig to walk the user through the menu prompt to create a config JSON
        if ($AsBuiltConfigFilePath) {
            if (Test-Path -Path $AsBuiltConfigFilePath) {
                $Global:AsBuiltConfig = Get-Content -Path $AsBuiltConfigFilePath | ConvertFrom-Json
                # Verbose Output for As Built Report configuration
                Write-PScriboMessage -Plugin "Module" -Message ($translate.LoadConfig -f $AsBuiltConfigFilePath)
            } else {
                Write-Error ($translate.NoConfigFound -f $AsBuiltConfigFilePath) -ErrorAction Stop
            }
        } else {
            Write-PScriboMessage -Plugin "Document" -Message $translate.GeneratingReport
            $Global:AsBuiltConfig = New-AsBuiltConfig
            # Restore core translations after New-AsBuiltConfig (which loads its own translations)
            Initialize-LocalizedData -ModuleBasePath (Split-Path (Split-Path $PSScriptRoot -Parent) -Parent) -LanguageFile 'New-AsBuiltReport' -ModuleType 'Core'
        }

        # Set ReportConfigFilePath as Global scope for use in New-AsBuiltConfig
        if ($ReportConfigFilePath) {
            $Global:ReportConfigFilePath = $ReportConfigFilePath
        }

        # Set $OutputFolderPath as Global scope
        if ($OutputFolderPath) {
            $Global:OutputFolderPath = $OutputFolderPath
        }

        # If StyleFilePath was specified, ensure the file provided in the path exists, otherwise exit with error
        if ($StyleFilePath) {
            if (-not (Test-Path -Path $StyleFilePath)) {
                Write-Error ($translate.StyleScriptNotFound -f $StyleFilePath) -ErrorAction Stop
            }
        }

        # Report Module Information
        $Global:Report = $Report
        $ReportModuleName = "AsBuiltReport.$Report"
        $CoreModulePath = (Get-Module -Name 'AsBuiltReport.Core' -ListAvailable | Sort-Object -Property Version -Descending | Select-Object -First 1).ModuleBase
        $ReportModulePath = (Get-Module -Name $ReportModuleName -ListAvailable | Sort-Object -Property Version -Descending | Select-Object -First 1).ModuleBase

        # Initialize report module localization
        # First, temporarily store the core translation
        $CoreTranslations = $global:translate

        if ($ReportConfigFilePath) {
            # If ReportConfigFilePath was specified, ensure the file provided in the path exists, otherwise exit with error
            if (-not (Test-Path -Path $ReportConfigFilePath)) {
                Write-Error ($translate.ReportModuleNotFound -f $ReportModuleName, $ReportConfigFilePath) -ErrorAction Stop
            } else {
                # Import the Report Configuration in to a variable
                Write-PScriboMessage -Plugin "Document" -Message ($translate.LoadingReportConfig -f $ReportModuleName, $ReportConfigFilePath)
                $Global:ReportConfig = Get-Content -Path $ReportConfigFilePath | ConvertFrom-Json
            }
        } else {
            # If a report config hasn't been provided, check for the existance of the default JSON in the paths the user specified in base config
            $ReportConfigFilePath =  Join-Path -Path $ReportModulePath -ChildPath "$($ReportModuleName).json"
            if (Test-Path -Path $ReportConfigFilePath) {
                Write-PScriboMessage -Plugin "Document" -Message ($translate.LoadingReportConfig -f $ReportModuleName, $ReportConfigFilePath)
                $Global:ReportConfig = Get-Content -Path $ReportConfigFilePath | ConvertFrom-Json
            } else {
                Write-Error ($translate.ReportConfigNotFound -f $ReportModulePath) -ErrorAction Stop
            }
        }

        # Determine final report language (ReportLanguage parameter overrides JSON config)
        $FinalReportLanguage = if ($PSBoundParameters.ContainsKey('ReportLanguage')) {
            # Parameter was explicitly specified by user
            $ReportLanguage
        } elseif ($ReportConfig.Report.Language) {
            # Use language from JSON config if available
            $ReportConfig.Report.Language
        } else {
            # Fallback to default
            'en-US'
        }

        # Initialize report module translations with the final determined language
        if ($ReportModulePath) {
            try {
                # Try to find a report-specific language file (most report modules use the report name)
                $ReportLanguageFile = ($Report -replace '\.', '')
                Initialize-LocalizedData -ModuleBasePath $ReportModulePath -LanguageFile $ReportLanguageFile -ModuleType 'Report' -ModuleName $Report -Language $FinalReportLanguage
                $ReportTranslations = $global:translate
            } catch {
                # If report-specific language file not found, use default empty translations for reports
                Write-Warning "Report module localization not found for $ReportModuleName. Using default language '$FinalReportLanguage'."
                $ReportTranslations = @{}
            }
        } else {
            $ReportTranslations = @{}
        }

        # Restore core translations and set report translations separately
        $global:translate = $CoreTranslations
        $global:reportTranslate = $ReportTranslations

        # If Filename parameter is not specified, set filename to the report name
        if (-not $Filename) {
            $FileName = $ReportConfig.Report.Name
        }
        # If Timestamp parameter is specified, add the timestamp to the report filename
        if ($Timestamp) {
            $FileName = $Filename + " - " + (Get-Date -Format 'yyyy-MM-dd_HH.mm.ss')
        }
        Write-PScriboMessage -Plugin "Document" -Message ($translate.SetReportFileName -f $FileName)

        # If the EnableHealthCheck parameter has been specified, set the global healthcheck variable so report scripts can reference the health checks
        if ($EnableHealthCheck) {
            $Global:Healthcheck = $ReportConfig.HealthCheck
        }

        # Set Global scope for Orientation parameter
        $Global:Orientation = $Orientation

        #endregion Variable config

        #region Email Server Authentication
        # If Email Server Authentication is required, prompt user for credentials
        if ($SendEmail -and $AsBuiltConfig.Email.Credentials) {
            Clear-Host
            Draw-AsciiBox -Lines @($translate.EmailBannerTitle) -ExtraPadding 4 -TextColor 'Cyan' -BorderColor 'Cyan'
            $MailCredentials = Get-Credential -Message ($translate.EmailCredentials -f $AsBuiltConfig.Email.Server)
            Clear-Host
        }
        #endregion Email Server Authentication

        # Check installed module version
        Try {
            $InstalledVersion = Get-Module -ListAvailable -Name AsBuiltReport.Core -ErrorAction SilentlyContinue | Sort-Object -Property Version -Descending | Select-Object -First 1 -ExpandProperty Version

            if ($InstalledVersion) {
                Write-PScriboMessage -Plugin "Module" -Message ($translate.InstalledModule -f $($InstalledVersion.ToString()))
                $LatestVersion = Find-Module -Name AsBuiltReport.Core -Repository PSGallery -ErrorAction SilentlyContinue | Select-Object -ExpandProperty Version
                if ($LatestVersion -gt $InstalledVersion) {
                    Write-PScriboMessage -Plugin "Module" -Message ($translate.AvailableModule -f $($LatestVersion.ToString()))
                    Write-PScriboMessage -Plugin "Module" -Message $translate.UpdateModule
                }
            }
        } Catch {
            Write-PscriboMessage -Plugin "Module" -IsWarning $_.Exception.Message
        }

        #region Generate PScribo document
        # if Verbose has been passed
        if ($PSCmdlet.MyInvocation.BoundParameters["Verbose"].IsPresent) {
            $AsBuiltReport = Document $FileName -Verbose {
                Write-PScriboMessage -Plugin "Document" -Message ($translate.ReportGenerating -f $($Report.Replace(".", " ")))
                # Set Document Style
                if ($StyleFilePath) {
                    Write-PScriboMessage -Plugin "Document" -Message ($translate.ReportStyleScript -f $StyleFilePath)
                    . $StyleFilePath
                } else {
                    $StyleFilePath = Join-Path -Path $CoreModulePath -ChildPath 'AsBuiltReport.Core.Style.ps1'
                    Write-PScriboMessage -Plugin "Document" -Message ($translate.ReportStyleScript -f $StyleFilePath)
                    . $StyleFilePath
                }

                # Restore core translations after style script (style script loads its own translations)
                $global:translate = $CoreTranslations

                # If Credential has been passed or previously created via Username/Password
                try {
                    if ($Credential) {
                        & "Invoke-$($ReportModuleName)" -Target $Target -Credential $Credential -Verbose -ErrorAction Stop
                    }
                    elseif ($Token) {
                        & "Invoke-$($ReportModuleName)" -Target $Target -Token $Token -Verbose -ErrorAction Stop
                    }
                    elseif ($UseInteractiveAuth) {
                        Write-PScriboMessage -Plugin "Module" -Message ($translate.InteractiveAuth)
                        & "Invoke-$($ReportModuleName)" -Target $Target -UseInteractiveAuth -Verbose -ErrorAction Stop
                    }
                } catch {
                    Write-Error ($($translate.ExecutionFailed) -f $($_.Exception.Message)) -ErrorAction Stop
                }
            }
        } else {
            # Show progress messages for non-verbose report generation
            Write-Host ($translate.ReportGenerating -f $($Report.Replace(".", " "))) -ForegroundColor Green
            Write-Host ($translate.ReportInitializing) -ForegroundColor Cyan

            try {
                $AsBuiltReport = Document $FileName {
                    Write-Host ($translate.DocumentStyle) -ForegroundColor Cyan
                    # Set Document Style
                    if ($StyleFilePath) {
                        #Write-Host ($translate.ReportStyleScript -f $StyleFilePath)
                        . $StyleFilePath
                    } else {
                        $StyleFilePath = Join-Path -Path $CoreModulePath -ChildPath 'AsBuiltReport.Core.Style.ps1'
                        #Write-Host ($translate.ReportStyleScript -f $StyleFilePath)
                        . $StyleFilePath
                    }

                    # Restore core translations after style script (style script loads its own translations)
                    $global:translate = $CoreTranslations

                    Write-Host ($translate.TargetSystem) -ForegroundColor Cyan
                    try {
                        # If Credential has been passed or previously created via Username/Password
                        if ($Credential) {
                            & "Invoke-$($ReportModuleName)" -Target $Target -Credential $Credential -ErrorAction Stop
                        }
                        elseif ($Token) {
                            & "Invoke-$($ReportModuleName)" -Target $Target -Token $Token -ErrorAction Stop
                        }
                        elseif ($UseInteractiveAuth) {
                            & "Invoke-$($ReportModuleName)" -Target $Target -UseInteractiveAuth -ErrorAction Stop
                        }
                    } catch {
                        Write-Error ($($translate.ExecutionFailed) -f $($_.Exception.Message)) -ErrorAction Stop
                    }
                    Write-Host ($translate.BuildingDocument) -ForegroundColor Cyan
                }
                Write-Host ($translate.ExportingDocument) -ForegroundColor Cyan
            } catch {
                Write-Host ($translate.Failed) -ForegroundColor Red
                throw
            }
        }
        Try {
            $Document = $AsBuiltReport | Export-Document -Path $OutputFolderPath -Format $Format -Options @{ TextWidth = 240 } -PassThru
            # Ensure core translations are available
            if (-not $translate.OutputFolder) {
                $CoreModuleBasePath = Split-Path (Split-Path $PSScriptRoot -Parent) -Parent
                Initialize-LocalizedData -ModuleBasePath $CoreModuleBasePath -LanguageFile 'New-AsBuiltReport' -ModuleType 'Core'
            }
            Write-Host ($translate.OutputFolder -f $($Report.Replace(".", " ")), $FileName, $OutputFolderPath) -ForegroundColor Green
        } catch {
            $Err = $_
            Write-Error $Err
        }
        #endregion Generate PScribo document

        #region Send-Email
        if ($SendEmail) {
            $EmailArguments = @{
                Attachments = $Document
                To = $AsBuiltConfig.Email.To
                From = $AsBuiltConfig.Email.From
                Subject = $ReportConfig.Report.Name
                Body = $AsBuiltConfig.Email.Body
                SmtpServer = $AsBuiltConfig.Email.Server
                Port = $AsBuiltConfig.Email.Port
                UseSSL = $AsBuiltConfig.Email.UseSSL
            }

            if ($AsBuiltConfig.Email.Credentials) {
                # Send the report via SMTP using SSL
                Send-MailMessage @EmailArguments -Credential $MailCredentials
            } else {
                # Send the report via SMTP
                Send-MailMessage @EmailArguments
            }
        }
        #endregion Send-Email

        #region Globals cleanup
        Remove-Variable -Name AsBuiltConfig -Scope Global
        Remove-Variable -Name ReportConfig -Scope Global
        Remove-Variable -Name Report -Scope Global
        Remove-Variable -Name Orientation -Scope Global
        if ($ReportConfigFilePath) {
            Remove-Variable -Name ReportConfigFilePath
        }
        if ($Healthcheck) {
            Remove-Variable -Name Healthcheck -Scope Global
        }
        if ($global:AsBuiltReportLocalizedWarnings) {
            Remove-Variable -Name AsBuiltReportLocalizedWarnings -Scope Global
        }
        #endregion Globals cleanup

    } catch {
        Write-Error $_
    }
}

Register-ArgumentCompleter -CommandName 'New-AsBuiltReport' -ParameterName 'Report' -ScriptBlock {
    param (
        $commandName,
        $parameterName,
        $wordToComplete,
        $commandAst,
        $fakeBoundParameter
    )

    $InstalledReportModules = Get-Module -Name "AsBuiltReport.*" -ListAvailable | Where-Object { $_.name -ne 'AsBuiltReport.Core' } | Sort-Object -Property Version -Descending | Select-Object -Unique
    $ValidReports = foreach ($InstalledReportModule in $InstalledReportModules) {
        $NameArray = $InstalledReportModule.Name.Split('.')
        "$($NameArray[-2]).$($NameArray[-1])"
    }

    $ValidReports | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object {
        [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_)
    }
}


# Argument completer for ReportLanguage parameter - dynamically gets available languages from selected Report module
Register-ArgumentCompleter -CommandName 'New-AsBuiltReport' -ParameterName 'ReportLanguage' -ScriptBlock {
    param (
        $commandName,
        $parameterName,
        $wordToComplete,
        $commandAst,
        $fakeBoundParameter
    )

    # Try to get the Report parameter value from the current command line
    $ReportValue = $null
    if ($fakeBoundParameter['Report']) {
        $ReportValue = $fakeBoundParameter['Report']
    } else {
        # Try to parse the Report value from the command AST
        if ($commandAst) {
            $reportParam = $commandAst.FindAll({
                $args[0] -is [System.Management.Automation.Language.CommandParameterAst] -and
                $args[0].ParameterName -eq 'Report'
            }, $true) | Select-Object -First 1

            if ($reportParam) {
                # Get the next element after -Report parameter
                $paramIndex = $commandAst.CommandElements.IndexOf($reportParam)
                if ($paramIndex -ge 0 -and ($paramIndex + 1) -lt $commandAst.CommandElements.Count) {
                    $nextElement = $commandAst.CommandElements[$paramIndex + 1]
                    if ($nextElement -is [System.Management.Automation.Language.StringConstantExpressionAst]) {
                        $ReportValue = $nextElement.Value
                    }
                }
            }
        }
    }

    # If we have a report module, get its available languages
    if ($ReportValue) {
        $ReportModuleName = "AsBuiltReport.$ReportValue"
        $ReportModule = Get-Module -Name $ReportModuleName -ListAvailable | Sort-Object -Property Version -Descending | Select-Object -First 1
        if ($ReportModule) {
            $LanguagePath = Join-Path -Path $ReportModule.ModuleBase -ChildPath 'Language'
            $AvailableLanguages = @()
            if (Test-Path $LanguagePath) {
                $AvailableLanguages = Get-ChildItem -Path $LanguagePath -Directory | Select-Object -ExpandProperty Name
            }

            if ($AvailableLanguages.Count -eq 0) {
                $AvailableLanguages = @('en-US')
            }

            $AvailableLanguages | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object {
                [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_)
            }
        } else {
            # If report module not found, default to common languages
            @('en-US') | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object {
                [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_)
            }
        }
    } else {
        # If no report specified, show common languages as fallback
        @('en-US', 'es-ES', 'fr-FR', 'de-DE') | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object {
            [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_)
        }
    }
}