name: Build Release

on:
  workflow_dispatch:
    inputs:
      version_tag:
        description: 'Version label for the build (e.g. alpha-1, beta-2)'
        required: false
        default: ''

jobs:
  build:
    name: Build on ${{ matrix.os }}
    runs-on: ${{ matrix.os }}
    env:
      CMAKE_BUILD_PARALLEL_LEVEL: '4'
      PRISM_JUCE_VERSION: 8.0.4
      PRISM_WEBVIEW2_VERSION: 1.0.1901.177
      SCCACHE_GHA_ENABLED: 'true'
      SCCACHE_IGNORE_SERVER_IO_ERROR: '1'
    strategy:
      fail-fast: false
      matrix:
        include:
          - os: macos-latest
            dist_cmd: dist:mac
          - os: ubuntu-latest
            dist_cmd: dist:linux:packages
          # Pinned to windows-2022 because windows-latest now ships VS 2026
          # (internal version 18), which the @electron/node-gyp version in our
          # lockfile rejects as "unsupported". 2022 has VS 2022 only, no ambiguity.
          - os: windows-2022
            dist_cmd: dist:win

    steps:
      - uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: 22.x
          cache: 'npm'

      - name: Cache Electron
        uses: actions/cache@v4
        with:
          path: |
            ~/.cache/electron
            ~/.cache/electron-builder
          key: ${{ runner.os }}-electron-cache

      - name: Setup sccache
        if: runner.os != 'Linux'
        uses: mozilla-actions/sccache-action@v0.0.10

      - name: Cache JUCE
        uses: actions/cache@v4
        with:
          path: .ci-cache/juce/JUCE-${{ env.PRISM_JUCE_VERSION }}
          key: ${{ runner.os }}-juce-${{ env.PRISM_JUCE_VERSION }}-v1

      - name: Prepare JUCE checkout
        shell: bash
        run: |
          juce_dir=".ci-cache/juce/JUCE-${PRISM_JUCE_VERSION}"
          if [ ! -f "$juce_dir/CMakeLists.txt" ]; then
            rm -rf "$juce_dir"
            mkdir -p "$(dirname "$juce_dir")"
            git clone --depth 1 --branch "$PRISM_JUCE_VERSION" https://github.com/juce-framework/JUCE.git "$juce_dir"
          fi

      - name: Install dependencies
        env:
          PRISM_SKIP_NATIVE_POSTINSTALL: '1'
        run: npm ci

      - name: Install Linux native build dependencies
        if: runner.os == 'Linux'
        run: |
          sudo apt-get update
          sudo apt-get install -y \
            build-essential \
            cmake \
            ninja-build \
            pkg-config \
            libpulse-dev \
            libasound2-dev \
            libjack-jackd2-dev \
            ladspa-sdk \
            libcurl4-openssl-dev \
            libfreetype-dev \
            libfontconfig1-dev \
            libgtk-3-dev \
            libx11-dev \
            libxcomposite-dev \
            libxcursor-dev \
            libxext-dev \
            libxinerama-dev \
            libxrandr-dev \
            libxrender-dev \
            libwebkit2gtk-4.1-dev \
            libglu1-mesa-dev \
            mesa-common-dev \
            rpm

      - name: Build native DSP module
        run: npm run rebuild:native

      - name: Verify native module exists
        shell: bash
        run: |
          if [ ! -f native/build/Release/visualizer_dsp.node ]; then
            echo "ERROR: native DSP module was not built!"
            exit 1
          fi
          echo "Native module built successfully: $(ls -lh native/build/Release/visualizer_dsp.node)"

      - name: Typecheck
        run: npm run typecheck

      - name: Test native spectrum and capture exports
        run: npm run test:spectrum-native

      - name: Build and test Prism TUI
        run: npm run test:tui

      # The AppImage-specific electron-builder config excludes both the native
      # terminal frontend and DAW plugins regardless of local staging state.
      - name: Build Linux AppImage
        if: runner.os == 'Linux'
        run: npm run dist:linux:appimage
        env:
          CSC_IDENTITY_AUTO_DISCOVERY: 'false'

      # --- JUCE plugins. These are staged into installer resources after the
      # app-only AppImage has already been built on Linux. ---

      - name: Build plugin webview bundle
        run: npm run plugin-ui:build

      - name: Configure JUCE plugins (macOS)
        if: runner.os == 'macOS'
        run: |
          cmake -B plugin/build -S plugin -G Ninja \
            -DCMAKE_BUILD_TYPE=Release \
            -DJUCE_PATH="$GITHUB_WORKSPACE/.ci-cache/juce/JUCE-${PRISM_JUCE_VERSION}" \
            -DPRISM_PLUGIN_FORMATS="AU;VST3" \
            -DPRISM_COPY_PLUGIN_AFTER_BUILD=OFF \
            -DCMAKE_C_COMPILER_LAUNCHER=sccache \
            -DCMAKE_CXX_COMPILER_LAUNCHER=sccache

      - name: Configure JUCE plugins (Linux)
        if: runner.os == 'Linux'
        run: |
          cmake -B plugin/build -S plugin -G Ninja \
            -DCMAKE_BUILD_TYPE=Release \
            -DJUCE_PATH="$GITHUB_WORKSPACE/.ci-cache/juce/JUCE-${PRISM_JUCE_VERSION}" \
            -DPRISM_PLUGIN_FORMATS=VST3 \
            -DPRISM_COPY_PLUGIN_AFTER_BUILD=OFF

      # JUCE's NEEDS_WEBVIEW2 expects the Microsoft.Web.WebView2 NuGet package
      # to already be installed locally; it doesn't fetch it itself. Install it
      # and point CMake at the resulting folder via JUCE_WEBVIEW2_PACKAGE_LOCATION.
      - name: Cache WebView2 SDK (Windows)
        if: runner.os == 'Windows'
        uses: actions/cache@v4
        with:
          path: ${{ runner.temp }}/webview2
          key: ${{ runner.os }}-webview2-${{ env.PRISM_WEBVIEW2_VERSION }}-v1

      - name: Install WebView2 SDK (Windows)
        if: runner.os == 'Windows'
        shell: pwsh
        run: |
          $sdkRoot = "$env:RUNNER_TEMP\webview2"
          $packageRoot = Join-Path $sdkRoot "Microsoft.Web.WebView2"
          $webViewHeader = Join-Path $packageRoot "build\native\include\WebView2.h"
          if (-not (Test-Path $webViewHeader)) {
            Remove-Item -Recurse -Force $sdkRoot -ErrorAction SilentlyContinue
            New-Item -ItemType Directory -Force -Path $sdkRoot | Out-Null
            nuget install Microsoft.Web.WebView2 -Version $env:PRISM_WEBVIEW2_VERSION -OutputDirectory $sdkRoot -ExcludeVersion
          }
          "JUCE_WEBVIEW2_PACKAGE_LOCATION=$sdkRoot" >> $env:GITHUB_ENV

      - name: Configure JUCE plugins (Windows)
        if: runner.os == 'Windows'
        shell: cmd
        run: |
          for /f "usebackq tokens=*" %%i in (`vswhere -latest -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath`) do set VSINSTALL=%%i
          call "%VSINSTALL%\VC\Auxiliary\Build\vcvars64.bat"
          cmake -B plugin/build -S plugin -G Ninja -DCMAKE_BUILD_TYPE=Release -DJUCE_PATH="%GITHUB_WORKSPACE%\.ci-cache\juce\JUCE-%PRISM_JUCE_VERSION%" -DPRISM_PLUGIN_FORMATS=VST3 -DPRISM_COPY_PLUGIN_AFTER_BUILD=OFF -DJUCE_WEBVIEW2_PACKAGE_LOCATION="%JUCE_WEBVIEW2_PACKAGE_LOCATION%" -DCMAKE_C_COMPILER_LAUNCHER=sccache -DCMAKE_CXX_COMPILER_LAUNCHER=sccache

      - name: Build JUCE plugins (macOS)
        if: runner.os == 'macOS'
        run: cmake --build plugin/build --target PrismInstallerPlugins --parallel 4

      - name: Build JUCE plugins (Linux)
        if: runner.os == 'Linux'
        run: cmake --build plugin/build --target PrismInstallerPlugins --parallel 4

      - name: Build JUCE plugins (Windows)
        if: runner.os == 'Windows'
        shell: cmd
        run: |
          for /f "usebackq tokens=*" %%i in (`vswhere -latest -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath`) do set VSINSTALL=%%i
          call "%VSINSTALL%\VC\Auxiliary\Build\vcvars64.bat"
          cmake --build plugin/build --target PrismInstallerPlugins --config Release --parallel 4

      - name: Show sccache stats
        if: runner.os != 'Linux'
        run: sccache --show-stats

      - name: Stage plugins for installer
        shell: bash
        run: |
          rm -rf plugin/dist-installer
          mkdir -p plugin/dist-installer
          mkdir -p plugin/dist-installer/VST3
          [ "$RUNNER_OS" = "macOS" ] && mkdir -p plugin/dist-installer/AU
          for art in PrismSpectrum PrismOscilloscope PrismVUMeter PrismLUFSMeter PrismVectorscope PrismSpectrogram PrismWaveform; do
            vst3_dir="plugin/build/${art}_artefacts/Release/VST3"
            [ -d "$vst3_dir" ] && find "$vst3_dir" -maxdepth 1 -name "*.vst3" -exec cp -R {} plugin/dist-installer/VST3/ \;
            if [ "$RUNNER_OS" = "macOS" ]; then
              au_dir="plugin/build/${art}_artefacts/Release/AU"
              [ -d "$au_dir" ] && find "$au_dir" -maxdepth 1 -name "*.component" -exec cp -R {} plugin/dist-installer/AU/ \;
            fi
          done
          echo "--- staged VST3 ---"
          ls -la plugin/dist-installer/VST3
          vst3_count="$(find plugin/dist-installer/VST3 -maxdepth 1 -name "*.vst3" | wc -l | tr -d ' ')"
          if [ "$vst3_count" -ne 7 ]; then
            echo "ERROR: expected 7 VST3 plugins, found $vst3_count"
            exit 1
          fi
          if [ "$RUNNER_OS" = "Linux" ]; then
            for vst3 in plugin/dist-installer/VST3/*.vst3; do
              if [ ! -d "$vst3/Contents/x86_64-linux" ]; then
                echo "ERROR: Linux VST3 bundle is missing Contents/x86_64-linux: $vst3"
                exit 1
              fi
            done
            cp resources/installer/linux/install-vst3.sh plugin/dist-installer/install-vst3.sh
            chmod +x plugin/dist-installer/install-vst3.sh
          fi
          if [ "$RUNNER_OS" = "macOS" ]; then
            echo "--- staged AU ---"
            ls -la plugin/dist-installer/AU
            au_count="$(find plugin/dist-installer/AU -maxdepth 1 -name "*.component" | wc -l | tr -d ' ')"
            if [ "$au_count" -ne 7 ]; then
              echo "ERROR: expected 7 AU plugins, found $au_count"
              exit 1
            fi
          fi

      - name: Stage Prism TUI for installer
        run: npm run stage:tui

      - name: Smoke-test staged Prism TUI
        shell: bash
        run: |
          tui_binary="tui/dist-installer/prism-tui"
          if [ "$RUNNER_OS" = "Windows" ]; then
            tui_binary="tui/dist-installer/prism-tui.exe"
          fi
          if [ ! -f "$tui_binary" ]; then
            echo "ERROR: staged prism-tui executable was not found: $tui_binary"
            exit 1
          fi
          "$tui_binary" --help
          "$tui_binary" --version

      - name: Build distributable
        if: runner.os != 'Linux'
        run: npm run ${{ matrix.dist_cmd }}
        env:
          CSC_IDENTITY_AUTO_DISCOVERY: 'false'

      - name: Build Linux packages with VST3 plugins
        if: runner.os == 'Linux'
        shell: bash
        run: |
          rm -rf dist/linux-unpacked
          npm run ${{ matrix.dist_cmd }}
        env:
          CSC_IDENTITY_AUTO_DISCOVERY: 'false'

      - name: Upload artifacts
        uses: actions/upload-artifact@v4
        with:
          name: prism-${{ runner.os }}${{ inputs.version_tag && format('-{0}', inputs.version_tag) || '' }}
          path: |
            dist/*.exe
            dist/*.pkg
            dist/*.zip
            dist/*.AppImage
            dist/*.deb
            dist/*.rpm
            dist/*.tar.gz
          if-no-files-found: error
          retention-days: 30
