name: Desktop Release

on:
  push:
    tags:
      - 'v*.*.*'
  workflow_dispatch:
    inputs:
      release_tag:
        description: 'Existing release tag in semver format, e.g. v3.2.12'
        required: true
        type: string

concurrency:
  group: desktop-release-${{ github.ref }}
  cancel-in-progress: true

jobs:
  build-windows:
    runs-on: windows-latest
    permissions:
      contents: read
    env:
      RELEASE_TAG: ${{ github.event_name == 'workflow_dispatch' && inputs.release_tag || github.ref_name }}
    steps:
      - uses: actions/checkout@v5
        with:
          fetch-depth: 0

      - name: Resolve release ref
        shell: bash
        run: |
          if [[ "${GITHUB_EVENT_NAME}" == "workflow_dispatch" ]]; then
            [[ "${RELEASE_TAG}" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]
            git fetch --tags --force
            git rev-parse "${RELEASE_TAG}" >/dev/null
            git checkout "${RELEASE_TAG}"
          fi

      - uses: actions/setup-python@v6
        with:
          python-version: '3.12'
          cache: 'pip'

      - uses: actions/setup-node@v6
        with:
          node-version: '20'
          cache: 'npm'
          cache-dependency-path: |
            apps/dsa-web/package-lock.json
            apps/dsa-desktop/package-lock.json

      - name: Sync desktop app version from release tag
        shell: bash
        run: |
          set -euo pipefail
          [[ "${RELEASE_TAG}" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]
          export VERSION="${RELEASE_TAG#v}"
          cd apps/dsa-desktop
          npm version "${VERSION}" --no-git-tag-version --allow-same-version
          node -e "const pkg = require('./package.json'); if (pkg.version !== process.env.VERSION) throw new Error('package version mismatch: ' + pkg.version); console.log('desktop version:', pkg.version);"

      - name: Build desktop package (Windows)
        shell: pwsh
        env:
          DSA_SKIP_DEVMODE_CHECK: 'true'
        run: |
          powershell -ExecutionPolicy Bypass -File scripts/build-all.ps1

      - name: Prepare release artifact (Windows)
        shell: pwsh
        run: |
          $installerExe = Get-ChildItem -Path 'apps/dsa-desktop/dist' -Filter '*.exe' `
            | Where-Object { $_.Name -match 'Setup' } `
            | Sort-Object LastWriteTime -Descending `
            | Select-Object -First 1
          if (-not $installerExe) {
            throw 'No NSIS setup .exe found under apps/dsa-desktop/dist.'
          }
          $winUnpacked = 'apps/dsa-desktop/dist/win-unpacked'
          if (-not (Test-Path $winUnpacked)) {
            throw 'No win-unpacked directory found under apps/dsa-desktop/dist.'
          }
          New-Item -ItemType Directory -Path 'dist/release-assets' -Force | Out-Null
          $exeTarget = "dist/release-assets/daily-stock-analysis-windows-installer-$env:RELEASE_TAG.exe"
          $zipTarget = "dist/release-assets/daily-stock-analysis-windows-noinstall-$env:RELEASE_TAG.zip"
          Copy-Item -Path $installerExe.FullName -Destination $exeTarget -Force
          Copy-Item -Path $installerExe.FullName -Destination "dist/release-assets/$($installerExe.Name)" -Force
          Compress-Archive -Path $winUnpacked -DestinationPath $zipTarget -CompressionLevel Optimal -Force
          Get-ChildItem -Path 'apps/dsa-desktop/dist' -File |
            Where-Object { $_.Name -eq 'latest.yml' -or $_.Name -like '*.blockmap' } |
            ForEach-Object {
              Copy-Item -Path $_.FullName -Destination "dist/release-assets/$($_.Name)" -Force
            }
          if (-not (Test-Path 'dist/release-assets/latest.yml')) {
            throw 'No latest.yml found under dist/release-assets.'
          }
          $expectedVersion = $env:RELEASE_TAG.TrimStart('v')
          $versionPattern = '^version:\s*[''"]?' + [regex]::Escape($expectedVersion) + '[''"]?\s*$'
          if (-not (Select-String -Path 'dist/release-assets/latest.yml' -Pattern $versionPattern)) {
            throw "latest.yml version does not match release tag $env:RELEASE_TAG."
          }
          if (-not (Get-ChildItem -Path 'dist/release-assets' -Filter '*.blockmap' -File)) {
            throw 'No blockmap files found under dist/release-assets.'
          }
          Get-ChildItem -Path 'dist/release-assets' -File | Sort-Object Name | Format-Table Name, Length

      - uses: actions/upload-artifact@v6
        with:
          name: desktop-windows-${{ env.RELEASE_TAG }}
          path: dist/release-assets/*
          if-no-files-found: error

  build-macos:
    strategy:
      fail-fast: false
      matrix:
        include:
          - arch: x64
            runner: macos-15-intel
          - arch: arm64
            runner: macos-15
    runs-on: ${{ matrix.runner }}
    permissions:
      contents: read
    env:
      RELEASE_TAG: ${{ github.event_name == 'workflow_dispatch' && inputs.release_tag || github.ref_name }}
    steps:
      - uses: actions/checkout@v5
        with:
          fetch-depth: 0

      - name: Resolve release ref
        shell: bash
        run: |
          if [[ "${GITHUB_EVENT_NAME}" == "workflow_dispatch" ]]; then
            [[ "${RELEASE_TAG}" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]
            git fetch --tags --force
            git rev-parse "${RELEASE_TAG}" >/dev/null
            git checkout "${RELEASE_TAG}"
          fi

      - uses: actions/setup-python@v6
        with:
          python-version: '3.12'
          cache: 'pip'

      - uses: actions/setup-node@v6
        with:
          node-version: '20'
          cache: 'npm'
          cache-dependency-path: |
            apps/dsa-web/package-lock.json
            apps/dsa-desktop/package-lock.json

      - name: Sync desktop app version from release tag
        shell: bash
        run: |
          set -euo pipefail
          [[ "${RELEASE_TAG}" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]
          export VERSION="${RELEASE_TAG#v}"
          cd apps/dsa-desktop
          npm version "${VERSION}" --no-git-tag-version --allow-same-version
          node -e "const pkg = require('./package.json'); if (pkg.version !== process.env.VERSION) throw new Error('package version mismatch: ' + pkg.version); console.log('desktop version:', pkg.version);"

      - name: Cache Electron binaries
        uses: actions/cache@v5
        with:
          path: ~/Library/Caches/electron
          key: electron-macos-${{ matrix.arch }}-${{ hashFiles('apps/dsa-desktop/package-lock.json') }}
          restore-keys: |
            electron-macos-${{ matrix.arch }}-

      - name: Disable Spotlight indexing (prevents hdiutil Resource busy)
        run: sudo mdutil -a -i off

      - name: Build desktop package (macOS)
        env:
          DSA_MAC_ARCH: ${{ matrix.arch }}
        run: bash scripts/build-all-macos.sh

      - name: Prepare release artifact (macOS)
        shell: bash
        run: |
          set -euo pipefail
          dmg_file="$(ls -t apps/dsa-desktop/dist/*${{ matrix.arch }}*.dmg 2>/dev/null | head -n 1 || true)"
          if [[ -z "${dmg_file}" ]]; then
            dmg_file="$(ls -t apps/dsa-desktop/dist/*.dmg | head -n 1)"
          fi
          test -n "$dmg_file"
          mkdir -p dist/release-assets
          cp "$dmg_file" "dist/release-assets/daily-stock-analysis-macos-${{ matrix.arch }}-${RELEASE_TAG}.dmg"

      - uses: actions/upload-artifact@v6
        with:
          name: desktop-macos-${{ matrix.arch }}-${{ env.RELEASE_TAG }}
          path: dist/release-assets/*.dmg
          if-no-files-found: error

  publish-release:
    runs-on: ubuntu-latest
    needs: [build-windows, build-macos]
    permissions:
      contents: write
    env:
      RELEASE_TAG: ${{ github.event_name == 'workflow_dispatch' && inputs.release_tag || github.ref_name }}
    steps:
      - uses: actions/checkout@v5

      - name: Validate release tag and extract changelog
        id: changelog
        run: |
          [[ "${RELEASE_TAG}" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]
          VERSION="${RELEASE_TAG#v}"
          # Extract the section for this version from CHANGELOG.md
          BODY=$(awk "/^## \[${VERSION}\]/{found=1; next} found && /^## \[/{exit} found{print}" docs/CHANGELOG.md)
          if [[ -z "$BODY" ]]; then
            echo "Warning: no CHANGELOG entry found for ${VERSION}"
            BODY="See [CHANGELOG](https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/CHANGELOG.md) for details."
          fi
          # Write changelog content + install instructions to file
          echo "$BODY" > /tmp/release_body.md

      - uses: actions/download-artifact@v7
        with:
          pattern: desktop-*
          path: dist/release-assets
          merge-multiple: true

      - name: Show release assets
        run: ls -lah dist/release-assets

      - name: Publish GitHub Release
        uses: softprops/action-gh-release@v2
        with:
          tag_name: ${{ env.RELEASE_TAG }}
          body_path: /tmp/release_body.md
          generate_release_notes: true
          files: dist/release-assets/*
          fail_on_unmatched_files: true
