name: Desktop E2E Smoke

on:
    push:
        branches:
            - main
    pull_request:
    merge_group:
        types:
            - checks_requested
    workflow_dispatch:

permissions:
    contents: read
    pull-requests: read

concurrency:
    group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.event.merge_group.head_sha || github.ref }}
    cancel-in-progress: true

jobs:
    changes:
        name: Classify PR changes
        runs-on: ubuntu-latest
        outputs:
            docs_only: ${{ steps.classify.outputs.docs_only }}
            desktop_changed: ${{ steps.classify.outputs.desktop_changed }}
        steps:
            - id: classify
              name: Detect docs-only pull requests
              uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
              with:
                  script: |
                      if (context.eventName !== 'pull_request' && context.eventName !== 'push') {
                        core.setOutput('docs_only', 'false');
                        core.setOutput('desktop_changed', 'true');
                        return;
                      }

                      const docsOnlyPatterns = [
                        /^docs\//,
                        /^[^/]+\.md$/,
                        /^COPYING$/,
                        /^\.github\/ISSUE_TEMPLATE\//,
                        /^\.github\/dependabot\.yml$/,
                        /^\.github\/labeler\.yml$/,
                        /^\.github\/labels\.yml$/,
                        /^\.github\/pull_request_template\.md$/,
                        /^CLAUDE\.md$/,
                        /^CODE_OF_CONDUCT\.md$/,
                        /^CONTRIBUTING\.md$/,
                        /^README\.en\.md$/,
                        /^README\.md$/,
                        /^SECURITY\.md$/,
                        /^SUPPORT\.md$/,
                      ];
                      const desktopPatterns = [
                        /^apps\/desktop\//,
                        /^packages\//,
                        /^package\.json$/,
                        /^pnpm-lock\.yaml$/,
                        /^pnpm-workspace\.yaml$/,
                        /^\.github\/workflows\/e2e-smoke\.yml$/,
                      ];

                      let files = [];

                      if (context.eventName === 'pull_request') {
                        files = await github.paginate(github.rest.pulls.listFiles, {
                          ...context.repo,
                          pull_number: context.issue.number,
                          per_page: 100,
                        });
                      } else if (context.eventName === 'push') {
                        const before = context.payload.before;
                        const after = context.payload.after;

                        if (!before || !after || /^0+$/.test(before)) {
                          core.setOutput('docs_only', 'false');
                          core.setOutput('desktop_changed', 'true');
                          return;
                        }

                        const comparison = await github.rest.repos.compareCommits({
                          ...context.repo,
                          base: before,
                          head: after,
                        });

                        files = comparison.data.files ?? [];

                        if (files.length >= 300) {
                          core.setOutput('docs_only', 'false');
                          core.setOutput('desktop_changed', 'true');
                          return;
                        }
                      }

                      const docsOnly =
                        files.length > 0 &&
                        files.every((file) => docsOnlyPatterns.some((pattern) => pattern.test(file.filename)));
                      const filenames = files.map((file) => file.filename);
                      const desktopChanged =
                        filenames.length === 0 ||
                        filenames.some((filename) => desktopPatterns.some((pattern) => pattern.test(filename)));

                      core.setOutput('docs_only', docsOnly ? 'true' : 'false');
                      core.setOutput('desktop_changed', desktopChanged ? 'true' : 'false');

            - name: Report classification
              run: |
                  echo "docs_only=${{ steps.classify.outputs.docs_only }}"
                  echo "desktop_changed=${{ steps.classify.outputs.desktop_changed }}"

    desktop-e2e-smoke-windows:
        name: Desktop E2E Smoke (Windows)
        needs: changes
        if: needs.changes.outputs.docs_only != 'true' && needs.changes.outputs.desktop_changed == 'true'
        runs-on: windows-latest
        env:
            TOUCHAI_CARGO_TARGET_DIR: apps/desktop/src-tauri/target/e2e
            TOUCHAI_E2E_CARGO_PROFILE: ci-check
        steps:
            - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4

            - uses: pnpm/action-setup@0e279bb959325dab635dd2c09392533439d90093 # v6

            - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
              with:
                  node-version: 22
                  package-manager-cache: false

            - name: Install Rust stable
              uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # stable

            - name: Rust cache
              uses: swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2
              with:
                  workspaces: apps/desktop/src-tauri -> target/e2e

            - name: Install frontend dependencies
              run: pnpm install --frozen-lockfile

            - name: Install tauri-driver
              run: cargo install tauri-driver --locked

            - name: Configure Edge WebDriver
              shell: pwsh
              run: |
                  $applicationRoots = @()
                  if (${env:ProgramFiles(x86)}) {
                    $applicationRoots += Join-Path ${env:ProgramFiles(x86)} 'Microsoft\EdgeWebView\Application'
                  }

                  if ($env:ProgramFiles) {
                    $applicationRoots += Join-Path $env:ProgramFiles 'Microsoft\EdgeWebView\Application'
                  }

                  $runtime = $applicationRoots |
                    Where-Object { Test-Path -LiteralPath $_ } |
                    ForEach-Object { Get-ChildItem -LiteralPath $_ -Directory } |
                    Where-Object { $_.Name -match '^\d+\.\d+\.\d+\.\d+$' } |
                    Sort-Object { [version]$_.Name } -Descending |
                    Select-Object -First 1

                  if (-not $runtime) {
                    throw "Microsoft Edge WebView2 runtime version directory was not found."
                  }

                  $driverVersion = $runtime.Name
                  $toolsDir = Join-Path $env:RUNNER_TEMP 'touchai-edgedriver'
                  $archivePath = Join-Path $toolsDir 'edgedriver_win64.zip'
                  $driverDir = Join-Path $toolsDir $driverVersion
                  $driverPath = Join-Path $driverDir 'msedgedriver.exe'
                  $downloadUrl = "https://msedgedriver.microsoft.com/$driverVersion/edgedriver_win64.zip"

                  New-Item -ItemType Directory -Force -Path $toolsDir, $driverDir | Out-Null
                  Invoke-WebRequest -UseBasicParsing -Uri $downloadUrl -OutFile $archivePath
                  Expand-Archive -LiteralPath $archivePath -DestinationPath $driverDir -Force

                  if (-not (Test-Path -LiteralPath $driverPath)) {
                    throw "msedgedriver.exe was not found after extracting $archivePath"
                  }

                  Write-Host "Using Microsoft Edge WebView2 runtime $driverVersion"
                  & $driverPath --version
                  "TOUCHAI_MSEDGEDRIVER_PATH=$driverPath" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append

            - name: Run desktop smoke tests
              run: pnpm test:e2e

    e2e-required:
        name: E2E Required
        if: always()
        needs:
            - changes
            - desktop-e2e-smoke-windows
        runs-on: ubuntu-latest
        steps:
            - name: Verify required E2E jobs
              env:
                  DOCS_ONLY: ${{ needs.changes.outputs.docs_only }}
                  DESKTOP_CHANGED: ${{ needs.changes.outputs.desktop_changed }}
                  CHANGES_RESULT: ${{ needs.changes.result }}
                  DESKTOP_E2E_RESULT: ${{ needs['desktop-e2e-smoke-windows'].result }}
              run: |
                  required=("changes:$CHANGES_RESULT")

                  if [ "$DOCS_ONLY" != "true" ] && [ "$DESKTOP_CHANGED" = "true" ]; then
                    required+=("desktop-e2e-smoke-windows:$DESKTOP_E2E_RESULT")
                  fi

                  failed=0
                  observed=(
                    "changes:$CHANGES_RESULT"
                    "desktop-e2e-smoke-windows:$DESKTOP_E2E_RESULT"
                  )

                  for check in "${observed[@]}"; do
                    name="${check%%:*}"
                    result="${check#*:}"

                    if [ "$result" = "failure" ] || [ "$result" = "cancelled" ]; then
                      echo "$name unexpectedly ended with $result."
                      failed=1
                    fi
                  done

                  for check in "${required[@]}"; do
                    name="${check%%:*}"
                    result="${check#*:}"
                    echo "$name -> $result"

                    if [ "$result" != "success" ]; then
                      failed=1
                    fi
                  done

                  if [ "$failed" -ne 0 ]; then
                    echo "One or more required E2E jobs did not pass."
                    exit 1
                  fi
