name: Release Client

on:
  push:
    tags:
      - 'v*'
  workflow_dispatch:
    inputs:
      tag_name:
        description: '要发布的 tag，例如 v2.0.1'
        required: true
        type: string

permissions:
  contents: write

jobs:
  create-release:
    name: Create GitHub Release
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4
        with:
          fetch-depth: 0
          ref: ${{ inputs.tag_name || github.ref }}

      - name: Create release notes from commits
        env:
          TAG_NAME: ${{ inputs.tag_name || github.ref_name }}
        run: |
          PREVIOUS_TAG="$(git describe --tags --abbrev=0 "${TAG_NAME}^" 2>/dev/null || true)"
          if [ -n "$PREVIOUS_TAG" ]; then
            RANGE="$PREVIOUS_TAG..$TAG_NAME"
            COMPARE_URL="https://github.com/${{ github.repository }}/compare/${PREVIOUS_TAG}...${TAG_NAME}"
          else
            RANGE="$TAG_NAME"
            COMPARE_URL="https://github.com/${{ github.repository }}/commits/${TAG_NAME}"
          fi

          {
            echo "## 更新内容"
            echo
            git log "$RANGE" --no-merges --pretty=format:'- %s (%h)' || true
            echo
            echo
            echo "**Full Changelog**: $COMPARE_URL"
          } > release_notes.md

      - name: Create or update GitHub Release
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          TAG_NAME: ${{ inputs.tag_name || github.ref_name }}
        run: |
          if gh release view "$TAG_NAME" >/dev/null 2>&1; then
            gh release edit "$TAG_NAME" --title "$TAG_NAME" --notes-file release_notes.md
          else
            gh release create "$TAG_NAME" --title "$TAG_NAME" --notes-file release_notes.md
          fi

  build:
    name: Build ${{ matrix.os }}
    needs: create-release
    runs-on: ${{ matrix.os }}
    strategy:
      fail-fast: false
      matrix:
        os: [windows-latest, macos-latest]

    defaults:
      run:
        working-directory: client
        shell: bash

    steps:
      - name: Checkout
        uses: actions/checkout@v4
        with:
          ref: ${{ inputs.tag_name || github.ref }}

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: 22
          cache: npm
          cache-dependency-path: client/package-lock.json

      - name: Cache Electron build downloads
        if: runner.os == 'macOS'
        uses: actions/cache@v4
        with:
          path: |
            ~/Library/Caches/electron
            ~/Library/Caches/electron-builder
          key: ${{ runner.os }}-electron-${{ hashFiles('client/package-lock.json') }}
          restore-keys: |
            ${{ runner.os }}-electron-

      - name: Install dependencies
        run: npm ci

      - name: Install build type dependencies
        run: npm install --no-save @types/plist

      - name: Sync package version from tag
        env:
          TAG_NAME: ${{ inputs.tag_name || github.ref_name }}
        run: |
          VERSION="${TAG_NAME#v}"
          npm version "$VERSION" --no-git-tag-version --allow-same-version

      - name: Generate macOS icon
        if: runner.os == 'macOS'
        run: |
          rm -rf assets/icon.iconset
          mkdir -p assets/icon.iconset
          sips -z 16 16 assets/icon_256.png --out assets/icon.iconset/icon_16x16.png
          sips -z 32 32 assets/icon_256.png --out assets/icon.iconset/icon_16x16@2x.png
          sips -z 32 32 assets/icon_256.png --out assets/icon.iconset/icon_32x32.png
          sips -z 64 64 assets/icon_256.png --out assets/icon.iconset/icon_32x32@2x.png
          sips -z 128 128 assets/icon_256.png --out assets/icon.iconset/icon_128x128.png
          sips -z 256 256 assets/icon_256.png --out assets/icon.iconset/icon_128x128@2x.png
          sips -z 256 256 assets/icon_256.png --out assets/icon.iconset/icon_256x256.png
          sips -z 512 512 assets/icon_256.png --out assets/icon.iconset/icon_256x256@2x.png
          sips -z 512 512 assets/icon_256.png --out assets/icon.iconset/icon_512x512.png
          sips -z 1024 1024 assets/icon_256.png --out assets/icon.iconset/icon_512x512@2x.png
          iconutil -c icns assets/icon.iconset -o assets/icon.icns

      - name: Build renderer
        run: npm run build

      - name: Preload Electron macOS runtimes
        if: runner.os == 'macOS'
        run: |
          export ELECTRON_CACHE="$HOME/Library/Caches/electron"
          ELECTRON_VERSION="$(node -p "require('./node_modules/electron/package.json').version")"
          cache_dir="$ELECTRON_CACHE"
          mkdir -p "$cache_dir"

          for arch in x64 arm64; do
            file="$cache_dir/electron-v${ELECTRON_VERSION}-darwin-${arch}.zip"
            if [ -f "$file" ] && unzip -tq "$file" >/dev/null 2>&1; then
              echo "Electron ${ELECTRON_VERSION} darwin-${arch} already cached."
              continue
            fi

            tmp="${file}.tmp"
            rm -f "$file" "$tmp"
            curl --fail --location --retry 5 --retry-all-errors --retry-delay 10 \
              "https://github.com/electron/electron/releases/download/v${ELECTRON_VERSION}/electron-v${ELECTRON_VERSION}-darwin-${arch}.zip" \
              --output "$tmp"
            unzip -tq "$tmp" >/dev/null
            mv "$tmp" "$file"
          done

      - name: Build Windows NSIS and ZIP artifacts
        if: runner.os == 'Windows'
        run: npx electron-builder --win nsis zip --publish never

      - name: Build Windows MSI artifact
        if: runner.os == 'Windows'
        run: npx electron-builder --win msi --publish never -c.productName=OpenBidKit_Yibiao -c.extraMetadata.author=mark -c.copyright="Copyright (c) 2026 mark"

      - name: Build and publish macOS artifacts
        if: runner.os == 'macOS'
        run: |
          export ELECTRON_CACHE="$HOME/Library/Caches/electron"
          export ELECTRON_BUILDER_CACHE="$HOME/Library/Caches/electron-builder"

          for attempt in 1 2 3; do
            echo "Build macOS artifacts, attempt ${attempt}/3."
            if npx electron-builder --mac --publish never; then
              exit 0
            fi

            if [ "$attempt" -eq 3 ]; then
              exit 1
            fi

            sleep $((attempt * 20))
          done

      - name: Package macOS DMG with instructions
        if: runner.os == 'macOS'
        run: |
          shopt -s nullglob
          for dmg in release/*.dmg; do
            base="$(basename "$dmg" .dmg)"
            package_dir="release/${base}-package"
            rm -rf "$package_dir"
            mkdir -p "$package_dir"
            cp "$dmg" "$package_dir/"
            cat > "$package_dir/macOS使用说明.txt" <<'EOF'
          macOS 使用说明

          如果双击 App 提示“已损坏，无法打开”或“无法验证开发者”，这是因为本软件未进行 Apple 官方签名和公证，并不代表文件真的损坏。

          使用步骤：

          1. 双击 dmg 文件。
          2. 将“易标投标工具箱.app”拖到“应用程序”文件夹。
          3. 打开“终端”App。
          4. 执行下面命令：

          xattr -dr com.apple.quarantine /Applications/易标投标工具箱.app

          5. 再从“应用程序”中打开“易标投标工具箱”。

          如果仍无法打开，请右键点击 App，选择“打开”，再确认打开。
          EOF
            (cd release && ditto -c -k --sequesterRsrc --keepParent "$(basename "$package_dir")" "${base}-package.zip")
            rm -rf "$package_dir"
            rm -f "$dmg"
          done

      - name: Upload release assets
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          TAG_NAME: ${{ inputs.tag_name || github.ref_name }}
        run: |
          shopt -s nullglob
          files=(release/*.exe release/*.msi release/*.zip release/*.blockmap release/latest*.yml)
          if [ ${#files[@]} -eq 0 ]; then
            echo "No release assets found."
            exit 1
          fi
          gh release upload "$TAG_NAME" "${files[@]}" --clobber
