name: CI

on:
  push:
    branches: [development, master]
  pull_request:
    branches: [development, master]
  workflow_dispatch:

permissions:
  contents: read

jobs:
  quality:
    name: Quality + Smoke
    runs-on: ubuntu-22.04
    timeout-minutes: 45
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Setup Node.js v22
        uses: actions/setup-node@v4
        with:
          node-version: 22
      - run: corepack enable

      - name: Install dependencies
        run: |
          for i in 1 2 3; do
            echo "yarn install attempt $i"
            yarn install --immutable && break
            sleep 5
            [ "$i" = "3" ] && exit 1
          done

      - name: Run Vitest
        run: yarn test --run

      - name: Collect coverage
        run: yarn test:coverage

      - name: Write coverage badge payload
        if: github.event_name == 'push' && github.ref == 'refs/heads/master'
        run: node scripts/write-coverage-badge.mjs

      - name: Upload coverage badge
        if: github.event_name == 'push' && github.ref == 'refs/heads/master'
        uses: actions/upload-artifact@v4
        with:
          name: coverage-badge
          path: badges/coverage.json
          if-no-files-found: error

      - name: Lint
        run: yarn lint

      - name: Type check
        run: yarn type-check

      - name: Build React app
        run: yarn build
        env:
          CI: ''

      - name: Build preload script
        run: yarn build:preload

      - name: Verify build outputs
        run: |
          ls -la build/ || exit 1
          ls -la build/electron/preload.cjs || exit 1
          ls -la build/index.html || exit 1

      - name: Verify forge config
        run: node -e "import('./forge.config.js').then(c => console.log('Config loaded, makers:', c.default.makers?.length || 0)).catch(e => { console.error(e); process.exit(1); })"

      - name: Detect React UI changes
        id: react-ui-changes
        if: github.event_name == 'pull_request'
        run: |
          git diff --name-only "${{ github.event.pull_request.base.sha }}" HEAD > changed-files.txt
          cat changed-files.txt
          if grep -Eq '^(src/(components|views|hooks|stores)/|src/app\.tsx|src/main\.tsx)' changed-files.txt; then
            echo "changed=true" >> "$GITHUB_OUTPUT"
          else
            echo "changed=false" >> "$GITHUB_OUTPUT"
          fi

      - name: Run React Doctor
        if: github.event_name != 'pull_request'
        run: yarn doctor

      - name: Run React Doctor on changed React files
        if: github.event_name == 'pull_request' && steps.react-ui-changes.outputs.changed == 'true'
        run: yarn doctor --diff "${{ github.event.pull_request.base.sha }}" --annotations

      - name: Skip React Doctor
        if: github.event_name == 'pull_request' && steps.react-ui-changes.outputs.changed != 'true'
        run: echo "Skipping React Doctor because this pull request did not change React UI source."

      - name: Write React Doctor badge payload
        if: github.event_name == 'push' && github.ref == 'refs/heads/master'
        run: node scripts/write-react-doctor-badge.mjs

      - name: Upload React Doctor badge
        if: github.event_name == 'push' && github.ref == 'refs/heads/master'
        uses: actions/upload-artifact@v4
        with:
          name: react-doctor-badge
          path: badges/react-doctor.json
          if-no-files-found: error

      - name: Install Chromium for smoke tests
        run: npx playwright install --with-deps chromium

      - name: Smoke test built web app
        run: node scripts/smoke-web-app.js --start-preview

      - name: Upload coverage summary
        if: always()
        uses: actions/upload-artifact@v4
        with:
          name: coverage-summary
          path: coverage/coverage-summary.json
          if-no-files-found: warn

  publish-coverage-badge:
    name: Publish Badges
    runs-on: ubuntu-22.04
    needs: quality
    if: github.event_name == 'push' && github.ref == 'refs/heads/master'
    permissions:
      contents: read
      pages: write
      id-token: write
    environment:
      name: github-pages
      url: ${{ steps.deployment.outputs.page_url }}
    steps:
      - name: Configure GitHub Pages
        uses: actions/configure-pages@v5

      - name: Download coverage badge
        uses: actions/download-artifact@v4
        with:
          name: coverage-badge
          path: ${{ runner.temp }}/coverage-badge

      - name: Download React Doctor badge
        uses: actions/download-artifact@v4
        with:
          name: react-doctor-badge
          path: ${{ runner.temp }}/react-doctor-badge

      - name: Prepare Pages artifact
        env:
          BADGE_SOURCE_PATH: ${{ runner.temp }}/coverage-badge/coverage.json
          REACT_DOCTOR_BADGE_SOURCE_PATH: ${{ runner.temp }}/react-doctor-badge/react-doctor.json
          PAGES_OUTPUT_PATH: ${{ runner.temp }}/github-pages
        run: |
          mkdir -p "${PAGES_OUTPUT_PATH}/badges"
          cp "${BADGE_SOURCE_PATH}" "${PAGES_OUTPUT_PATH}/badges/coverage.json"
          cp "${REACT_DOCTOR_BADGE_SOURCE_PATH}" "${PAGES_OUTPUT_PATH}/badges/react-doctor.json"
          touch "${PAGES_OUTPUT_PATH}/.nojekyll"

      - name: Upload Pages artifact
        uses: actions/upload-pages-artifact@v4
        with:
          path: ${{ runner.temp }}/github-pages

      - name: Deploy badge site
        id: deployment
        uses: actions/deploy-pages@v4

  android-build:
    name: Android Build
    runs-on: ubuntu-22.04
    needs: quality
    timeout-minutes: 45
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Setup Java 17
        uses: actions/setup-java@v4
        with:
          distribution: 'zulu'
          java-version: '17'

      - name: Setup Gradle
        uses: gradle/actions/setup-gradle@v4

      - name: Setup Node.js v22
        uses: actions/setup-node@v4
        with:
          node-version: 22
      - run: corepack enable

      - name: Install dependencies
        run: |
          for i in 1 2 3; do
            echo "yarn install attempt $i"
            yarn install --immutable && break
            sleep 5
            [ "$i" = "3" ] && exit 1
          done

      - name: Build React app
        run: yarn build
        env:
          CI: ''

      - name: Sync Capacitor Android project
        run: npx cap sync android

      - name: Run Android unit tests and assemble debug APKs
        run: cd android && ./gradlew testGithubDebugUnitTest testFdroidDebugUnitTest assembleGithubDebug assembleFdroidDebug --stacktrace

  package-linux:
    name: Package Linux (Ubuntu)
    runs-on: ubuntu-22.04
    needs: quality
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Setup Node.js v22
        uses: actions/setup-node@v4
        with:
          node-version: 22
      - run: corepack enable

      - name: Cache electron binaries
        uses: actions/cache@v4
        with:
          path: ~/.cache/electron
          key: ${{ runner.os }}-electron-v2-${{ hashFiles('**/yarn.lock') }}
          restore-keys: ${{ runner.os }}-electron-v2-

      - name: Install dependencies
        run: |
          for i in 1 2 3; do
            echo "yarn install attempt $i"
            yarn install --immutable && break
            sleep 5
            [ "$i" = "3" ] && exit 1
          done

      - name: Build React app
        run: yarn build
        env:
          CI: ''

      - name: Build preload script
        run: yarn build:preload

      - name: Verify build outputs
        run: |
          ls -la build/ || exit 1
          ls -la build/electron/preload.cjs || exit 1
          ls -la build/index.html || exit 1

      - name: Rebuild and verify Electron native modules
        timeout-minutes: 10
        run: yarn electron:prepare-package

      - name: Package Electron app
        timeout-minutes: 30
        run: |
          set -o pipefail
          yarn electron-forge package 2>&1 | tee forge-output.log
          ls -la out/

      - name: Verify executable
        run: |
          ls -la out/
          EXE=$(node scripts/find-forge-executable.js)
          echo "Found executable: $EXE"
          test -f "$EXE" && test -x "$EXE" && echo "Executable is valid"

  package-mac-intel:
    name: Package Mac (Intel)
    runs-on: macos-15-intel
    needs: quality
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Setup Node.js v22
        uses: actions/setup-node@v4
        with:
          node-version: 22
      - run: corepack enable

      - name: Install setuptools for native modules
        run: pip3 install --break-system-packages setuptools || pip3 install --user setuptools || true

      - name: Cache electron binaries
        uses: actions/cache@v4
        with:
          path: ~/Library/Caches/electron
          key: ${{ runner.os }}-electron-v2-${{ hashFiles('**/yarn.lock') }}
          restore-keys: ${{ runner.os }}-electron-v2-

      - name: Install dependencies
        run: |
          for i in 1 2 3; do
            echo "yarn install attempt $i"
            yarn install --immutable && break
            sleep 5
            [ "$i" = "3" ] && exit 1
          done

      - name: Build React app
        run: yarn build
        env:
          CI: ''

      - name: Build preload script
        run: yarn build:preload

      - name: Verify build outputs
        run: |
          ls -la build/ || exit 1
          ls -la build/electron/preload.cjs || exit 1
          ls -la build/index.html || exit 1

      - name: Rebuild and verify Electron native modules
        timeout-minutes: 10
        run: yarn electron:prepare-package

      - name: Package Electron app
        timeout-minutes: 30
        run: |
          set -o pipefail
          yarn electron-forge package 2>&1 | tee forge-output.log
          ls -la out/

      - name: Verify executable
        run: |
          ls -la out/
          EXE=$(node scripts/find-forge-executable.js)
          echo "Found executable: $EXE"
          test -f "$EXE" && test -x "$EXE" && echo "Executable is valid"

  package-mac-arm:
    name: Package Mac (Apple Silicon)
    runs-on: macos-latest
    needs: quality
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Setup Node.js v22
        uses: actions/setup-node@v4
        with:
          node-version: 22
      - run: corepack enable

      - name: Install setuptools for native modules
        run: pip3 install --break-system-packages setuptools || pip3 install --user setuptools || true

      - name: Cache electron binaries
        uses: actions/cache@v4
        with:
          path: ~/Library/Caches/electron
          key: ${{ runner.os }}-electron-v2-${{ hashFiles('**/yarn.lock') }}
          restore-keys: ${{ runner.os }}-electron-v2-

      - name: Install dependencies
        run: |
          for i in 1 2 3; do
            echo "yarn install attempt $i"
            yarn install --immutable && break
            sleep 5
            [ "$i" = "3" ] && exit 1
          done

      - name: Build React app
        run: yarn build
        env:
          CI: ''

      - name: Build preload script
        run: yarn build:preload

      - name: Verify build outputs
        run: |
          ls -la build/ || exit 1
          ls -la build/electron/preload.cjs || exit 1
          ls -la build/index.html || exit 1

      - name: Rebuild and verify Electron native modules
        timeout-minutes: 10
        run: yarn electron:prepare-package

      - name: Package Electron app
        timeout-minutes: 30
        run: |
          set -o pipefail
          yarn electron-forge package 2>&1 | tee forge-output.log
          ls -la out/

      - name: Verify executable
        run: |
          ls -la out/
          EXE=$(node scripts/find-forge-executable.js)
          echo "Found executable: $EXE"
          test -f "$EXE" && test -x "$EXE" && echo "Executable is valid"

  package-windows:
    name: Package Windows
    runs-on: windows-2022
    needs: quality
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Setup Node.js v22
        uses: actions/setup-node@v4
        with:
          node-version: 22
      - run: corepack enable

      - name: Resolve Yarn cache path
        id: yarn-cache
        shell: bash
        run: echo "dir=$(corepack yarn config get cacheFolder)" >> "$GITHUB_OUTPUT"

      - name: Cache Yarn package tarballs
        uses: actions/cache@v4
        with:
          path: ${{ steps.yarn-cache.outputs.dir }}
          key: ${{ runner.os }}-yarn-v1-${{ hashFiles('yarn.lock', 'package.json', '.yarnrc.yml') }}
          restore-keys: ${{ runner.os }}-yarn-v1-

      - name: Cache electron binaries
        uses: actions/cache@v4
        with:
          path: ~/AppData/Local/electron/Cache
          key: ${{ runner.os }}-electron-v2-${{ hashFiles('**/yarn.lock') }}
          restore-keys: ${{ runner.os }}-electron-v2-

      - name: Install dependencies
        shell: bash
        run: |
          for i in 1 2 3; do
            echo "yarn install attempt $i"
            yarn install --immutable && break
            sleep 5
            [ "$i" = "3" ] && exit 1
          done

      - name: Build React app
        shell: bash
        run: yarn build
        env:
          CI: ''

      - name: Build preload script
        shell: bash
        run: yarn build:preload

      - name: Verify build outputs
        shell: bash
        run: |
          ls -la build/ || exit 1
          ls -la build/electron/preload.cjs || exit 1
          ls -la build/index.html || exit 1

      - name: Rebuild and verify Electron native modules
        shell: bash
        timeout-minutes: 10
        run: yarn electron:prepare-package

      - name: Package Electron app
        shell: bash
        timeout-minutes: 30
        run: |
          set -o pipefail
          yarn electron-forge package 2>&1 | tee forge-output.log
          ls -la out/

      - name: Verify executable
        shell: bash
        run: |
          ls -la out/
          EXE=$(node scripts/find-forge-executable.js)
          echo "Found executable: $EXE"
          test -f "$EXE" && test -x "$EXE" && echo "Executable is valid"
