<#
.SYNOPSIS
    Builds the MSIX package for Oh My Posh.

.DESCRIPTION
    This script creates the MSIX installer package for Oh My Posh with the specified architecture and version.
    It can optionally copy the executable and sign the package.

.PARAMETER Architecture
    The target architecture for the package. Must be either 'x64' or 'arm64'.

.PARAMETER Version
    The version number to assign to the package (e.g., "1.2.3").

.PARAMETER SDKVersion
    The Windows SDK version to use for signing and packaging tools. Defaults to "10.0.26100.0".

.PARAMETER Sign
    When specified, signs the MSIX package using Azure Code Signing.

.PARAMETER Copy
    When specified, copies the appropriate executable from the dist folder before packaging.

.EXAMPLE
    .\build.ps1 -Architecture x64 -Version "1.2.3" -Copy

    Creates the MSIX package for x64 architecture with version 1.2.3, copying the executable first.

.EXAMPLE
    .\build.ps1 -Architecture arm64 -Version "1.2.3" -Sign -Copy

    Creates and signs the MSIX package for arm64 architecture with version 1.2.3.

.OUTPUTS
    Creates install-{Architecture}.msix in the 'out' directory.

.NOTES
    Requires the Windows SDK for MSIX packaging and signing.
#>

[CmdletBinding()]
param(
    [Parameter(Mandatory = $true)]
    [ValidateSet('x64', 'arm64')]
    [string]$Architecture,

    [Parameter(Mandatory = $true)]
    [ValidateNotNullOrEmpty()]
    [string]$Version,

    [Parameter()]
    [ValidateNotNullOrEmpty()]
    [string]$SDKVersion = "10.0.26100.0",

    [Parameter()]
    [switch]$Sign,

    [Parameter()]
    [switch]$Copy
)

# Set error handling preferences
$ErrorActionPreference = 'Stop'
$PSNativeCommandUseErrorActionPreference = $true
$PSDefaultParameterValues['Out-File:Encoding'] = 'UTF8'

#region Helper Functions

function Initialize-SigningEnvironment {
    <#
    .SYNOPSIS
        Sets up the signing environment and returns signing tool paths.

    .PARAMETER SDKVersion
        The Windows SDK version to use.

    .OUTPUTS
        Hashtable containing signtool and signtoolDlib paths.
    #>
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [string]$SDKVersion
    )

    try {
        Write-Verbose "Setting up signing environment" -Verbose

        # Install Microsoft.Trusted.Signing.Client
        nuget.exe install Microsoft.Trusted.Signing.Client -Version 1.0.92 -x | Out-Null

        $signtoolDlib = "$PWD/Microsoft.Trusted.Signing.Client/bin/x64/Azure.CodeSigning.Dlib.dll" -replace '\\', '/'
        $signtool = "C:/Program Files (x86)/Windows Kits/10/bin/$SDKVersion/x64/signtool.exe" -replace '\\', '/'

        # Validate tools exist
        if (-not (Test-Path $signtool)) {
            throw "signtool.exe not found at: $signtool"
        }
        if (-not (Test-Path $signtoolDlib)) {
            throw "Azure.CodeSigning.Dlib.dll not found at: $signtoolDlib"
        }

        # Explicitly create and return a hashtable
        [hashtable]$result = @{
            SignTool = $signtool
            SignToolDlib = $signtoolDlib
        }

        return $result
    }
    catch {
        Write-Error "Failed to initialize signing environment: $_"
        throw
    }
}

function Invoke-PackageSigning {
    <#
    .SYNOPSIS
        Signs a package using Azure Code Signing.

    .PARAMETER PackagePath
        The path to the package to sign.

    .PARAMETER SigningTools
        Hashtable containing signing tool paths from Initialize-SigningEnvironment.
    #>
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [ValidateScript({Test-Path $_})]
        [string]$PackagePath,

        [Parameter(Mandatory = $true)]
        [hashtable]$SigningTools
    )

    try {
        $packageName = Split-Path $PackagePath -Leaf
        Write-Verbose "Signing package: $packageName" -Verbose

        & $SigningTools.SignTool sign /v /debug /d "Oh My Posh" /fd SHA256 /tr 'http://timestamp.acs.microsoft.com' /td SHA256 /dlib $SigningTools.SignToolDlib /dmdf ../../src/metadata.json $PackagePath

        Write-Verbose "Successfully signed: $packageName" -Verbose
    }
    catch {
        Write-Error "Failed to sign package ${PackagePath}: ${_}"
        throw
    }
}

#endregion

#region Main Script

Write-Verbose "Building MSIX for $Architecture with version $Version" -Verbose
Write-Verbose "Setting up output directories" -Verbose

try {
    New-Item -Path "." -Name "dist" -ItemType Directory -ErrorAction SilentlyContinue | Out-Null
    New-Item -Path "." -Name "out" -ItemType Directory -ErrorAction SilentlyContinue | Out-Null
}
catch {
    Write-Error "Failed to create output directories: ${_}"
    throw
}

if ($Copy) {
    $sourceFile = switch ($Architecture) {
        'x64' { "posh-windows-amd64.exe" }
        Default { "posh-windows-$Architecture.exe" }
    }

    Write-Verbose "Copying $sourceFile to ./dist/oh-my-posh.exe" -Verbose

    try {
        $sourcePath = "../../dist/$sourceFile"
        if (-not (Test-Path $sourcePath)) {
            throw "Source file not found: $sourcePath"
        }
        Copy-Item -Path $sourcePath -Destination "./dist/oh-my-posh.exe" -Force
    }
    catch {
        Write-Error "Failed to copy executable: $_"
        throw
    }
}

Write-Verbose "Creating MSIX package" -Verbose

try {
    # Define MSIX package paths and files
    $currentPath = $PWD -replace '\\', '/'
    $manifestPath = "$currentPath/appxmanifest.xml"
    $mappingFilePath = "$currentPath/mapping.txt"
    $msixPackagePath = "$currentPath/out/install-$Architecture.msix"
    $makeappxPath = "C:/Program Files (x86)/Windows Kits/10/bin/$SDKVersion/x64/makeappx.exe"

    # Validate required files exist
    if (-not (Test-Path $manifestPath)) {
        throw "Manifest file not found: $manifestPath"
    }
    if (-not (Test-Path $mappingFilePath)) {
        throw "Mapping file not found: $mappingFilePath"
    }
    if (-not (Test-Path $makeappxPath)) {
        throw "makeappx.exe not found at: $makeappxPath"
    }

    # Update manifest with version and architecture
    [xml]$manifestDocument = Get-Content $manifestPath
    $manifestDocument.Package.Identity.Version = "$Version.0"
    $manifestDocument.Package.Identity.ProcessorArchitecture = $Architecture
    $manifestDocument.Save($manifestPath)

    # Build MSIX package
    Write-Verbose "Building MSIX: $msixPackagePath" -Verbose
    & "$makeappxPath" pack /p $msixPackagePath /v /o /m $manifestPath /f $mappingFilePath

    if (-not (Test-Path $msixPackagePath)) {
        throw "MSIX package was not created successfully"
    }
}
catch {
    Write-Error "Failed to create MSIX package: ${_}"
    throw
}

if ($Sign) {
    $signingTools = Initialize-SigningEnvironment -SDKVersion $SDKVersion
    Invoke-PackageSigning -PackagePath $msixPackagePath -SigningTools $signingTools
}

Write-Verbose "Successfully completed building the MSIX package" -Verbose

#endregion
