name: CI

on:
  push:
    branches: [main]
    tags: ["v*"]
  pull_request:
    branches: [main]
  workflow_dispatch:

# Targets a self-hosted Windows runner inside mainland China. See README
# "Continuous integration" for runner prerequisites. Key points:
#   * github.com is unreachable here, so the checkout action is pulled from the
#     gitea.com mirror. If gitea.com is also unreachable, use the manual
#     checkout fallback below (it clones from your own Gitea instance).
#   * Flutter must be pre-installed on the runner (the dev machine). We do NOT
#     download the SDK.
#   * pub/Flutter use the flutter-io.cn mirrors; the sqlite3 native binary is
#     vendored (vendor/sqlite3/), so neither is downloaded from GitHub.
#   * The flutter_onnxruntime plugin downloads the ONNX Runtime native lib from
#     GitHub during the Windows build; we route that through the local proxy.
env:
  PUB_HOSTED_URL: https://pub.flutter-io.cn
  FLUTTER_STORAGE_BASE_URL: https://storage.flutter-io.cn

jobs:
  windows:
    name: Windows build
    runs-on: windows-latest
    steps:
      - name: Checkout
        uses: https://gitea.com/actions/checkout@v4

      # Ensure Flutter is available. The runner's Actions shell does not have
      # Flutter on PATH, so: use it if present (PATH / FLUTTER_ROOT / common
      # dirs), otherwise install the pinned SDK from the flutter-io.cn mirror
      # into a persistent dir (C:\flutter-ci) that is reused across runs.
      # Pin Flutter 3.44.2 DETERMINISTICALLY. pdfrx needs Windows stylus +
      # multitouch, which only landed in Flutter 3.44.0 (WM_POINTER migration).
      # We install into a version-pinned dir and PREPEND it to PATH so this
      # version shadows any older Flutter already on the runner (a stale
      # C:\flutter-ci from prior runs must NOT win, or pen input breaks again).
      - name: Set up Flutter (pinned 3.44.2)
        shell: powershell
        run: |
          $ErrorActionPreference = 'Stop'
          $ProgressPreference = 'SilentlyContinue'
          $ver  = '3.44.2'
          $root = "C:\flutter-$ver"
          $bin  = Join-Path $root 'flutter\bin'
          if (-not (Test-Path (Join-Path $bin 'flutter.bat'))) {
            $url = "https://storage.flutter-io.cn/flutter_infra_release/releases/stable/windows/flutter_windows_$ver-stable.zip"
            $zip = Join-Path $env:TEMP "flutter_$ver.zip"
            New-Item -ItemType Directory -Force -Path $root | Out-Null
            Write-Host "Downloading Flutter $ver from $url"
            $ok = $false
            $curl = "$env:SystemRoot\System32\curl.exe"
            if (Test-Path $curl) {
              & $curl -L --fail --retry 3 --retry-delay 5 -o $zip $url
              if ($LASTEXITCODE -eq 0 -and (Test-Path $zip)) { $ok = $true }
              else { Write-Host "curl download failed (exit $LASTEXITCODE), falling back" }
            }
            if (-not $ok) {
              for ($i = 1; $i -le 3 -and -not $ok; $i++) {
                try { Invoke-WebRequest -Uri $url -OutFile $zip -UseBasicParsing; $ok = $true }
                catch { Write-Host "IWR attempt $i failed: $_"; Start-Sleep 5 }
              }
            }
            if (-not $ok) { Write-Error "Failed to download Flutter $ver"; exit 1 }
            Write-Host "Extracting to $root (slow on first run)"
            Expand-Archive -Path $zip -DestinationPath $root -Force
            Remove-Item $zip -Force
          }
          if (-not (Test-Path (Join-Path $bin 'flutter.bat'))) {
            Write-Error "Flutter $ver install failed"; exit 1
          }
          # Prepend (first line wins in GITHUB_PATH) so 3.44.2 shadows any
          # system/cached Flutter on the runner.
          Add-Content -Path $env:GITHUB_PATH -Value $bin
          Write-Host "Flutter $ver pinned at $bin"

      - name: Flutter version
        run: flutter --version

      # pdfrx REQUIRES Windows Developer Mode (its build creates symbolic links;
      # without Dev Mode `flutter build windows` fails). Enable it on the runner
      # by setting the AppModelUnlock registry key (idempotent — a no-op if it is
      # already on). Requires the runner to be elevated; if it is not, this fails
      # loudly here, which is correct since the build cannot proceed regardless.
      - name: Enable Windows Developer Mode (pdfrx symlinks)
        shell: powershell
        run: |
          $ErrorActionPreference = 'Stop'
          $key = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\AppModelUnlock'
          if (-not (Test-Path $key)) { New-Item -Path $key -Force | Out-Null }
          New-ItemProperty -Path $key -Name 'AllowDevelopmentWithoutDevLicense' `
            -Value 1 -PropertyType DWORD -Force | Out-Null
          $v = (Get-ItemProperty -Path $key -Name 'AllowDevelopmentWithoutDevLicense').AllowDevelopmentWithoutDevLicense
          Write-Host "AllowDevelopmentWithoutDevLicense = $v (1 = Developer Mode on)"

      - name: Install dependencies
        run: flutter pub get

      # Quality gates run but never block the build, so a usable .exe is always
      # produced even if a different toolchain version reports new lints.
      - name: Format check (non-blocking)
        continue-on-error: true
        run: dart format --output=none --set-exit-if-changed lib test

      - name: Analyze (non-blocking)
        continue-on-error: true
        run: flutter analyze

      - name: Test (non-blocking)
        continue-on-error: true
        run: flutter test --reporter expanded

      - name: Enable Windows desktop
        run: flutter config --enable-windows-desktop

      # pdfrx -> pdfium_dart pulls pdfium.dll from github.com at build time via a
      # native-assets build hook (hook/build.dart) that uses package:http, which
      # IGNORES HTTP_PROXY. Behind the GFW that direct download fails. The hook
      # skips downloading when its output file already exists, so we (1) pre-fetch
      # pdfium.dll through a CN-accessible GitHub mirror and pre-place it at the
      # hook's deterministic shared-output path, and (2) also rewrite the hook's
      # hardcoded GitHub URL to the mirror as a fallback.
      - name: Pre-fetch PDFium (pdfrx native asset)
        shell: powershell
        run: |
          $ErrorActionPreference = 'Stop'
          $ProgressPreference = 'SilentlyContinue'
          $rel = 'chromium/7811'
          # Deterministic native-assets shared output path (verified from a real
          # build: .dart_tool/hooks_runner/shared/pdfium_dart/build/<rel>/<plat>).
          $dst = '.dart_tool\hooks_runner\shared\pdfium_dart\build\chromium_7811\win-x64'
          $dll = Join-Path $dst 'pdfium.dll'
          if (-not (Test-Path $dll)) {
            New-Item -ItemType Directory -Force -Path $dst | Out-Null
            $tgz = Join-Path $env:TEMP 'pdfium-win-x64.tgz'
            $gh = "github.com/bblanchon/pdfium-binaries/releases/download/$rel/pdfium-win-x64.tgz"
            $urls = @("https://gh-proxy.com/https://$gh", "https://ghfast.top/https://$gh", "https://$gh")
            $curl = "$env:SystemRoot\System32\curl.exe"
            $ok = $false
            foreach ($u in $urls) {
              Write-Host "Fetching PDFium: $u"
              & $curl -L --fail --retry 2 --retry-delay 5 -o $tgz $u
              if ($LASTEXITCODE -eq 0 -and (Test-Path $tgz)) { $ok = $true; break }
              Write-Host "  failed (exit $LASTEXITCODE)"
            }
            if (-not $ok) { Write-Error "Failed to fetch PDFium from all mirrors"; exit 1 }
            $ex = Join-Path $env:TEMP 'pdfium_extract'
            if (Test-Path $ex) { Remove-Item -Recurse -Force $ex }
            New-Item -ItemType Directory -Force -Path $ex | Out-Null
            & tar -xzf $tgz -C $ex
            if ($LASTEXITCODE -ne 0) { Write-Error "tar extract failed"; exit 1 }
            Copy-Item (Join-Path $ex 'bin\pdfium.dll') $dll -Force
            Remove-Item $tgz -Force
            if (-not (Test-Path $dll)) { Write-Error "pdfium.dll not placed"; exit 1 }
            Write-Host "PDFium pre-placed at $dll"
          } else {
            Write-Host "pdfium.dll already present at $dll"
          }
          # Fallback: rewrite the hook's hardcoded GitHub URL to the mirror, in
          # case the pre-placed path is not consulted for some build config.
          $roots = @($env:PUB_CACHE, "$env:LOCALAPPDATA\Pub\Cache", "$env:APPDATA\Pub\Cache") |
            Where-Object { $_ -and (Test-Path $_) }
          foreach ($r in $roots) {
            $hook = Get-ChildItem -Path $r -Recurse -Filter build.dart -ErrorAction SilentlyContinue |
              Where-Object { $_.FullName -match 'pdfium_dart-[\d.]+[\\/]hook[\\/]build\.dart' } |
              Select-Object -First 1
            if ($hook) {
              $orig = 'https://github.com/bblanchon/pdfium-binaries/releases/download/'
              $mirror = 'https://gh-proxy.com/https://github.com/bblanchon/pdfium-binaries/releases/download/'
              $c = Get-Content -Raw -LiteralPath $hook.FullName
              if ($c -notmatch [regex]::Escape($mirror)) {
                Set-Content -LiteralPath $hook.FullName -Value ($c.Replace($orig, $mirror)) -NoNewline
                Write-Host "Patched hook URL to mirror: $($hook.FullName)"
              }
              break
            }
          }

      # The flutter_onnxruntime plugin downloads the ONNX Runtime native lib
      # from github.com during the build, which is blocked here. Pre-place the
      # extracted SDK where the plugin expects it (via a China-accessible GitHub
      # proxy) so it skips the download and still bundles onnxruntime.dll.
      - name: Pre-fetch ONNX Runtime
        shell: powershell
        run: |
          $ErrorActionPreference = 'Stop'
          $ProgressPreference = 'SilentlyContinue'
          $ver = 'onnxruntime-win-x64-1.22.0'
          $dst = 'build\windows\x64\plugins\flutter_onnxruntime\onnxruntime'
          $marker = Join-Path $dst "$ver\include\onnxruntime_cxx_api.h"
          if (Test-Path $marker) { Write-Host "ONNX Runtime already present"; exit 0 }
          New-Item -ItemType Directory -Force -Path $dst | Out-Null
          $zip = Join-Path $env:TEMP 'ort.zip'
          $gh = "github.com/microsoft/onnxruntime/releases/download/v1.22.0/$ver.zip"
          $urls = @("https://gh-proxy.com/https://$gh", "https://ghfast.top/https://$gh", "https://$gh")
          $curl = "$env:SystemRoot\System32\curl.exe"
          $ok = $false
          foreach ($u in $urls) {
            Write-Host "Fetching ONNX Runtime: $u"
            & $curl -L --fail --retry 2 --retry-delay 5 -o $zip $u
            if ($LASTEXITCODE -eq 0 -and (Test-Path $zip)) { $ok = $true; break }
            Write-Host "  failed (exit $LASTEXITCODE)"
          }
          if (-not $ok) { Write-Error "Failed to fetch ONNX Runtime from all mirrors"; exit 1 }
          Expand-Archive -Path $zip -DestinationPath $dst -Force
          Remove-Item $zip -Force
          if (-not (Test-Path $marker)) { Write-Error "ONNX Runtime not in expected layout"; exit 1 }
          Write-Host "ONNX Runtime ready at $dst\$ver"

      # Proxy vars are kept as a fallback in case the pre-fetch above is bypassed
      # (CMake's file(DOWNLOAD) honours them).
      - name: Build Windows release
        env:
          HTTP_PROXY: http://192.168.31.189:7890
          HTTPS_PROXY: http://192.168.31.189:7890
          http_proxy: http://192.168.31.189:7890
          https_proxy: http://192.168.31.189:7890
        run: flutter build windows --release

      - name: Show build output
        shell: powershell
        run: Get-ChildItem build\windows\x64\runner\Release

      - name: Package artifact
        shell: powershell
        run: Compress-Archive -Path "build/windows/x64/runner/Release/*" -DestinationPath "badnote-windows-x64.zip" -Force

      - name: Upload artifact (best-effort)
        continue-on-error: true
        uses: https://gitea.com/actions/upload-artifact@v3
        with:
          name: badnote-windows-x64
          path: badnote-windows-x64.zip
          if-no-files-found: warn
