Describe 'generate bootstrap' {
    BeforeAll {
        $script:originalPath = Get-Location
        # The workflow sets these for the whole job and Pester runs every suite in one process, so
        # put back what was there rather than dropping it.
        $script:originalEnv = @{}
        foreach ($name in 'MISE_TRUSTED_CONFIG_PATHS', 'MISE_VERSION', 'MISE_INSTALL_PATH') {
            $script:originalEnv[$name] = [Environment]::GetEnvironmentVariable($name, 'Process')
        }
        # Both of these would quietly invalidate tests below rather than fail them: an inherited
        # MISE_VERSION sends the launcher to the remote checksum instead of the tampered embedded
        # one, and an inherited MISE_INSTALL_PATH puts the binary somewhere `.mise` is not.
        Remove-Item -Path Env:\MISE_VERSION -ErrorAction SilentlyContinue
        Remove-Item -Path Env:\MISE_INSTALL_PATH -ErrorAction SilentlyContinue

        $script:testDir = Join-Path $TestDrive ([System.Guid]::NewGuid().ToString())
        New-Item -ItemType Directory -Path $script:testDir | Out-Null
        Set-Location $script:testDir
        $env:MISE_TRUSTED_CONFIG_PATHS = $script:testDir

        mise generate bootstrap --localize --write bin/mise --windows | Out-Null
        # Without this a generation failure surfaces as every `It` failing on a missing file,
        # which says nothing about the cause.
        if ($LASTEXITCODE -ne 0) {
            throw "mise generate bootstrap failed with exit code $LASTEXITCODE"
        }

        # A second project generated without the flag, for the default-off assertion below.
        $script:plainDir = Join-Path $TestDrive ([System.Guid]::NewGuid().ToString())
        New-Item -ItemType Directory -Path $script:plainDir | Out-Null
        Push-Location $script:plainDir
        try {
            mise generate bootstrap --localize --write bin/mise | Out-Null
            if ($LASTEXITCODE -ne 0) {
                throw "mise generate bootstrap (no --windows) failed with exit code $LASTEXITCODE"
            }
        } finally {
            Pop-Location
        }
    }

    AfterAll {
        Set-Location $script:originalPath
        foreach ($name in $script:originalEnv.Keys) {
            if ($null -eq $script:originalEnv[$name]) {
                Remove-Item -Path "Env:\$name" -ErrorAction SilentlyContinue
            } else {
                [Environment]::SetEnvironmentVariable($name, $script:originalEnv[$name], 'Process')
            }
        }
    }

    It 'writes a .cmd beside the bash script when asked' {
        # Windows cannot execute the shebang script, so without this there is nothing to run.
        Test-Path 'bin\mise' | Should -BeTrue
        Test-Path 'bin\mise.cmd' | Should -BeTrue
        (Get-Content 'bin\mise.cmd' -Raw) | Should -BeLike '*rem generated by mise generate bootstrap*'
    }

    It 'writes only the bash script without --windows' {
        # The launcher is opt-in, so the default has to leave the project exactly as it was before
        # the flag existed. Paired with the case above, this is what pins the flag as the switch.
        Test-Path (Join-Path $script:plainDir 'bin\mise') | Should -BeTrue
        Test-Path (Join-Path $script:plainDir 'bin\mise.cmd') | Should -BeFalse
    }

    It 'rejects --windows without --write' {
        # stdout cannot carry two files, and a silently ignored flag would read as success.
        mise generate bootstrap --windows 2>&1 | Out-Null
        $LASTEXITCODE | Should -Not -Be 0
    }

    It 'downloads and runs mise' {
        # The point of the change: not that the file exists, but that it works. This really does
        # fetch the release binary and check it before running.
        $out = & cmd.exe /c 'bin\mise.cmd --version' 2>&1 | Out-String
        $LASTEXITCODE | Should -Be 0
        $out | Should -Match '\d+\.\d+\.\d+'
        Test-Path (Join-Path $script:testDir '.mise') | Should -BeTrue
    }

    It 'does not download again once installed' {
        # Mirrors `test -f "$MISE_INSTALL_PATH" || install` on the bash side.
        $installed = { @(Get-ChildItem (Join-Path $script:testDir '.mise') -Filter 'mise-*.exe') }
        # Pin the count first: with an empty directory both sides of the comparison below would be
        # $null and the test would pass while proving nothing.
        (& $installed).Count | Should -Be 1
        $before = (& $installed)[0].LastWriteTime

        & cmd.exe /c 'bin\mise.cmd --version' | Out-Null
        $LASTEXITCODE | Should -Be 0

        (& $installed).Count | Should -Be 1
        (& $installed)[0].LastWriteTime | Should -Be $before
    }

    It 'refuses a binary whose checksum does not match' {
        # The control for the check above: without this, "it ran" would not tell us the hash is
        # ever compared. One character is changed, so only the comparison can catch it.
        $tamperDir = Join-Path $TestDrive ([System.Guid]::NewGuid().ToString())
        New-Item -ItemType Directory -Path (Join-Path $tamperDir 'bin') | Out-Null
        $body = Get-Content (Join-Path $script:testDir 'bin\mise.cmd') -Raw
        # Both sums, not just x64: an arm64 runner selects sum_arm64, so tampering one would let
        # the download verify and the test would fail for the wrong reason.
        $body = [regex]::Replace($body, '(set "sum_(?:x64|arm64)=[0-9a-f]{63})[0-9a-f]"', {
            param($m) $m.Groups[1].Value + 'z"'
        })
        Set-Content (Join-Path $tamperDir 'bin\mise.cmd') $body -NoNewline

        # The rejected download is deleted, so count what is there beforehand. Checking for
        # `.mise\mise-*.exe` under a directory created moments ago would be false whether or not
        # the script cleaned up.
        $stragglers = { @(Get-ChildItem $env:TEMP -Filter 'mise-bootstrap-*.exe' -ErrorAction SilentlyContinue) }
        $tempBefore = (& $stragglers).Count

        Push-Location $tamperDir
        try {
            $out = & cmd.exe /c 'bin\mise.cmd --version' 2>&1 | Out-String
            $LASTEXITCODE | Should -Not -Be 0
            $out | Should -BeLike '*checksum mismatch*'
            Test-Path (Join-Path $tamperDir '.mise\mise-*.exe') | Should -BeFalse
            (& $stragglers).Count | Should -Be $tempBefore
        } finally {
            Pop-Location
        }
    }
}
