---
name: CI-Windows
permissions:
  contents: read

on:
  workflow_call:
    inputs:
      release_commit:
        required: true
        type: string
      release_version:
        required: true
        type: string
    secrets:
      CODECOV_TOKEN:
        required: false

jobs:
  build_windows:
    name: ${{ matrix.name }}
    runs-on: ${{ matrix.os }}
    defaults:
      run:
        shell: msys2 {0}
    strategy:
      fail-fast: false
      matrix:
        include:
          - name: Windows-AMD64
            os: windows-2022
            arch: x86_64
            msystem: ucrt64
            toolchain: ucrt-x86_64
    steps:
      - name: Checkout
        uses: actions/checkout@v5
        with:
          submodules: recursive

      - name: Prepare tests
        id: prepare-tests
        if: false  # todo: DirectX11 is not available, so even software encoder fails
        shell: pwsh
        run: |
          # function to download and extract a zip file
          function DownloadAndExtract {
            param (
              [string]$Uri,
              [string]$OutFile
            )

            $maxRetries = 5
            $retryCount = 0
            $success = $false

            while (-not $success -and $retryCount -lt $maxRetries) {
              $retryCount++
              Write-Host "Downloading $Uri to $OutFile, attempt $retryCount of $maxRetries"
              try {
                Invoke-WebRequest -Uri $Uri -OutFile $OutFile
                $success = $true
              } catch {
                Write-Host "Attempt $retryCount of $maxRetries failed with error: $($_.Exception.Message). Retrying..."
                Start-Sleep -Seconds 5
              }
            }

            if (-not $success) {
              Write-Host "Failed to download the file after $maxRetries attempts."
              exit 1
            }

            # use .NET to get the base name of the file
            $baseName = (Get-Item $OutFile).BaseName

            # Extract the zip file
            Expand-Archive -Path $OutFile -DestinationPath $baseName
          }

          # virtual display driver
          DownloadAndExtract `
            -Uri "https://www.amyuni.com/downloads/usbmmidd_v2.zip" `
            -OutFile "usbmmidd_v2.zip"

          # install
          Set-Location -Path usbmmidd_v2/usbmmidd_v2
          ./deviceinstaller64 install usbmmidd.inf usbmmidd

          # create the virtual display
          ./deviceinstaller64 enableidd 1

          # move up a directory
          Set-Location -Path ../..

          # install devcon
          DownloadAndExtract `
            -Uri "https://github.com/Drawbackz/DevCon-Installer/releases/download/1.4-rc/Devcon.Installer.zip" `
            -OutFile "Devcon.Installer.zip"
          Set-Location -Path Devcon.Installer
          # hash needs to match OS version
          # https://github.com/Drawbackz/DevCon-Installer/blob/master/devcon_sources.json
          Start-Process -FilePath "./Devcon Installer.exe" -Wait -ArgumentList `
            'install', `
            '-hash', '54004C83EE34F6A55380528A8B29F4C400E61FBB947A19E0AB9E5A193D7D961E', `
            '-addpath', `
            '-update', `
            '-dir', 'C:\Windows\System32'

          # disable Hyper-V Video
          # https://stackoverflow.com/a/59490940
          C:\Windows\System32\devcon.exe disable "VMBUS\{da0a7802-e377-4aac-8e77-0558eb1073f8}"

          # move up a directory
          Set-Location -Path ..

          # multi monitor tool
          DownloadAndExtract `
            -Uri "http://www.nirsoft.net/utils/multimonitortool-x64.zip" `
            -OutFile "multimonitortool.zip"

          # enable the virtual display
          # http://www.nirsoft.net/utils/multi_monitor_tool.html
          Set-Location -Path multimonitortool

          # Original Hyper-V is \\.\DISPLAY1, it will recreate itself as \\.\DISPLAY6 (or something higher than 2)
          # USB Mobile Monitor Virtual Display is \\.\DISPLAY2

          # these don't seem to work if not using runAs
          # todo: do they work if not using runAs?
          Start-Process powershell -Verb runAs -ArgumentList '-Command ./MultiMonitorTool.exe /enable \\.\DISPLAY2'
          Start-Process powershell -Verb runAs -ArgumentList '-Command ./MultiMonitorTool.exe /SetPrimary \\.\DISPLAY2'

          # wait a few seconds
          Start-Sleep -s 5

          # list monitors
          ./MultiMonitorTool.exe /stext monitor_list.txt

          # wait a few seconds
          Start-Sleep -s 5

          # print the monitor list
          Get-Content -Path monitor_list.txt

      - name: Setup Dependencies Windows
        # if a dependency needs to be pinned, see https://github.com/LizardByte/build-deps/pull/186
        uses: msys2/setup-msys2@v2
        with:
          msystem: ${{ matrix.msystem }}
          update: true
          install: >-
            wget

      - name: Update Windows dependencies
        env:
          MSYSTEM: ${{ matrix.msystem }}
          TOOLCHAIN: ${{ matrix.toolchain }}
        shell: msys2 {0}
        run: |
          # variables
          declare -A pinned_deps

          # dependencies
          dependencies=(
            "git"
            "mingw-w64-${TOOLCHAIN}-cmake"
            "mingw-w64-${TOOLCHAIN}-cppwinrt"
            "mingw-w64-${TOOLCHAIN}-curl-winssl"
            "mingw-w64-${TOOLCHAIN}-gcc"
            "mingw-w64-${TOOLCHAIN}-graphviz"
            "mingw-w64-${TOOLCHAIN}-MinHook"
            "mingw-w64-${TOOLCHAIN}-miniupnpc"
            "mingw-w64-${TOOLCHAIN}-nlohmann-json"
            "mingw-w64-${TOOLCHAIN}-nodejs"
            "mingw-w64-${TOOLCHAIN}-nsis"
            "mingw-w64-${TOOLCHAIN}-onevpl"
            "mingw-w64-${TOOLCHAIN}-openssl"
            "mingw-w64-${TOOLCHAIN}-opus"
            "mingw-w64-${TOOLCHAIN}-toolchain"
          )

          # do not modify below this line

          ignore_packages=()
          tarballs=""
          for pkg in "${!pinned_deps[@]}"; do
            ignore_packages+=("${pkg}")
            version="${pinned_deps[$pkg]}"
            tarball="${pkg}-${version}-any.pkg.tar.zst"

            # download working version
            wget "https://repo.msys2.org/mingw/${MSYSTEM}/${tarball}"

            tarballs="${tarballs} ${tarball}"
          done

          # Create the ignore string for pacman
          ignore_list=$(IFS=,; echo "${ignore_packages[*]}")

          # install pinned dependencies
          if [ -n "${tarballs}" ]; then
            pacman -U --noconfirm "${tarballs}"
          fi

          # Only add --ignore if we have packages to ignore
          if [ -n "${ignore_list}" ]; then
            pacman -Syu --noconfirm --ignore="${ignore_list}" "${dependencies[@]}"
          else
            pacman -Syu --noconfirm "${dependencies[@]}"
          fi

      - name: Install Doxygen
        # GCC compiled doxygen has issues when running graphviz
        env:
          DOXYGEN_VERSION: "1.11.0"
        shell: pwsh
        run: |
          # Set version variables
          $doxy_ver = $env:DOXYGEN_VERSION
          $_doxy_ver = $doxy_ver.Replace(".", "_")

          # Download the Doxygen installer
          Invoke-WebRequest -Uri `
            "https://github.com/doxygen/doxygen/releases/download/Release_${_doxy_ver}/doxygen-${doxy_ver}-setup.exe" `
            -OutFile "doxygen-setup.exe"

          # Run the installer
          Start-Process `
            -FilePath .\doxygen-setup.exe `
            -ArgumentList `
              '/VERYSILENT' `
          -Wait `
          -NoNewWindow

          # Clean up
          Remove-Item -Path doxygen-setup.exe

      - name: Setup python
        id: setup-python
        uses: actions/setup-python@v5
        with:
          python-version: '3.11'

      - name: Python Path
        id: python-path
        shell: msys2 {0}
        run: |
          # replace backslashes with double backslashes
          python_path=$(echo "${{ steps.setup-python.outputs.python-path }}" | sed 's/\\/\\\\/g')

          # step output
          echo "python-path=${python_path}"
          echo "python-path=${python_path}" >> "${GITHUB_OUTPUT}"

      - name: Build Windows
        shell: msys2 {0}
        env:
          BRANCH: ${{ github.head_ref || github.ref_name }}
          BUILD_VERSION: ${{ inputs.release_version }}
          CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
          COMMIT: ${{ inputs.release_commit }}
        run: |
          mkdir -p build
          cmake \
            -B build \
            -G Ninja \
            -S . \
            -DBUILD_WERROR=ON \
            -DCMAKE_BUILD_TYPE=RelWithDebInfo \
            -DSUNSHINE_ASSETS_DIR=assets \
            -DSUNSHINE_PUBLISHER_NAME='${{ github.repository_owner }}' \
            -DSUNSHINE_PUBLISHER_WEBSITE='https://app.lizardbyte.dev' \
            -DSUNSHINE_PUBLISHER_ISSUE_URL='https://app.lizardbyte.dev/support'
          echo "::add-matcher::.github/matchers/gcc.json"
          ninja -C build
          echo "::remove-matcher owner=gcc::"

      - name: Package Windows
        shell: msys2 {0}
        run: |
          mkdir -p artifacts
          cd build

          # package
          cpack -G NSIS
          cpack -G ZIP

          # move
          mv ./cpack_artifacts/Sunshine.exe ../artifacts/Sunshine-${{ matrix.name }}-installer.exe
          mv ./cpack_artifacts/Sunshine.zip ../artifacts/Sunshine-${{ matrix.name }}-portable.zip

      - name: Run tests
        id: test
        shell: msys2 {0}
        working-directory: build/tests
        run: |
          ./test_sunshine.exe --gtest_color=yes --gtest_output=xml:test_results.xml

      - name: Generate gcov report
        id: test_report
        # any except canceled or skipped
        if: always() && (steps.test.outcome == 'success' || steps.test.outcome == 'failure')
        shell: msys2 {0}
        working-directory: build
        run: |
          ${{ steps.python-path.outputs.python-path }} -m pip install gcovr
          ${{ steps.python-path.outputs.python-path }} -m gcovr . -r ../src \
            --exclude-noncode-lines \
            --exclude-throw-branches \
            --exclude-unreachable-branches \
            --verbose \
            --xml-pretty \
            -o coverage.xml

      - name: Upload coverage artifact
        if: >-
          always() &&
          (steps.test_report.outcome == 'success')
        uses: actions/upload-artifact@v4
        with:
          name: coverage-${{ matrix.name }}
          path: |
            build/coverage.xml
            build/tests/test_results.xml
          if-no-files-found: error

      - name: Package Windows Debug Info
        shell: pwsh
        working-directory: build
        run: |
          # use .dbg file extension for binaries to avoid confusion with real packages
          Get-ChildItem -File -Recurse | `
            % { Rename-Item -Path $_.PSPath -NewName $_.Name.Replace(".exe",".dbg") }

          # save the binaries with debug info
          7z -r `
            "-xr!CMakeFiles" `
            "-xr!cpack_artifacts" `
            a "../artifacts/Sunshine-${{ matrix.name }}-debuginfo.7z" "*.dbg"

      - name: Upload Artifacts
        uses: actions/upload-artifact@v4
        with:
          name: build-${{ matrix.name }}
          path: artifacts/
          if-no-files-found: error
