name: Build Lotus Chat Desktop

on:
  push:
    branches: [main]
  workflow_dispatch:

env:
  GITEA_URL: https://code.lotusguild.org
  REPO: LotusGuild/cinny-desktop

jobs:
  prepare:
    runs-on: ubuntu-latest
    outputs:
      version: ${{ steps.ver.outputs.version }}
      release_id: ${{ steps.release.outputs.release_id }}
    steps:
      - name: Compute version
        id: ver
        run: echo "version=4.12.${{ github.run_number }}" >> $GITHUB_OUTPUT

      - name: Create or update release
        id: release
        env:
          TOKEN: ${{ secrets.RELEASE_TOKEN }}
        run: |
          VERSION="4.12.${{ github.run_number }}"
          EXISTING=$(curl -sf "$GITEA_URL/api/v1/repos/$REPO/releases/tags/latest" \
            -H "Authorization: token $TOKEN" 2>/dev/null || true)
          RELEASE_ID=$(echo "$EXISTING" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('id',''))" 2>/dev/null || true)
          if [ -n "$RELEASE_ID" ] && [ "$RELEASE_ID" != "None" ] && [ "$RELEASE_ID" != "" ]; then
            curl -sf -X PATCH "$GITEA_URL/api/v1/repos/$REPO/releases/$RELEASE_ID" \
              -H "Authorization: token $TOKEN" \
              -H "Content-Type: application/json" \
              -d "{\"name\":\"Lotus Chat $VERSION\",\"body\":\"Built from ${{ github.sha }}\"}" > /dev/null
          else
            RELEASE_ID=$(curl -sf -X POST "$GITEA_URL/api/v1/repos/$REPO/releases" \
              -H "Authorization: token $TOKEN" \
              -H "Content-Type: application/json" \
              -d "{\"tag_name\":\"latest\",\"name\":\"Lotus Chat $VERSION\",\"prerelease\":true,\"body\":\"Built from ${{ github.sha }}\"}" \
              | python3 -c "import sys,json; print(json.load(sys.stdin)['id'])")
          fi
          echo "release_id=$RELEASE_ID" >> $GITHUB_OUTPUT

  build-windows:
    needs: prepare
    runs-on: windows
    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-node@v4
        with:
          node-version-file: .node-version

      - name: Checkout submodules (shallow)
        shell: powershell
        run: git submodule update --init --depth=1

      - name: Patch version
        shell: powershell
        run: |
          $ver = '${{ needs.prepare.outputs.version }}'
          node -e "const fs=require('fs');const d=JSON.parse(fs.readFileSync('src-tauri/tauri.conf.json','utf8'));d.version='$ver';fs.writeFileSync('src-tauri/tauri.conf.json',JSON.stringify(d,null,2),'utf8');"

      - uses: Swatinem/rust-cache@v2
        with:
          workspaces: src-tauri

      - name: Install frontend deps
        shell: powershell
        run: cd cinny; npm ci

      - name: Install Tauri deps
        shell: powershell
        run: npm ci

      - name: Build
        shell: powershell
        env:
          TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
          TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ''
          NODE_OPTIONS: '--max_old_space_size=4096'
          # Sparse registry avoids a full git clone of the crates.io index —
          # eliminates the curl SSL handshake failures seen on Windows runners.
          CARGO_REGISTRIES_CRATES_IO_PROTOCOL: sparse
          # Disable HTTP/2 multiplexing — ALPN negotiation can reset on Windows Schannel.
          CARGO_HTTP_MULTIPLEXING: 'false'
          # Retry transient network errors before failing.
          CARGO_NET_RETRY: '5'
        run: |
          # USERPROFILE is set by Windows directly; more reliable than C:\Users\$USERNAME
          $env:PATH = "$env:USERPROFILE\.cargo\bin;$env:PATH"
          # Also add the actual toolchain bin to bypass rustup shim execution issues
          $toolchain = Get-ChildItem "$env:USERPROFILE\.rustup\toolchains" -Directory -ErrorAction SilentlyContinue |
                       Where-Object { $_.Name -match 'stable' } | Select-Object -First 1
          if ($toolchain) { $env:PATH = "$($toolchain.FullName)\bin;$env:PATH" }
          Write-Host "cargo: $((Get-Command cargo -ErrorAction SilentlyContinue).Source)"
          cargo --version
          npm run tauri -- build --bundles nsis

      - name: Upload to release
        shell: powershell
        env:
          TOKEN: ${{ secrets.RELEASE_TOKEN }}
          RELEASE_ID: ${{ needs.prepare.outputs.release_id }}
          VERSION: ${{ needs.prepare.outputs.version }}
        run: |
          $releaseId = $env:RELEASE_ID
          $VERSION = $env:VERSION
          Write-Host "Version: $VERSION  Release: $releaseId"

          $nsis = "src-tauri\target\release\bundle\nsis"
          $files = @(
            "$nsis\Lotus Chat_${VERSION}_x64-setup.exe",
            "$nsis\Lotus Chat_${VERSION}_x64-setup.nsis.zip",
            "$nsis\Lotus Chat_${VERSION}_x64-setup.nsis.zip.sig"
          )
          $names = @("LotusChat-x86_64-setup.exe", "LotusChat-x86_64-setup.nsis.zip", "LotusChat-x86_64-setup.nsis.zip.sig")
          for ($i = 0; $i -lt $files.Length; $i++) {
            $existing = (Invoke-RestMethod -Uri "$env:GITEA_URL/api/v1/repos/$env:REPO/releases/$releaseId/assets" `
              -Headers @{ Authorization = "token $env:TOKEN" }) | Where-Object { $_.name -eq $names[$i] }
            if ($existing) {
              Invoke-RestMethod -Uri "$env:GITEA_URL/api/v1/repos/$env:REPO/releases/$releaseId/assets/$($existing.id)" `
                -Method Delete -Headers @{ Authorization = "token $env:TOKEN" }
            }
            $bytes = [System.IO.File]::ReadAllBytes($files[$i])
            Invoke-RestMethod -Uri "$env:GITEA_URL/api/v1/repos/$env:REPO/releases/$releaseId/assets?name=$($names[$i])" `
              -Method Post `
              -Headers @{ Authorization = "token $env:TOKEN"; "Content-Type" = "application/octet-stream" } `
              -Body $bytes
          }

  build-linux:
    needs: prepare
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          submodules: true

      - uses: actions/setup-node@v4
        with:
          node-version-file: .node-version

      - name: Install system deps
        run: |
          apt-get update
          apt-get install -y \
            curl wget file gcc imagemagick \
            libwebkit2gtk-4.1-dev \
            libssl-dev \
            libxdo-dev \
            libayatana-appindicator3-dev \
            librsvg2-dev \
            patchelf \
            xdg-utils \
            squashfs-tools

      - name: Ensure icons are RGBA PNG
        run: |
          for f in src-tauri/icons/*.png; do
            info=$(identify -verbose "$f" 2>/dev/null | grep "Type:" | head -1)
            echo "$f: $info"
            convert "$f" -type TrueColorAlpha PNG32:"$f"
          done

      - name: Set up Rust toolchain
        run: |
          source "$HOME/.cargo/env" 2>/dev/null || true
          if command -v cargo >/dev/null 2>&1; then
            echo "Using existing Rust: $(cargo --version)"
          else
            curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --no-modify-path --default-toolchain stable --profile minimal
            source "$HOME/.cargo/env"
          fi
          echo "$HOME/.cargo/bin" >> $GITHUB_PATH

      - uses: Swatinem/rust-cache@v2
        with:
          workspaces: src-tauri

      - name: Patch version
        run: python3 -c "import json; d=json.load(open('src-tauri/tauri.conf.json')); d['version']='${{ needs.prepare.outputs.version }}'; open('src-tauri/tauri.conf.json','w').write(json.dumps(d,indent=2))"

      - name: Install frontend deps
        run: cd cinny && npm ci

      - name: Install Tauri deps
        run: npm ci

      - name: Stage AppRun and linuxdeploy for AppImage bundler
        run: |
          set -e
          mkdir -p ~/.cache/tauri
          cp tools/AppRun-x86_64 ~/.cache/tauri/AppRun-x86_64
          chmod +x ~/.cache/tauri/AppRun-x86_64

          wget -q \
            "https://github.com/tauri-apps/binary-releases/releases/download/linuxdeploy/linuxdeploy-x86_64.AppImage" \
            -O /tmp/linuxdeploy.AppImage
          chmod +x /tmp/linuxdeploy.AppImage

          rm -rf /root/linuxdeploy-root
          (cd /root && /tmp/linuxdeploy.AppImage --appimage-extract)
          mv /root/squashfs-root /root/linuxdeploy-root
          echo "Extracted linuxdeploy:"
          ls /root/linuxdeploy-root/
          ls /root/linuxdeploy-root/usr/bin/ 2>/dev/null || echo "no usr/bin"

          # Pre-stage plugin scripts next to linuxdeploy so it finds them via /proc/self/exe lookup
          wget -q "https://raw.githubusercontent.com/tauri-apps/linuxdeploy-plugin-gtk/master/linuxdeploy-plugin-gtk.sh" \
            -O /root/linuxdeploy-root/usr/bin/linuxdeploy-plugin-gtk.sh
          wget -q "https://raw.githubusercontent.com/tauri-apps/linuxdeploy-plugin-gstreamer/master/linuxdeploy-plugin-gstreamer.sh" \
            -O /root/linuxdeploy-root/usr/bin/linuxdeploy-plugin-gstreamer.sh
          chmod +x /root/linuxdeploy-root/usr/bin/linuxdeploy-plugin-gtk.sh \
                   /root/linuxdeploy-root/usr/bin/linuxdeploy-plugin-gstreamer.sh

          gcc -o ~/.cache/tauri/linuxdeploy-x86_64.AppImage tools/ld_wrapper.c
          chmod +x ~/.cache/tauri/linuxdeploy-x86_64.AppImage

      - name: Build
        env:
          TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
          TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ''
          NODE_OPTIONS: '--max_old_space_size=4096'
          RUST_LOG: tauri_bundler=debug
        run: npm run tauri -- build --bundles appimage,deb

      - name: Show linuxdeploy wrapper log
        if: always()
        run: cat /tmp/ld-wrapper.log 2>/dev/null || echo "no wrapper log found"

      - name: Upload to release
        env:
          TOKEN: ${{ secrets.RELEASE_TOKEN }}
          RELEASE_ID: ${{ needs.prepare.outputs.release_id }}
          VERSION: ${{ needs.prepare.outputs.version }}
        run: |
          APPIMAGE_DIR="src-tauri/target/release/bundle/appimage"
          DEB_DIR="src-tauri/target/release/bundle/deb"

          upload() {
            local name="$1" path="$2"
            local existing_id
            existing_id=$(curl -sf "$GITEA_URL/api/v1/repos/$REPO/releases/$RELEASE_ID/assets" \
              -H "Authorization: token $TOKEN" \
              | python3 -c "import sys,json; assets=json.load(sys.stdin); print(next((str(a['id']) for a in assets if a['name']=='$name'), ''))" 2>/dev/null || true)
            if [ -n "$existing_id" ]; then
              curl -sf -X DELETE "$GITEA_URL/api/v1/repos/$REPO/releases/$RELEASE_ID/assets/$existing_id" \
                -H "Authorization: token $TOKEN" || true
            fi
            echo "Uploading $name"
            curl -sf -X POST \
              "$GITEA_URL/api/v1/repos/$REPO/releases/$RELEASE_ID/assets?name=$name" \
              -H "Authorization: token $TOKEN" \
              -H "Content-Type: application/octet-stream" \
              --data-binary @"$path"
          }

          upload "LotusChat-x86_64.AppImage"            "$APPIMAGE_DIR/Lotus Chat_${VERSION}_amd64.AppImage"
          upload "LotusChat-x86_64.AppImage.tar.gz"     "$APPIMAGE_DIR/Lotus Chat_${VERSION}_amd64.AppImage.tar.gz"
          upload "LotusChat-x86_64.AppImage.tar.gz.sig" "$APPIMAGE_DIR/Lotus Chat_${VERSION}_amd64.AppImage.tar.gz.sig"
          upload "LotusChat-x86_64.deb"                 "$DEB_DIR/Lotus Chat_${VERSION}_amd64.deb"

  update-manifest:
    needs: [prepare, build-windows, build-linux]
    runs-on: ubuntu-latest
    steps:
      - name: Generate and upload release.json
        env:
          TOKEN: ${{ secrets.RELEASE_TOKEN }}
          RELEASE_ID: ${{ needs.prepare.outputs.release_id }}
          VERSION: ${{ needs.prepare.outputs.version }}
        run: |
          BASE="$GITEA_URL/LotusGuild/cinny-desktop/releases/download/latest"

          WIN_SIG=$(curl -sf "$BASE/LotusChat-x86_64-setup.nsis.zip.sig")
          LIN_SIG=$(curl -sf "$BASE/LotusChat-x86_64.AppImage.tar.gz.sig")
          DATE=$(date -u +%Y-%m-%dT%H:%M:%SZ)

          python3 -c "import json,sys; v,d,wu,ws,lu,ls=sys.argv[1:]; print(json.dumps({'version':v,'notes':'Latest Lotus Chat release','pub_date':d,'platforms':{'windows-x86_64':{'url':wu,'signature':ws},'linux-x86_64':{'url':lu,'signature':ls}}},indent=2))" \
            "$VERSION" "$DATE" \
            "$BASE/LotusChat-x86_64-setup.nsis.zip" "$WIN_SIG" \
            "$BASE/LotusChat-x86_64.AppImage.tar.gz" "$LIN_SIG" \
            > release.json

          cat release.json

          OLD=$(curl -sf "$GITEA_URL/api/v1/repos/$REPO/releases/$RELEASE_ID/assets" \
            -H "Authorization: token $TOKEN" \
            | python3 -c "import sys,json; print(next((str(a['id']) for a in json.load(sys.stdin) if a['name']=='release.json'), ''))" 2>/dev/null || true)
          [ -n "$OLD" ] && curl -sf -X DELETE \
            "$GITEA_URL/api/v1/repos/$REPO/releases/$RELEASE_ID/assets/$OLD" \
            -H "Authorization: token $TOKEN" || true

          curl -sf -X POST \
            "$GITEA_URL/api/v1/repos/$REPO/releases/$RELEASE_ID/assets?name=release.json" \
            -H "Authorization: token $TOKEN" \
            -H "Content-Type: application/json" \
            --data-binary @release.json
