name: CI and release

on:
  pull_request:
  push:
    branches:
      - '**'
    tags:
      - '*'

permissions:
  contents: read

jobs:
  validate:
    name: Validate (${{ matrix.label }})
    runs-on: ${{ matrix.os }}
    strategy:
      fail-fast: false
      matrix:
        include:
          - os: windows-latest
            label: Windows
          - os: macos-15-intel
            label: macOS (x64)
          - os: macos-15
            label: macOS (arm64)
          - os: ubuntu-latest
            label: Linux

    steps:
      - name: Check out repository
        uses: actions/checkout@v4

      - name: Set up Bun
        uses: oven-sh/setup-bun@v2
        with:
          bun-version: 1.3.6

      - name: Set up .NET
        uses: actions/setup-dotnet@v4
        with:
          dotnet-version: 9.0.x

      - name: Install Linux native dependencies
        if: runner.os == 'Linux'
        run: |
          sudo apt-get update
          sudo apt-get install -y libsecret-1-dev

      - name: Install dependencies
        run: bun install --frozen-lockfile

      - name: Run web tests
        run: bun run test

      - name: Run sidecar tests
        run: bun run sidecar:test

      - name: Build the application
        run: bun run build

  create-release:
    name: Create GitHub release
    if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/')
    needs: validate
    runs-on: ubuntu-latest
    permissions:
      contents: write

    steps:
      - name: Check out repository
        uses: actions/checkout@v4

      - name: Create release if needed
        env:
          GH_TOKEN: ${{ github.token }}
          TAG_NAME: ${{ github.ref_name }}
        run: |
          if gh release view "$TAG_NAME" >/dev/null 2>&1; then
            echo "Release $TAG_NAME already exists."
          else
            gh release create "$TAG_NAME" --title "$TAG_NAME" --generate-notes
          fi

  publish-release-assets:
    name: Publish release asset (${{ matrix.label }})
    if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/')
    needs: create-release
    runs-on: ${{ matrix.os }}
    permissions:
      contents: write
    strategy:
      fail-fast: false
      matrix:
        include:
          - os: windows-latest
            label: Windows
          - os: macos-15-intel
            label: macOS (x64)
          - os: macos-15
            label: macOS (arm64)
          - os: ubuntu-latest
            label: Linux

    steps:
      - name: Check out repository
        uses: actions/checkout@v4

      - name: Set up Bun
        uses: oven-sh/setup-bun@v2
        with:
          bun-version: 1.3.6

      - name: Set up .NET
        uses: actions/setup-dotnet@v4
        with:
          dotnet-version: 9.0.x

      - name: Install Linux native dependencies
        if: runner.os == 'Linux'
        run: |
          sudo apt-get update
          sudo apt-get install -y libsecret-1-dev

      - name: Install dependencies
        run: bun install --frozen-lockfile

      - name: Prepare Apple signing assets
        if: runner.os == 'macOS'
        shell: bash
        env:
          APPLE_CERT_P12_BASE64: ${{ secrets.APPLE_CERT_P12_BASE64 }}
          APPLE_CERT_PASSWORD: ${{ secrets.APPLE_CERT_PASSWORD }}
          APPLE_API_KEY_P8: ${{ secrets.APPLE_API_KEY_P8 }}
          APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }}
          APPLE_API_ISSUER: ${{ secrets.APPLE_API_ISSUER }}
          APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
        run: |
          write_github_env() {
            local name="$1"
            local value="$2"
            local delimiter
            delimiter="ARYX_ENV_$(uuidgen | tr '[:lower:]' '[:upper:]')"
            {
              printf '%s<<%s\n' "$name" "$delimiter"
              printf '%s\n' "$value"
              printf '%s\n' "$delimiter"
            } >> "$GITHUB_ENV"
          }

          if [[ -z "$APPLE_CERT_P12_BASE64" ]]; then
            echo "Missing required secret: APPLE_CERT_P12_BASE64" >&2
            exit 1
          fi
          if [[ -z "$APPLE_CERT_PASSWORD" ]]; then
            echo "Missing required secret: APPLE_CERT_PASSWORD" >&2
            exit 1
          fi
          if [[ -z "$APPLE_API_KEY_P8" ]]; then
            echo "Missing required secret: APPLE_API_KEY_P8" >&2
            exit 1
          fi
          if [[ -z "$APPLE_API_KEY_ID" ]]; then
            echo "Missing required secret: APPLE_API_KEY_ID" >&2
            exit 1
          fi
          if [[ -z "$APPLE_API_ISSUER" ]]; then
            echo "Missing required secret: APPLE_API_ISSUER" >&2
            exit 1
          fi
          if [[ -z "$APPLE_TEAM_ID" ]]; then
            echo "Missing required secret: APPLE_TEAM_ID" >&2
            exit 1
          fi

          SOURCE_CERT_PATH="$RUNNER_TEMP/apple-signing-source.p12"
          CERT_PATH="$RUNNER_TEMP/apple-signing.p12"
          PEM_PATH="$RUNNER_TEMP/apple-signing.pem"
          PRECHECK_KEYCHAIN_PATH="$RUNNER_TEMP/apple-signing-preflight.keychain-db"
          PRECHECK_KEYCHAIN_PASSWORD="$(uuidgen)"
          API_KEY_PATH="$RUNNER_TEMP/AuthKey_${APPLE_API_KEY_ID}.p8"

          cleanup_precheck_keychain() {
            security delete-keychain "$PRECHECK_KEYCHAIN_PATH" >/dev/null 2>&1 || true
          }

          trap cleanup_precheck_keychain EXIT

          CERT_PATH="$SOURCE_CERT_PATH" python3 - <<'PY'
          import base64
          import binascii
          import os
          from pathlib import Path

          raw_value = os.environ["APPLE_CERT_P12_BASE64"]
          normalized_value = "".join(raw_value.split())
          if not normalized_value:
              raise SystemExit("APPLE_CERT_P12_BASE64 is empty after whitespace normalization")

          try:
              decoded = base64.b64decode(normalized_value, validate=True)
          except binascii.Error:
              raise SystemExit("APPLE_CERT_P12_BASE64 is not valid base64")

          if not decoded:
              raise SystemExit("Decoded Apple signing certificate is empty")

          Path(os.environ["CERT_PATH"]).write_bytes(decoded)
          PY
          printf '%s' "$APPLE_API_KEY_P8" > "$API_KEY_PATH"

          if [[ ! -s "$SOURCE_CERT_PATH" ]]; then
            echo "Decoded Apple signing certificate file is empty." >&2
            exit 1
          fi

          if ! openssl pkcs12 -in "$SOURCE_CERT_PATH" -noout -passin env:APPLE_CERT_PASSWORD >/dev/null 2>&1; then
            echo "Decoded Apple signing certificate could not be opened with APPLE_CERT_PASSWORD." >&2
            exit 1
          fi

          if ! openssl pkcs12 -in "$SOURCE_CERT_PATH" -passin env:APPLE_CERT_PASSWORD -nodes -out "$PEM_PATH" >/dev/null 2>&1; then
            echo "Decoded Apple signing certificate could not be converted to PEM." >&2
            exit 1
          fi

          if ! openssl pkcs12 -export -out "$CERT_PATH" -in "$PEM_PATH" -passout env:APPLE_CERT_PASSWORD -macalg sha1 -keypbe PBE-SHA1-3DES -certpbe PBE-SHA1-3DES >/dev/null 2>&1; then
            echo "Apple signing certificate could not be re-exported into a macOS-compatible PKCS#12." >&2
            exit 1
          fi

          if [[ ! -s "$CERT_PATH" ]]; then
            echo "Normalized Apple signing certificate file is empty." >&2
            exit 1
          fi

          if ! security create-keychain -p "$PRECHECK_KEYCHAIN_PASSWORD" "$PRECHECK_KEYCHAIN_PATH" >/dev/null 2>&1; then
            echo "Unable to create the macOS signing precheck keychain." >&2
            exit 1
          fi

          if ! security unlock-keychain -p "$PRECHECK_KEYCHAIN_PASSWORD" "$PRECHECK_KEYCHAIN_PATH" >/dev/null 2>&1; then
            echo "Unable to unlock the macOS signing precheck keychain." >&2
            exit 1
          fi

          if ! security import "$CERT_PATH" -k "$PRECHECK_KEYCHAIN_PATH" -P "$APPLE_CERT_PASSWORD" -T /usr/bin/codesign -T /usr/bin/productsign >/dev/null 2>&1; then
            echo "Normalized Apple signing certificate is still not importable by macOS security." >&2
            exit 1
          fi

          rm -f "$SOURCE_CERT_PATH" "$PEM_PATH"
          cleanup_precheck_keychain
          trap - EXIT

          write_github_env "CSC_LINK" "$CERT_PATH"
          write_github_env "CSC_KEY_PASSWORD" "$APPLE_CERT_PASSWORD"
          write_github_env "APPLE_API_KEY" "$API_KEY_PATH"
          write_github_env "APPLE_API_KEY_ID" "$APPLE_API_KEY_ID"
          write_github_env "APPLE_API_ISSUER" "$APPLE_API_ISSUER"
          write_github_env "APPLE_TEAM_ID" "$APPLE_TEAM_ID"

      - name: Build and publish release artifacts
        env:
          GH_TOKEN: ${{ github.token }}
        run: bun run publish-release
