name: Sync Release to GitCode

on:
  release:
    types: [published]
  workflow_dispatch:
    inputs:
      tag:
        description: 'Release tag (e.g. v1.0.0)'
        required: true
      clean:
        description: 'Clean node_modules before build'
        type: boolean
        default: false

permissions:
  contents: read

jobs:
  build-and-sync-to-gitcode:
    runs-on: [self-hosted, windows-signing]
    steps:
      - name: Get tag name
        id: get-tag
        shell: bash
        run: |
          if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
            echo "tag=${{ github.event.inputs.tag }}" >> $GITHUB_OUTPUT
          else
            echo "tag=${{ github.event.release.tag_name }}" >> $GITHUB_OUTPUT
          fi

      - name: Check out Git repository
        uses: actions/checkout@v6
        with:
          fetch-depth: 0
          ref: ${{ steps.get-tag.outputs.tag }}

      - name: Set package.json version
        shell: bash
        run: |
          TAG="${{ steps.get-tag.outputs.tag }}"
          VERSION="${TAG#v}"
          npm version "$VERSION" --no-git-tag-version --allow-same-version

      - name: Install Node.js
        uses: actions/setup-node@v6
        with:
          node-version: 22

      - name: Install corepack
        shell: bash
        run: corepack enable && corepack prepare yarn@4.9.1 --activate

      - name: Clean node_modules
        if: ${{ github.event.inputs.clean == 'true' }}
        shell: bash
        run: rm -rf node_modules

      - name: Install Dependencies
        shell: bash
        run: yarn install

      - name: Build Windows with code signing
        shell: bash
        run: yarn build:win
        env:
          WIN_SIGN: true
          CHERRY_CERT_PATH: ${{ secrets.CHERRY_CERT_PATH }}
          CHERRY_CERT_KEY: ${{ secrets.CHERRY_CERT_KEY }}
          CHERRY_CERT_CSP: ${{ secrets.CHERRY_CERT_CSP }}
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          NODE_OPTIONS: --max-old-space-size=8192
          MAIN_VITE_CHERRYAI_CLIENT_SECRET: ${{ secrets.MAIN_VITE_CHERRYAI_CLIENT_SECRET }}
          MAIN_VITE_MINERU_API_KEY: ${{ secrets.MAIN_VITE_MINERU_API_KEY }}
          RENDERER_VITE_AIHUBMIX_SECRET: ${{ secrets.RENDERER_VITE_AIHUBMIX_SECRET }}
          RENDERER_VITE_PPIO_APP_SECRET: ${{ secrets.RENDERER_VITE_PPIO_APP_SECRET }}

      - name: List built Windows artifacts
        shell: bash
        run: |
          echo "Built Windows artifacts:"
          ls -la dist/*.exe dist/*.blockmap dist/latest*.yml

      - name: Download GitHub release assets
        shell: bash
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          TAG_NAME: ${{ steps.get-tag.outputs.tag }}
        run: |
          echo "Downloading release assets for $TAG_NAME..."
          mkdir -p release-assets
          cd release-assets

          # Download all assets from the release
          gh release download "$TAG_NAME" \
            --repo "${{ github.repository }}" \
            --pattern "*" \
            --skip-existing

          echo "Downloaded GitHub release assets:"
          ls -la

      - name: Replace Windows files with signed versions
        shell: bash
        run: |
          echo "Replacing Windows files with signed versions..."

          # Verify signed files exist first
          if ! ls dist/*.exe 1>/dev/null 2>&1; then
            echo "ERROR: No signed .exe files found in dist/"
            exit 1
          fi

          # Remove unsigned Windows files from downloaded assets
          # *.exe, *.exe.blockmap, latest.yml (Windows only)
          rm -f release-assets/*.exe release-assets/*.exe.blockmap release-assets/latest.yml 2>/dev/null || true

          # Copy signed Windows files with error checking
          cp dist/*.exe release-assets/ || { echo "ERROR: Failed to copy .exe files"; exit 1; }
          cp dist/*.exe.blockmap release-assets/ || { echo "ERROR: Failed to copy .blockmap files"; exit 1; }
          cp dist/latest.yml release-assets/ || { echo "ERROR: Failed to copy latest.yml"; exit 1; }

          echo "Final release assets:"
          ls -la release-assets/

      - name: Get release info
        id: release-info
        shell: bash
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          TAG_NAME: ${{ steps.get-tag.outputs.tag }}
          LANG: C.UTF-8
          LC_ALL: C.UTF-8
        run: |
          # Always use gh cli to avoid special character issues
          RELEASE_NAME=$(gh release view "$TAG_NAME" --repo "${{ github.repository }}" --json name -q '.name')
          # Use delimiter to safely handle special characters in release name
          {
            echo 'name<<EOF'
            echo "$RELEASE_NAME"
            echo 'EOF'
          } >> $GITHUB_OUTPUT
          # Extract releaseNotes from electron-builder.yml (from releaseNotes: | to end of file, remove 4-space indent)
          sed -n '/releaseNotes: |/,$ { /releaseNotes: |/d; s/^    //; p }' electron-builder.yml > release_body.txt

      - name: Create GitCode release and upload files
        shell: bash
        env:
          GITCODE_TOKEN: ${{ secrets.GITCODE_TOKEN }}
          GITCODE_OWNER: ${{ vars.GITCODE_OWNER }}
          GITCODE_REPO: ${{ vars.GITCODE_REPO }}
          GITCODE_API_URL: ${{ vars.GITCODE_API_URL }}
          TAG_NAME: ${{ steps.get-tag.outputs.tag }}
          RELEASE_NAME: ${{ steps.release-info.outputs.name }}
          LANG: C.UTF-8
          LC_ALL: C.UTF-8
        run: |
          # Validate required environment variables
          if [ -z "$GITCODE_TOKEN" ]; then
            echo "ERROR: GITCODE_TOKEN is not set"
            exit 1
          fi
          if [ -z "$GITCODE_OWNER" ]; then
            echo "ERROR: GITCODE_OWNER is not set"
            exit 1
          fi
          if [ -z "$GITCODE_REPO" ]; then
            echo "ERROR: GITCODE_REPO is not set"
            exit 1
          fi

          API_URL="${GITCODE_API_URL:-https://api.gitcode.com/api/v5}"

          echo "Creating GitCode release..."
          echo "Tag: $TAG_NAME"
          echo "Repo: $GITCODE_OWNER/$GITCODE_REPO"

          # Step 1: Create release
          # Use --rawfile to read body directly from file, avoiding shell variable encoding issues
          jq -n \
            --arg tag "$TAG_NAME" \
            --arg name "$RELEASE_NAME" \
            --rawfile body release_body.txt \
            '{
              tag_name: $tag,
              name: $name,
              body: $body,
              target_commitish: "main"
            }' > /tmp/release_payload.json

          RELEASE_RESPONSE=$(curl -s -w "\n%{http_code}" -X POST \
            --connect-timeout 30 --max-time 60 \
            "${API_URL}/repos/${GITCODE_OWNER}/${GITCODE_REPO}/releases" \
            -H "Content-Type: application/json; charset=utf-8" \
            -H "Authorization: Bearer ${GITCODE_TOKEN}" \
            --data-binary "@/tmp/release_payload.json")

          HTTP_CODE=$(echo "$RELEASE_RESPONSE" | tail -n1)
          RESPONSE_BODY=$(echo "$RELEASE_RESPONSE" | sed '$d')

          if [ "$HTTP_CODE" -ge 200 ] && [ "$HTTP_CODE" -lt 300 ]; then
            echo "Release created successfully"
          else
            echo "Warning: Release creation returned HTTP $HTTP_CODE"
            echo "$RESPONSE_BODY"
            exit 1
          fi

          # Step 2: Upload files to release
          echo "Uploading files to GitCode release..."

          # Function to upload a single file with retry
          upload_file() {
            local file="$1"
            local filename=$(basename "$file")
            local max_retries=3
            local retry=0
            local curl_status=0

            echo "Uploading: $filename"

            # URL encode the filename
            encoded_filename=$(printf '%s' "$filename" | jq -sRr @uri)

            while [ $retry -lt $max_retries ]; do
              # Get upload URL
              curl_status=0
              UPLOAD_INFO=$(curl -s --connect-timeout 30 --max-time 60 \
                -H "Authorization: Bearer ${GITCODE_TOKEN}" \
                "${API_URL}/repos/${GITCODE_OWNER}/${GITCODE_REPO}/releases/${TAG_NAME}/upload_url?file_name=${encoded_filename}") || curl_status=$?

              if [ $curl_status -eq 0 ]; then
                UPLOAD_URL=$(echo "$UPLOAD_INFO" | jq -r '.url // empty')

                if [ -n "$UPLOAD_URL" ]; then
                  # Write headers to temp file to avoid shell escaping issues
                  echo "$UPLOAD_INFO" | jq -r '.headers | to_entries[] | "header = \"" + .key + ": " + .value + "\""' > /tmp/upload_headers.txt

                  # Upload file using PUT with headers from file
                  curl_status=0
                  UPLOAD_RESPONSE=$(curl -s -w "\n%{http_code}" -X PUT \
                    -K /tmp/upload_headers.txt \
                    --data-binary "@${file}" \
                    "$UPLOAD_URL") || curl_status=$?

                  if [ $curl_status -eq 0 ]; then
                    HTTP_CODE=$(echo "$UPLOAD_RESPONSE" | tail -n1)
                    RESPONSE_BODY=$(echo "$UPLOAD_RESPONSE" | sed '$d')

                    if [ "$HTTP_CODE" -ge 200 ] && [ "$HTTP_CODE" -lt 300 ]; then
                      echo "  Uploaded: $filename"
                      return 0
                    else
                      echo "  Failed (HTTP $HTTP_CODE), retry $((retry + 1))/$max_retries"
                      echo "  Response: $RESPONSE_BODY"
                    fi
                  else
                    echo "  Upload request failed (curl exit $curl_status), retry $((retry + 1))/$max_retries"
                  fi
                else
                  echo "  Failed to get upload URL, retry $((retry + 1))/$max_retries"
                  echo "  Response: $UPLOAD_INFO"
                fi
              else
                echo "  Failed to get upload URL (curl exit $curl_status), retry $((retry + 1))/$max_retries"
                echo "  Response: $UPLOAD_INFO"
              fi

              retry=$((retry + 1))
              [ $retry -lt $max_retries ] && sleep 3
            done

            echo "  Failed: $filename after $max_retries retries"
            exit 1
          }

          # Upload non-yml/json files first
          for file in release-assets/*; do
            if [ -f "$file" ]; then
              filename=$(basename "$file")
              if [[ ! "$filename" =~ \.(yml|yaml|json)$ ]]; then
                upload_file "$file"
              fi
            fi
          done

          # Upload yml/json files last
          for file in release-assets/*; do
            if [ -f "$file" ]; then
              filename=$(basename "$file")
              if [[ "$filename" =~ \.(yml|yaml|json)$ ]]; then
                upload_file "$file"
              fi
            fi
          done

          echo "GitCode release sync completed!"

      - name: Cleanup temp files
        if: always()
        shell: bash
        run: |
          rm -f /tmp/release_payload.json /tmp/upload_headers.txt release_body.txt
          rm -rf release-assets/
