name: ci-deploy

on:
  push:
    branches: [main]
  workflow_dispatch:

# Single deploy at a time per host — pve-201's docker container name
# `flights-web` is a shared mutex. Without this, back-to-back pushes
# race on `docker stop / rm / run`, with the second run hitting
# "container name already in use". Queue, don't cancel.
concurrency:
  group: ci-deploy-pve-201
  cancel-in-progress: false

jobs:
  build-deploy-test:
    runs-on: ubuntu-latest
    timeout-minutes: 30
    env:
      # MAP_TILE_URL / API_BASE_URL are intentionally NOT exported at job level —
      # vitest validates them via Zod and rejects relative paths. Build args are
      # set inline on the docker_build step instead.
      BASIC_AUTH_USER: ${{ secrets.BASIC_AUTH_USER }}
      BASIC_AUTH_PASS: ${{ secrets.BASIC_AUTH_PASS }}
      TELEGRAM_BOT_TOKEN: ${{ secrets.TELEGRAM_BOT_TOKEN }}
      TELEGRAM_CHAT_ID: ${{ secrets.TELEGRAM_CHAT_ID }}
      FLIGHTS_WEB_PORT: '3002'

    steps:
      - name: Checkout
        uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Notify start
        if: ${{ env.TELEGRAM_BOT_TOKEN != '' }}
        run: scripts/ci/notify-telegram.sh start ci-deploy

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version-file: '.nvmrc'

      - name: Setup pnpm
        uses: pnpm/action-setup@v4

      - name: Restore pnpm cache
        uses: actions/cache@v4
        with:
          path: ~/.pnpm-store
          key: pnpm-${{ hashFiles('pnpm-lock.yaml') }}
          restore-keys: pnpm-

      - name: Install dependencies
        id: deps
        run: pnpm install --frozen-lockfile

      - name: Typecheck
        id: typecheck
        run: pnpm typecheck

      - name: Lint
        id: lint
        run: pnpm lint

      - name: Unit tests
        id: unit
        # tests/eslint/* are skipped in CI: typescript-eslint's project cache
        # doesn't see runtime-generated probe files inside the runner container,
        # though they pass locally. They're a dev-time eslint-config-drift guard
        # and re-run on `pnpm test` locally before commit.
        run: pnpm test -- --exclude 'tests/eslint/**'

      - name: CI script tests
        id: citest
        run: pnpm test:ci

      - name: Build SSR image
        id: docker_build
        env:
          # Both must be full URLs — Zod's .url() validator in src/env/index.ts
          # rejects relative paths at runtime in the browser. Same-origin works
          # because the public host is also where nginx is.
          MAP_TILE_URL: ${{ secrets.MAP_TILE_URL || 'https://ui-dashboard.gnerim.ru/map/api/tile/{z}/{x}/{y}.jpeg' }}
          API_BASE_URL: ${{ secrets.API_BASE_URL || 'https://ui-dashboard.gnerim.ru/api' }}
        run: |
          docker build -f Dockerfile.react \
            --build-arg "MAP_TILE_URL=${MAP_TILE_URL}" \
            --build-arg "API_BASE_URL=${API_BASE_URL}" \
            -t "flights-web:${GITHUB_SHA:0:7}" \
            .

      - name: Swap container
        id: swap
        run: scripts/ci/deploy-container.sh swap

      - name: Wait for health
        id: health
        env:
          BASIC_AUTH_USER: ${{ secrets.BASIC_AUTH_USER }}
          BASIC_AUTH_PASS: ${{ secrets.BASIC_AUTH_PASS }}
        run: scripts/ci/wait-for-url.sh https://ui-dashboard.gnerim.ru/ 30 2

      - name: Rollback on failure (post-deploy steps)
        if: failure() && (steps.swap.outcome == 'failure' || steps.health.outcome == 'failure')
        id: rollback
        run: scripts/ci/deploy-container.sh rollback

      - name: Capture container logs (on failure)
        if: failure()
        run: docker logs flights-web --tail 500 > container.log 2>&1 || true

      - name: Upload artifacts on failure
        if: failure()
        uses: actions/upload-artifact@v3
        with:
          name: ci-deploy-failure-${{ github.run_id }}
          path: container.log
          retention-days: 7

      - name: Prune old images
        if: success()
        run: |
          docker images flights-web --format '{{.Tag}} {{.ID}}' \
            | grep -vE '^(current|previous)\b' \
            | tail -n +6 \
            | awk '{print $2}' \
            | xargs -r docker rmi 2>/dev/null || true

      - name: Notify (success)
        if: success() && env.TELEGRAM_BOT_TOKEN != ''
        run: scripts/ci/notify-telegram.sh ok ci-deploy

      - name: Notify (failure)
        if: failure() && env.TELEGRAM_BOT_TOKEN != ''
        run: scripts/ci/notify-telegram.sh fail ci-deploy "see run for details" container.log
