
#!/usr/bin/env powershell
[CmdletBinding(PositionalBinding = $false)]
param (
    [ValidateSet('amd64', 'arm64')]
    [Alias('arch')]
    [string]$architecture = $(
        $defaultArchitecture = 'amd64'
        $arch = try { & go env GOARCH } catch {
            Write-Warning "Failed retrieving the host architecture, using default ($defaultArchitecture). Is Go installed?"
            return $defaultArchitecture
        }
        if ($arch -cnotin @('arm64', 'amd64')) {
            Write-Warning "Unsupported architecture $arch. Using default ($defaultArchitecture)."
            return $defaultArchitecture
        }
        return $arch
    ),
    [parameter(ValueFromRemainingArguments)][object[]]$params = @()
)

. $PSScriptRoot\hack\ci\win-lib.ps1

# Targets
function Podman-Remote {
    New-Item -ItemType Directory -Force -Path './bin/windows'

    $buildInfo = Get-Date -UFormat %s -Millisecond 0
    $buildInfo = "-X go.podman.io/podman/v6/libpod/define.buildInfo=$buildInfo "
    $commit = Git-Commit
    $commit = "-X go.podman.io/podman/v6/libpod/define.gitCommit=$commit "

    $ENV:GOARCH = $architecture
    Run-Command "go build --ldflags `"$commit $buildInfo `" --tags `"$remotetags`" --o ./bin/windows/podman.exe ./cmd/podman/."
}

function Make-Clean {
    $paths = @(
        # Files generated by the `podman` target
        "$PSScriptRoot\bin\windows"
        # Files generated by the `installer` target
        "$PSScriptRoot\test\version\version.exe"
        "$PSScriptRoot\contrib\win-installer\artifacts"
        "$PSScriptRoot\contrib\win-installer\current"
        "$PSScriptRoot\contrib\win-installer\docs"
        "$PSScriptRoot\contrib\win-installer\fetch"
        "$PSScriptRoot\contrib\win-installer\wix\obj"
        "$PSScriptRoot\contrib\win-installer\*.log"
        "$PSScriptRoot\contrib\win-installer\*.msi"
        "$PSScriptRoot\contrib\win-installer\*.wixpdb"
        "$PSScriptRoot\contrib\win-installer\shasums"
        # Files generated by the Documentation target
        "$PSScriptRoot\docs\build\remote\podman-*.html"
        "$PSScriptRoot\docs\build\remote\podman-for-windows.html"
    )

    foreach ($path in $paths) {
        if (Test-Path -Path $path -PathType Container) {
            Remove-Item $path -Recurse -Force -Confirm:$false
        }
        elseif (Test-Path -Path $path -PathType Leaf) {
            Remove-Item $path -Force -Confirm:$false
        }
    }
}

function Local-Unit {
    Build-Ginkgo
    $skippackages = 'hack,internal\domain\infra\abi,internal\domain\infra\tunnel,libpod\lock\shm,pkg\api\handlers\libpod,pkg\api\handlers\utils,pkg\bindings,'
    $skippackages += 'pkg\domain\infra\abi,pkg\emulation,pkg\machine\apple,pkg\machine\applehv,pkg\machine\e2e,pkg\machine\libkrun,'
    $skippackages += 'pkg\machine\proxyenv,pkg\machine\qemu,pkg\specgen\generate,pkg\systemd,test\e2e,test\utils,cmd\rootlessport,'
    $skippackages += 'pkg\pidhandle'
    if ($null -eq $ENV:GINKGOTIMEOUT) { $ENV:GINKGOTIMEOUT = '--timeout=15m' }
    Run-Command "./bin/ginkgo.exe -vv -r --tags `"$remotetags`" ${ENV:GINKGOTIMEOUT} --trace --no-color --skip-package `"$skippackages`""
}

function Local-Machine {
    param (
        [string]$files
    );
    Ginkgo-Run -files $files -testPath 'pkg/machine/e2e/.' -defaultTimeout '50m'
}

function Ginkgo-Run {
    param (
        [string]$files,
        [string]$testPath = 'test/e2e/.',
        [string]$defaultTimeout = '5m'
    );
    Build-Ginkgo
    if ($files) {
        $files = "--focus-file ""$files"""
    }
    elseif ($FOCUS_FILE) {
        $files = "--focus-file ""$FOCUS_FILE"" --silence-skips"
    }
    if ($FOCUS) {
        $focus = "--focus ""$FOCUS"" --silence-skips"
    }

    if ($null -eq $ENV:GINKGOTIMEOUT) { $ENV:GINKGOTIMEOUT = "--timeout=$defaultTimeout" }
    Run-Command "./bin/ginkgo.exe -vv --tags `"$remotetags`" ${ENV:GINKGOTIMEOUT} --trace --no-color $focus $files $testPath"
}

# Expect starting directory to be /podman
function Win-SSHProxy {
    param (
        [string]$Version
    );

    New-Item -ItemType Directory -Force -Path './bin/windows'
    if (-Not $Version) {
        $match = Select-String -Path "$PSScriptRoot\go.mod" -Pattern 'github.com/containers/gvisor-tap-vsock\s+(v[\d\.]+)'
        $Version = $match.Matches.Groups[1].Value
    }
    Write-Host "Downloading gvproxy version $version"
    if ($architecture -eq 'amd64') {
        curl.exe --fail -sSL -o './bin/windows/gvproxy.exe' --retry 5 "https://github.com/containers/gvisor-tap-vsock/releases/download/$Version/gvproxy-windowsgui.exe"
        curl.exe --fail -sSL -o './bin/windows/win-sshproxy.exe' --retry 5 "https://github.com/containers/gvisor-tap-vsock/releases/download/$Version/win-sshproxy.exe"
    }
    else {
        curl.exe --fail -sSL -o './bin/windows/gvproxy.exe' --retry 5 "https://github.com/containers/gvisor-tap-vsock/releases/download/$Version/gvproxy-windows-arm64.exe"
        curl.exe --fail -sSL -o './bin/windows/win-sshproxy.exe' --retry 5 "https://github.com/containers/gvisor-tap-vsock/releases/download/$Version/win-sshproxy-arm64.exe"
    }
}

function Installer {
    param (
        [string]$version,
        [string]$suffix = 'dev',
        [string]$installerPath = "$PSScriptRoot\contrib\win-installer"
    );
    Write-Host "Building the windows installer for $architecture"

    # Check for the files to include in the installer
    $requiredArtifacts = @(
        "$PSScriptRoot\bin\windows\podman.exe"
        "$PSScriptRoot\bin\windows\gvproxy.exe"
        "$PSScriptRoot\bin\windows\win-sshproxy.exe"
        "$PSScriptRoot\docs\build\remote\podman-for-windows.html"
    )
    $requiredArtifacts | ForEach-Object {
        if (!(Test-Path -Path $PSItem -PathType Leaf)) {
            Write-Host "$PSItem not found."
            Write-Host "Make 'podman', 'win-gvproxy' and 'docs' (or 'docs-using-podman') before making the installer:"
            Write-Host '   .\winmake.ps1 podman-remote'
            Write-Host '   .\winmake.ps1 win-gvproxy'
            Write-Host '   .\winmake.ps1 docs or .\winmake.ps1 docs-using-podman'
            Exit 1
        }
    }

    # Create the ZIP file with the full client distribution
    $zipFileDest = "$installerPath\current"
    Build-Distribution-Zip-File -destinationPath $zipFileDest

    if (-Not $version) {
        # Get Podman version from local source code
        $version = Get-Podman-Version
    }

    # Run build.ps1
    Run-Command "$installerPath\build.ps1 -Version $version -Architecture $architecture -LocalReleaseDirPath `"$zipFileDest`""
}

function Test-Installer {
    param (
        [string]$version,
        [ValidateSet('wsl', 'hyperv')]
        [string]$provider = 'wsl'
    );

    if (-Not $version) {
        # Get Podman version from local source code
        $version = Get-Podman-Version
    }

    $msiPath = "$PSScriptRoot\contrib\win-installer\podman-${version}.msi"
    if (!(Test-Path -Path $msiPath -PathType Leaf)) {
        Write-Host "MSI executable not found in path $msiPath."
        Write-Host "Make 'installer' before making the installer test:"
        Write-Host '   .\winmake.ps1 installer'
        Exit 1
    }

    $nextMsiPath = "$PSScriptRoot\contrib\win-installer\podman-9.9.9.msi"
    if (!(Test-Path -Path $nextMsiPath -PathType Leaf)) {
        Write-Host 'The automated tests include testing the upgrade from current version to a future version.'
        Write-Host 'That requires a version 9.9.9 of the installer:'
        Write-Host '   .\winmake.ps1 installer 9.9.9'
        Write-Host 'Build it and retry running installertest.'
        Exit 1
    }

    $command = "$PSScriptRoot\contrib\win-installer\test.ps1"
    $command += ' -scenario all'
    $command += " -provider $provider"
    $command += " -msiPath $msiPath"
    $command += " -nextMsiPath $nextMsiPath"
    Run-Command "${command}"
}

function Documentation {
    Write-Host 'Generating the documentation artifacts'
    # Check that pandoc is installed
    if (!(Get-Command -Name 'pandoc' -ErrorAction SilentlyContinue)) {
        Write-Host 'Pandoc not found. Pandoc is required to convert the documentation Markdown files into HTML files.'
        Write-Host "Alternatively, use '.\winmake docs-using-podman' to use a container to run pandoc and generate the documentation."
        Exit 1
    }
    # Check that the podman client is built
    $podmanClient = "$PSScriptRoot\bin\windows\podman.exe"
    if (!(Test-Path -Path $podmanClient -PathType Leaf)) {
        Write-Host "$podmanClient not found. Make 'podman-remote' before 'documentation'."
        Exit 1
    }
    Run-Command "$PSScriptRoot\docs\make.ps1 $podmanClient"
}

# DocumentationUsingPodman generates documentation with pandoc running in a container.
# This is useful on Windows arm64 where pandoc is not available.
# It's also useful to generate documentation identical to CI.
# It requires the podman client to be built and a podman machine running.
# The whole podman git repository is bind mounted in the container at /podman.
# The documentation is generated by running the command `make podman-remote-windows-docs`.
# The generated documentation is stored in the directory docs/build/remote.
function DocumentationUsingPodman {
    Write-Host 'Generating documentation artifacts'
    # Check that podman has been built
    $podmanClient = "${PSScriptRoot}\bin\windows\podman.exe"
    if (!(Test-Path -Path $podmanClient -PathType Leaf)) {
        Write-Host "$podmanClient not found. Make 'podman-remote' before 'documentation'."
        Exit 1
    }
    # Check that a podman machine exist
    $currentMachine = (& ${podmanClient} machine info -f json | ConvertFrom-Json).Host.CurrentMachine
    if (!$currentMachine) {
        Write-Host "Podman machine doesn't exist. Initialize and start one before running the validate script."
        Exit 1
    }
    # Check that the podman machine is running
    $state = (& ${podmanClient} machine info -f json | ConvertFrom-Json).Host.MachineState
    if ($state -ne 'Running') {
        Write-Host 'Podman machine is not running. Start the machine before running the validate script.'
        Exit 1
    }

    Write-Host 'Building the image to generate the documentation'
    Run-Command "${podmanClient} build --build-arg TARGET_OS=windows -t podman-docs-generator ${PSScriptRoot}/docs"

    Write-Host 'Starting the container to run the documentation build'
    Run-Command "${podmanClient} run -t --rm -v ${PSScriptRoot}:/podman podman-docs-generator"
}

function Validate {
    $podmanExecutable = 'podman'
    $podmanSrcVolumeMount = "${PSScriptRoot}:/go/src/github.com/containers/podman"
    # All files bind mounted from a Windows host are marked as executable.
    # That makes the pre-commit hook "check-executables-have-shebangs" fail.
    # Setting the environment variable "SKIP=check-executables-have-shebangs"
    # allow to skip that pre-commit hook.
    $podmanEnvVariable = '-e SKIP=check-executables-have-shebangs'
    $podmanRunArgs = "--rm -v $podmanSrcVolumeMount --security-opt label=disable -t -w /go/src/github.com/containers/podman $podmanEnvVariable"
    $validateImage = 'quay.io/libpod/validatepr:latest'
    $validateCommand = 'make .validatepr'

    # Check that podman is installed
    if (!(Get-Command -Name $podmanExecutable -ErrorAction SilentlyContinue)) {
        Write-Host "$podmanExecutable not found. $podmanExecutable is required to run the validate script."
        Exit 1
    }

    # Check that a podman machine exist
    $currentMachine = (podman machine info -f json | ConvertFrom-Json).Host.CurrentMachine
    if (!$currentMachine) {
        Write-Host "Podman machine doesn't exist. Initialize and start one before running the validate script."
        Exit 1
    }

    # Check that the podman machine is running
    $state = (podman machine info -f json | ConvertFrom-Json).Host.MachineState
    if ($state -ne 'Running') {
        Write-Host 'Podman machine is not running. Start the machine before running the validate script.'
        Exit 1
    }

    Run-Command "$podmanExecutable run $podmanRunArgs $validateImage $validateCommand"
}

function Lint {
    # Check that golangci-lint is installed
    if (!(Get-Command -Name 'golangci-lint' -ErrorAction SilentlyContinue)) {
        Write-Host 'The tool "golangci-lint" not found. Install https://golangci-lint.run/ before running the lint script.'
        Exit 1
    }

    # Check that pre-commit is installed
    if (!(Get-Command -Name 'pre-commit' -ErrorAction SilentlyContinue)) {
        Write-Host 'The tool "pre-commit" not found. Install https://pre-commit.com/ before running the lint script.'
        Exit 1
    }

    Run-Command 'pre-commit run --all-files'
    Run-Command "golangci-lint run --timeout=10m --build-tags=`"$remotetags`" $PSScriptRoot\cmd\podman"
}

# Helpers
function Build-Ginkgo {
    if (Test-Path -Path ./bin/ginkgo.exe -PathType Leaf) {
        return
    }
    Write-Host 'Building Ginkgo'
    Run-Command 'go build -o ./bin/ginkgo.exe ./vendor/github.com/onsi/ginkgo/v2/ginkgo'
}

function Git-Commit {
    # git is not installed by default on windows,
    # so if we can't get the commit, we don't include this info
    Get-Command git -ErrorAction SilentlyContinue | out-null
    if (!$?) {
        return
    }
    $commit = git rev-parse HEAD
    $dirty = git status --porcelain --untracked-files=no
    if ($dirty) {
        $commit = "$commit-dirty"
    }
    return $commit
}

function Build-Distribution-Zip-File {
    param (
        [string]$destinationPath
    );
    $binariesFolder = "$PSScriptRoot\bin\windows"
    $documentationFolder = "$PSScriptRoot\docs\build\remote\"
    $zipFile = "$destinationPath\podman-remote-release-windows_$architecture.zip"

    # Create a temporary folder to store the distribution files
    $tempFolder = New-Item -ItemType Directory -Force -Path "$env:TEMP\podman-windows"

    # Copy bin\windows\ content to the temporary folder
    Copy-Item -Recurse -Force -Path "$binariesFolder\*" -Destination "$tempFolder\"

    # Copy docs\build\remote to the temporary folder
    Copy-Item -Recurse -Force -Path "$documentationFolder" -Destination "$tempFolder\docs\"

    # If $destination path doesn't exist, create it
    if (-Not (Test-Path -Path $destinationPath -PathType Container)) {
        New-Item -ItemType Directory -Force -Path $destinationPath
    }

    # Create the ZIP file with the full client distribution
    Compress-Archive -Path "$tempFolder\*" -DestinationPath $zipFile -Force

    # Delete the temporary folder
    Remove-Item -Recurse -Force -Path "$tempFolder"
}

function Get-Podman-Version {
    $versionSrc = "$PSScriptRoot\test\version\"
    $versionBin = "$PSScriptRoot\test\version\version.exe"
    Run-Command "go build --o `"$versionBin`" `"$versionSrc`""
    $version = Invoke-Expression "$versionBin"
    # Remove the '-dev' suffix from the version
    $version = $version -replace '-.*', ''
    return $version
}

# Init script
$target = $params[0]

$remotetags = 'remote exclude_graphdriver_btrfs containers_image_openpgp'

switch ($target) {
    { $_ -in '', 'podman-remote', 'podman' } {
        Podman-Remote
    }
    'whoami' {
        Write-Host "Current user: $(whoami)"
    }
    'localunit' {
        Local-Unit
    }
    'localmachine' {
        if ($params.Count -gt 1) {
            $files = $params[1]
        }
        Local-Machine -files $files
    }
    'ginkgo-run' {
        if ($params.Count -gt 1) {
            $files = $params[1]
        }
        Ginkgo-Run -files $files
    }
    'clean' {
        Make-Clean
    }
    { $_ -in 'win-sshproxy', 'win-gvproxy' } {
        if ($params.Count -gt 1) {
            $ref = $params[1]
        }
        Win-SSHProxy($ref)
    }
    'installer' {
        if ($params.Count -gt 1) {
            Installer -version $params[1]
        }
        else {
            Installer
        }
    }
    'installertest' {
        if ($params.Count -gt 1) {
            Test-Installer -provider $params[1]
        }
        else {
            Test-Installer
        }
    }
    'docs' {
        Documentation
    }
    'docs-using-podman' {
        DocumentationUsingPodman
    }
    'validatepr' {
        Validate
    }
    'lint' {
        Lint
    }
    default {
        Write-Host 'Usage: ' $MyInvocation.MyCommand.Name '<target> [options] [<-architecture|-arch>=<amd64|arm64>]'
        Write-Host
        Write-Host 'Example: Build podman-remote '
        Write-Host ' .\winmake podman-remote'
        Write-Host
        Write-Host 'Example: Run all unit tests '
        Write-Host ' .\winmake localunit'
        Write-Host
        Write-Host 'Example: Run all machine tests '
        Write-Host ' .\winmake localmachine'
        Write-Host
        Write-Host 'Example: Run specific machine tests '
        Write-Host ' .\winmake localmachine 'basic_test.go""
        Write-Host
        Write-Host 'Example: Run all e2e tests '
        Write-Host ' .\winmake ginkgo-run'
        Write-Host
        Write-Host 'Example: Run specific e2e tests '
        Write-Host ' .\winmake ginkgo-run "system_hyperv_prep_test.go"'
        Write-Host
        Write-Host 'Example: Download win-gvproxy and win-sshproxy helpers'
        Write-Host ' .\winmake win-gvproxy'
        Write-Host
        Write-Host 'Example: Build the windows installer'
        Write-Host ' .\winmake installer'
        Write-Host
        Write-Host 'Example: Run windows installer tests'
        Write-Host ' .\winmake installertest hyperv'
        Write-Host
        Write-Host 'Example: Generate the documentation artifacts'
        Write-Host ' .\winmake docs'
        Write-Host
        Write-Host 'Example: Generate the documentation artifacts by running pandoc in a container'
        Write-Host ' .\winmake docs-using-podman'
        Write-Host
        Write-Host 'Example: Validate code changes before submitting a PR'
        Write-Host ' .\winmake validatepr'
        Write-Host
        Write-Host 'Example: Run linters'
        Write-Host ' .\winmake lint'
    }
}
