# =============================================================================
# Wraith — Build & Sign Release
# =============================================================================
# Builds the Wails v3 desktop app for Windows amd64, cross-compiles FreeRDP3
# from source via MinGW, signs everything with Azure Key Vault EV cert,
# then uploads to SeaweedFS.
#
# Trigger: push a tag matching v* (e.g. v1.0.0) or run manually.
#
# Required secrets:
#   AZURE_TENANT_ID       — Azure AD tenant
#   AZURE_CLIENT_ID       — Service principal client ID
#   AZURE_CLIENT_SECRET   — Service principal secret
#   AZURE_KEY_VAULT_URL   — e.g. https://my-vault.vault.azure.net
#   AZURE_CERT_NAME       — Certificate/key name in the vault
#   GIT_TOKEN             — PAT for cloning private repo
# =============================================================================

name: Build & Sign Wraith

on:
  push:
    tags:
      - 'v*'
  workflow_dispatch:

jobs:
  build-and-sign:
    name: Build Windows + Sign
    runs-on: linux
    steps:
      # ---------------------------------------------------------------
      # Checkout
      # ---------------------------------------------------------------
      - name: Checkout code
        run: git clone --depth 1 --branch ${{ github.ref_name }} https://${{ secrets.GIT_TOKEN }}@git.command.vigilcyber.com/vstockwell/wraith.git .

      # ---------------------------------------------------------------
      # Extract version from tag
      # ---------------------------------------------------------------
      - name: Get version from tag
        id: version
        run: |
          TAG=$(echo "${{ github.ref_name }}" | sed 's/^v//')
          echo "version=${TAG}" >> $GITHUB_OUTPUT
          echo "Building version: ${TAG}"

      # ---------------------------------------------------------------
      # Install toolchain
      # ---------------------------------------------------------------
      - name: Install build dependencies
        run: |
          apt-get update -qq
          apt-get install -y -qq \
            mingw-w64 mingw-w64-tools binutils-mingw-w64 \
            cmake ninja-build nasm meson \
            default-jre-headless \
            python3 curl pkg-config nsis

          # Node.js 22 — Tailwind CSS v4 and Naive UI require Node >= 20
          NODE_MAJOR=$(node --version 2>/dev/null | sed 's/v\([0-9]*\).*/\1/' || echo "0")
          if [ "$NODE_MAJOR" -lt 20 ]; then
            echo "Node $NODE_MAJOR is too old, installing Node 22..."
            curl -fsSL https://deb.nodesource.com/setup_22.x | bash -
            apt-get install -y -qq nodejs
          fi

          echo "=== Toolchain versions ==="
          go version
          node --version
          x86_64-w64-mingw32-gcc --version | head -1
          cmake --version | head -1

      # ===============================================================
      # FreeRDP3 dependencies — cross-compile zlib + OpenSSL for MinGW
      # ===============================================================
      - name: Build FreeRDP3 dependencies (zlib + OpenSSL for MinGW)
        run: |
          INSTALL_PREFIX="/tmp/mingw-deps"
          mkdir -p "$INSTALL_PREFIX"
          export CROSS=x86_64-w64-mingw32

          # --- zlib ---
          echo "=== Building zlib for MinGW ==="
          ZLIB_VERSION="1.3.1"
          curl -sSL -o /tmp/zlib.tar.gz "https://github.com/madler/zlib/releases/download/v${ZLIB_VERSION}/zlib-${ZLIB_VERSION}.tar.gz"
          tar -xzf /tmp/zlib.tar.gz -C /tmp
          cd /tmp/zlib-${ZLIB_VERSION}
          CC=${CROSS}-gcc AR=${CROSS}-ar RANLIB=${CROSS}-ranlib \
            ./configure --prefix="$INSTALL_PREFIX" --static
          make -j$(nproc)
          make install
          echo "zlib installed to $INSTALL_PREFIX"

          # --- OpenSSL ---
          echo "=== Building OpenSSL for MinGW ==="
          OPENSSL_VERSION="3.4.1"
          curl -sSL -o /tmp/openssl.tar.gz "https://github.com/openssl/openssl/releases/download/openssl-${OPENSSL_VERSION}/openssl-${OPENSSL_VERSION}.tar.gz"
          tar -xzf /tmp/openssl.tar.gz -C /tmp
          cd /tmp/openssl-${OPENSSL_VERSION}
          ./Configure mingw64 no-shared no-tests \
            --cross-compile-prefix=${CROSS}- \
            --prefix="$INSTALL_PREFIX"
          make -j$(nproc)
          make install_sw
          echo "OpenSSL installed to $INSTALL_PREFIX"

      # ===============================================================
      # FreeRDP3 — Cross-compile from source via MinGW
      # ===============================================================
      - name: Build FreeRDP3 for Windows (MinGW cross-compile)
        run: |
          FREERDP_VERSION="3.10.3"
          INSTALL_PREFIX="/tmp/mingw-deps"
          echo "=== Building FreeRDP ${FREERDP_VERSION} for Windows amd64 via MinGW ==="

          # Nuke any cached source/build from previous runs
          rm -rf /tmp/FreeRDP-* /tmp/freerdp-install /tmp/freerdp.tar.gz

          # Download FreeRDP source
          curl -sSL -o /tmp/freerdp.tar.gz "https://github.com/FreeRDP/FreeRDP/archive/refs/tags/${FREERDP_VERSION}.tar.gz"
          tar -xzf /tmp/freerdp.tar.gz -C /tmp
          cd /tmp/FreeRDP-${FREERDP_VERSION}

          # Create MinGW toolchain file pointing to our built deps
          cat > /tmp/mingw-toolchain.cmake << TCEOF
          set(CMAKE_SYSTEM_NAME Windows)
          set(CMAKE_SYSTEM_PROCESSOR AMD64)
          set(CMAKE_C_COMPILER x86_64-w64-mingw32-gcc)
          set(CMAKE_CXX_COMPILER x86_64-w64-mingw32-g++)
          set(CMAKE_RC_COMPILER x86_64-w64-mingw32-windres)
          set(CMAKE_FIND_ROOT_PATH /usr/x86_64-w64-mingw32;${INSTALL_PREFIX})
          set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
          set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
          set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
          set(CMAKE_PREFIX_PATH ${INSTALL_PREFIX})
          set(OPENSSL_ROOT_DIR ${INSTALL_PREFIX})
          set(ZLIB_ROOT ${INSTALL_PREFIX})
          TCEOF

          # Force suppress all warnings via environment — FreeRDP cmake overrides CMAKE_C_FLAGS
          export CFLAGS="-w"
          export CXXFLAGS="-w"

          # Stub out unwind source — uses dlfcn.h which doesn't exist in MinGW
          # Replace with empty implementations so cmake still finds the file
          cat > winpr/libwinpr/utils/unwind/debug.c << 'STUBEOF'
          #include <winpr/wtypes.h>
          void* winpr_unwind_backtrace(void) { return NULL; }
          char** winpr_unwind_backtrace_symbols(void* buffer, size_t* used) { *used = 0; return NULL; }
          void winpr_unwind_backtrace_free(void* buffer) { }
          STUBEOF

          cmake -B build -G Ninja \
            -DCMAKE_TOOLCHAIN_FILE=/tmp/mingw-toolchain.cmake \
            -DCMAKE_BUILD_TYPE=Release \
            -DCMAKE_C_FLAGS_INIT="-w" \
            -DCMAKE_INSTALL_PREFIX=/tmp/freerdp-install \
            -DBUILD_SHARED_LIBS=ON \
            -DWITH_CLIENT=OFF \
            -DWITH_CLIENT_SDL=OFF \
            -DWITH_CLIENT_WINDOWS=OFF \
            -DWITH_SERVER=OFF \
            -DWITH_SHADOW=OFF \
            -DWITH_PROXY=OFF \
            -DWITH_SAMPLE=OFF \
            -DWITH_PLATFORM_SERVER=OFF \
            -DWITH_WINPR_TOOLS=OFF \
            -DWITH_FFMPEG=OFF \
            -DWITH_SWSCALE=OFF \
            -DWITH_CAIRO=OFF \
            -DWITH_CUPS=OFF \
            -DWITH_PULSE=OFF \
            -DWITH_ALSA=OFF \
            -DWITH_OSS=OFF \
            -DWITH_WAYLAND=OFF \
            -DWITH_X11=OFF \
            -DCHANNEL_URBDRC=OFF \
            -DWITH_OPENH264=OFF \
            -DWITH_LIBSYSTEMD=OFF \
            -DWITH_UNWIND=OFF \
            -DWITH_PCSC=OFF \
            -DWITH_SMARTCARD=OFF

          # Build — use -v for verbose output on failure
          cmake --build build --parallel $(nproc) -- -v 2>&1 || {
            echo "=== Build failed — retrying with single thread for clear error output ==="
            cmake --build build -j1 -- -v 2>&1 | tail -50
            exit 1
          }
          cmake --install build

          echo "=== FreeRDP3 DLLs built ==="
          ls -la /tmp/freerdp-install/bin/*.dll 2>/dev/null || ls -la /tmp/freerdp-install/lib/*.dll 2>/dev/null || echo "Checking build output..."
          find /tmp/freerdp-install -name "*.dll" -type f

      - name: Stage FreeRDP3 DLLs
        run: |
          mkdir -p dist

          # Copy all FreeRDP DLLs (MinGW produces lib-prefixed names)
          find /tmp/freerdp-install -name "*.dll" -type f -exec cp {} dist/ \;

          echo "=== Staged DLLs ==="
          ls -la dist/*.dll 2>/dev/null || echo "No DLLs found — FreeRDP build may have failed"

      # ===============================================================
      # Build Wraith
      # ===============================================================
      - name: Build frontend
        run: |
          cd frontend
          npm ci
          npm run build
          echo "Frontend build complete:"
          ls -la dist/

      - name: Build wraith.exe (Windows amd64)
        run: |
          VERSION="${{ steps.version.outputs.version }}"
          echo "=== Cross-compiling wraith.exe for Windows amd64 ==="

          GOOS=windows GOARCH=amd64 CGO_ENABLED=0 \
            go build \
              -ldflags="-s -w -H windowsgui -X main.version=${VERSION}" \
              -o dist/wraith.exe \
              .

          ls -la dist/wraith.exe

      # ===============================================================
      # Code signing — jsign + Azure Key Vault (EV cert)
      # ===============================================================
      - name: Install jsign
        run: |
          JSIGN_VERSION="7.0"
          curl -sSL -o /usr/local/bin/jsign.jar \
            "https://github.com/ebourg/jsign/releases/download/${JSIGN_VERSION}/jsign-${JSIGN_VERSION}.jar"

      - name: Get Azure Key Vault access token
        id: azure-token
        run: |
          TOKEN=$(curl -s -X POST \
            "https://login.microsoftonline.com/${{ secrets.AZURE_TENANT_ID }}/oauth2/v2.0/token" \
            -d "client_id=${{ secrets.AZURE_CLIENT_ID }}" \
            -d "client_secret=${{ secrets.AZURE_CLIENT_SECRET }}" \
            -d "scope=https://vault.azure.net/.default" \
            -d "grant_type=client_credentials" \
            | python3 -c "import sys,json; print(json.load(sys.stdin)['access_token'])")
          echo "::add-mask::${TOKEN}"
          echo "token=${TOKEN}" >> $GITHUB_OUTPUT

      - name: Sign all Windows binaries
        run: |
          echo "=== Signing all .exe and .dll files with EV certificate ==="
          for binary in dist/*.exe dist/*.dll; do
            [ -f "$binary" ] || continue
            echo "Signing: $binary"
            java -jar /usr/local/bin/jsign.jar \
              --storetype AZUREKEYVAULT \
              --keystore "${{ secrets.AZURE_KEY_VAULT_URL }}" \
              --storepass "${{ steps.azure-token.outputs.token }}" \
              --alias "${{ secrets.AZURE_CERT_NAME }}" \
              --tsaurl http://timestamp.digicert.com \
              --tsmode RFC3161 \
              "$binary"
            echo "Signed: $binary"
          done

      # ===============================================================
      # Version manifest
      # ===============================================================
      - name: Create version.json
        run: |
          VERSION="${{ steps.version.outputs.version }}"
          EXE_SHA=$(sha256sum dist/wraith.exe | awk '{print $1}')

          # Build DLL manifest
          DLL_ENTRIES=""
          for dll in dist/*.dll; do
            [ -f "$dll" ] || continue
            DLL_NAME=$(basename "$dll")
            DLL_SHA=$(sha256sum "$dll" | awk '{print $1}')
            DLL_ENTRIES="${DLL_ENTRIES}    \"${DLL_NAME}\": \"${DLL_SHA}\",
          "
          done

          cat > dist/version.json << EOF
          {
            "version": "${VERSION}",
            "filename": "wraith.exe",
            "sha256": "${EXE_SHA}",
            "platform": "windows",
            "architecture": "amd64",
            "released": "$(date -u +"%Y-%m-%dT%H:%M:%SZ")",
            "signed": true,
            "dlls": {
          ${DLL_ENTRIES}    "_note": "All DLLs are EV code-signed"
            }
          }
          EOF

          echo "=== version.json ==="
          cat dist/version.json

      # ===============================================================
      # NSIS Installer
      # ===============================================================
      - name: Build NSIS installer
        run: |
          VERSION="${{ steps.version.outputs.version }}"

          cat > /tmp/wraith-installer.nsi << 'NSIEOF'
          !include "MUI2.nsh"

          Name "Wraith"
          OutFile "wraith-setup.exe"
          InstallDir "$PROGRAMFILES64\Wraith"
          InstallDirRegKey HKLM "Software\Wraith" "InstallDir"
          RequestExecutionLevel admin

          !define MUI_ICON "${NSISDIR}\Contrib\Graphics\Icons\modern-install.ico"

          !insertmacro MUI_PAGE_DIRECTORY
          !insertmacro MUI_PAGE_INSTFILES
          !insertmacro MUI_LANGUAGE "English"

          Section "Install"
            SetOutPath "$INSTDIR"
            File "wraith.exe"
            File "*.dll"
            File "version.json"

            ; Start Menu shortcut
            CreateDirectory "$SMPROGRAMS\Wraith"
            CreateShortcut "$SMPROGRAMS\Wraith\Wraith.lnk" "$INSTDIR\wraith.exe"
            CreateShortcut "$DESKTOP\Wraith.lnk" "$INSTDIR\wraith.exe"

            ; Uninstaller
            WriteUninstaller "$INSTDIR\uninstall.exe"

            ; Registry
            WriteRegStr HKLM "Software\Wraith" "InstallDir" "$INSTDIR"
            WriteRegStr HKLM "Software\Wraith" "Version" "${VERSION}"
            WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Wraith" "DisplayName" "Wraith"
            WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Wraith" "UninstallString" "$INSTDIR\uninstall.exe"
            WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Wraith" "DisplayVersion" "${VERSION}"
            WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Wraith" "Publisher" "Vantz Stockwell"
          SectionEnd

          Section "Uninstall"
            Delete "$INSTDIR\wraith.exe"
            Delete "$INSTDIR\*.dll"
            Delete "$INSTDIR\version.json"
            Delete "$INSTDIR\uninstall.exe"
            RMDir "$INSTDIR"
            Delete "$SMPROGRAMS\Wraith\Wraith.lnk"
            RMDir "$SMPROGRAMS\Wraith"
            Delete "$DESKTOP\Wraith.lnk"
            DeleteRegKey HKLM "Software\Wraith"
            DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Wraith"
          SectionEnd
          NSIEOF

          # Replace VERSION placeholder
          sed -i "s/\${VERSION}/${VERSION}/g" /tmp/wraith-installer.nsi

          # Copy NSIS script into dist so File directives find the binaries
          cp /tmp/wraith-installer.nsi dist/wraith-installer.nsi
          cd dist
          makensis -DVERSION="${VERSION}" wraith-installer.nsi
          mv wraith-setup.exe ../wraith-${VERSION}-setup.exe
          rm wraith-installer.nsi
          cd ..

          echo "=== Installer built ==="
          ls -la wraith-${VERSION}-setup.exe

      - name: Sign installer
        run: |
          VERSION="${{ steps.version.outputs.version }}"
          echo "=== Signing installer ==="
          java -jar /usr/local/bin/jsign.jar \
            --storetype AZUREKEYVAULT \
            --keystore "${{ secrets.AZURE_KEY_VAULT_URL }}" \
            --storepass "${{ steps.azure-token.outputs.token }}" \
            --alias "${{ secrets.AZURE_CERT_NAME }}" \
            --tsaurl http://timestamp.digicert.com \
            --tsmode RFC3161 \
            wraith-${VERSION}-setup.exe
          echo "Installer signed."

          # Move to dist for upload
          mv wraith-${VERSION}-setup.exe dist/

      # ===============================================================
      # Upload to Gitea Package Registry
      # ===============================================================
      - name: Upload to Gitea packages
        run: |
          VERSION="${{ steps.version.outputs.version }}"
          GITEA_URL="https://git.command.vigilcyber.com"
          OWNER="vstockwell"
          PACKAGE="wraith"

          echo "=== Uploading Wraith ${VERSION} to Gitea packages ==="

          # Upload each file as a generic package
          for file in dist/*; do
            [ -f "$file" ] || continue
            FILENAME=$(basename "$file")
            echo "Uploading: ${FILENAME}"
            curl -s -X PUT \
              -H "Authorization: token ${{ secrets.GIT_TOKEN }}" \
              -H "Content-Type: application/octet-stream" \
              --data-binary @"$file" \
              "${GITEA_URL}/api/packages/${OWNER}/generic/${PACKAGE}/${VERSION}/${FILENAME}"
            echo " Done."
          done

          echo ""
          echo "=== Upload complete ==="
          echo "Package: ${GITEA_URL}/${OWNER}/-/packages/generic/${PACKAGE}/${VERSION}"
          echo ""
          echo "=== Contents ==="
          ls -la dist/
