name: Release

on:
  push:
    tags:
      - "v*"   # e.g. v1.0.0, v1.2.3

concurrency:
  group: ${{ gitea.workflow }}-${{ gitea.ref }}
  cancel-in-progress: true

jobs:
  # ── backend: vet & test ───────────────────────────────────────────────────────
  test-backend:
    name: Test backend
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-go@v5
        with:
          go-version-file: backend/go.mod
          cache-dependency-path: backend/go.sum

      - name: go vet
        working-directory: backend
        run: go vet ./...

      - name: Run tests
        working-directory: backend
        run: go test -short -race -count=1 -timeout=60s ./...

  # ── ui: type-check & build ────────────────────────────────────────────────────
  check-ui:
    name: Check ui
    runs-on: ubuntu-latest
    defaults:
      run:
        working-directory: ui
    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-node@v4
        with:
          node-version: "22"
          cache: npm
          cache-dependency-path: ui/package-lock.json

      - name: Install dependencies
        run: npm ci

      - name: Type check
        run: npm run check

      - name: Build
        run: npm run build

      - name: Upload build artifacts
        uses: actions/upload-artifact@v3
        with:
          name: ui-build
          path: ui/build
          retention-days: 1

  # ── docker: backend ───────────────────────────────────────────────────────────
  docker-backend:
    name: Docker / backend
    runs-on: ubuntu-latest
    needs: [test-backend]
    steps:
      - uses: actions/checkout@v4

      - uses: docker/setup-buildx-action@v3

      - name: Log in to Docker Hub
        uses: docker/login-action@v3
        with:
          username: ${{ secrets.DOCKER_USER }}
          password: ${{ secrets.DOCKER_TOKEN }}

      - name: Docker meta
        id: meta
        uses: docker/metadata-action@v5
        with:
          images: ${{ secrets.DOCKER_USER }}/libnovel-backend
          tags: |
            type=semver,pattern={{version}}
            type=semver,pattern={{major}}.{{minor}}
            type=raw,value=latest

      - name: Build and push
        uses: docker/build-push-action@v6
        with:
          context: backend
          target: backend
          push: true
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}
          build-args: |
            VERSION=${{ steps.meta.outputs.version }}
            COMMIT=${{ gitea.sha }}
          cache-from: type=registry,ref=${{ secrets.DOCKER_USER }}/libnovel-backend:latest
          cache-to: type=inline

  # ── docker: runner ────────────────────────────────────────────────────────────
  docker-runner:
    name: Docker / runner
    runs-on: ubuntu-latest
    needs: [test-backend]
    steps:
      - uses: actions/checkout@v4

      - uses: docker/setup-buildx-action@v3

      - name: Log in to Docker Hub
        uses: docker/login-action@v3
        with:
          username: ${{ secrets.DOCKER_USER }}
          password: ${{ secrets.DOCKER_TOKEN }}

      - name: Docker meta
        id: meta
        uses: docker/metadata-action@v5
        with:
          images: ${{ secrets.DOCKER_USER }}/libnovel-runner
          tags: |
            type=semver,pattern={{version}}
            type=semver,pattern={{major}}.{{minor}}
            type=raw,value=latest

      - name: Build and push
        uses: docker/build-push-action@v6
        with:
          context: backend
          target: runner
          push: true
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}
          build-args: |
            VERSION=${{ steps.meta.outputs.version }}
            COMMIT=${{ gitea.sha }}
          cache-from: type=registry,ref=${{ secrets.DOCKER_USER }}/libnovel-runner:latest
          cache-to: type=inline

  # ── ui: source map upload ─────────────────────────────────────────────────────
  upload-sourcemaps:
    name: Upload source maps
    runs-on: ubuntu-latest
    needs: [check-ui]
    steps:
      - name: Compute release version (strip leading v)
        id: ver
        run: |
          V="${{ gitea.ref_name }}"
          echo "version=${V#v}" >> "$GITHUB_OUTPUT"

      - name: Download build artifacts
        uses: actions/download-artifact@v3
        with:
          name: ui-build
          path: build

      - name: Install sentry-cli
        run: npm install -g @sentry/cli

      - name: Inject debug IDs into build artifacts
        run: sentry-cli sourcemaps inject ./build
        env:
          SENTRY_URL: https://errors.libnovel.cc/
          SENTRY_AUTH_TOKEN: ${{ secrets.GLITCHTIP_AUTH_TOKEN }}
          SENTRY_ORG: libnovel
          SENTRY_PROJECT: ui

      - name: Upload injected build (for docker-ui)
        uses: actions/upload-artifact@v3
        with:
          name: ui-build-injected
          path: build
          retention-days: 1

      - name: Create GlitchTip release
        run: sentry-cli releases new ${{ steps.ver.outputs.version }}
        env:
          SENTRY_URL: https://errors.libnovel.cc/
          SENTRY_AUTH_TOKEN: ${{ secrets.GLITCHTIP_AUTH_TOKEN }}
          SENTRY_ORG: libnovel
          SENTRY_PROJECT: ui

      - name: Upload source maps to GlitchTip
        run: sentry-cli sourcemaps upload ./build --release ${{ steps.ver.outputs.version }}
        env:
          SENTRY_URL: https://errors.libnovel.cc/
          SENTRY_AUTH_TOKEN: ${{ secrets.GLITCHTIP_AUTH_TOKEN }}
          SENTRY_ORG: libnovel
          SENTRY_PROJECT: ui

      - name: Finalize GlitchTip release
        run: sentry-cli releases finalize ${{ steps.ver.outputs.version }}
        env:
          SENTRY_URL: https://errors.libnovel.cc/
          SENTRY_AUTH_TOKEN: ${{ secrets.GLITCHTIP_AUTH_TOKEN }}
          SENTRY_ORG: libnovel
          SENTRY_PROJECT: ui

      - name: Prune old GlitchTip releases (keep latest 10)
        run: |
          set -euo pipefail
          KEEP=10
          OLD=$(curl -sf \
            -H "Authorization: Bearer $SENTRY_AUTH_TOKEN" \
            "$SENTRY_URL/api/0/organizations/$SENTRY_ORG/releases/?project=$SENTRY_PROJECT&per_page=100" \
            | python3 -c "
          import sys, json
          releases = json.load(sys.stdin)
          for r in releases[$KEEP:]:
              print(r['version'])
          " KEEP=$KEEP)
          for ver in $OLD; do
            echo "Deleting old release: $ver"
            sentry-cli releases delete "$ver" || true
          done
        env:
          SENTRY_URL: https://errors.libnovel.cc
          SENTRY_AUTH_TOKEN: ${{ secrets.GLITCHTIP_AUTH_TOKEN }}
          SENTRY_ORG: libnovel
          SENTRY_PROJECT: ui

  # ── docker: ui ────────────────────────────────────────────────────────────────
  docker-ui:
    name: Docker / ui
    runs-on: ubuntu-latest
    needs: [check-ui, upload-sourcemaps]
    steps:
      - uses: actions/checkout@v4

      - name: Download injected build (debug IDs already embedded)
        uses: actions/download-artifact@v3
        with:
          name: ui-build-injected
          path: ui/build

      - name: Allow build/ into Docker context (override .dockerignore)
        run: |
          grep -v '^build$' ui/.dockerignore > ui/.dockerignore.tmp
          mv ui/.dockerignore.tmp ui/.dockerignore

      - uses: docker/setup-buildx-action@v3

      - name: Log in to Docker Hub
        uses: docker/login-action@v3
        with:
          username: ${{ secrets.DOCKER_USER }}
          password: ${{ secrets.DOCKER_TOKEN }}

      - name: Docker meta
        id: meta
        uses: docker/metadata-action@v5
        with:
          images: ${{ secrets.DOCKER_USER }}/libnovel-ui
          tags: |
            type=semver,pattern={{version}}
            type=semver,pattern={{major}}.{{minor}}
            type=raw,value=latest

      - name: Build and push
        uses: docker/build-push-action@v6
        with:
          context: ui
          push: true
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}
          build-args: |
            BUILD_VERSION=${{ steps.meta.outputs.version }}
            BUILD_COMMIT=${{ gitea.sha }}
            BUILD_TIME=${{ gitea.event.head_commit.timestamp }}
            PREBUILT=1
          cache-from: type=registry,ref=${{ secrets.DOCKER_USER }}/libnovel-ui:latest
          cache-to: type=inline

  # ── docker: caddy ─────────────────────────────────────────────────────────────
  docker-caddy:
    name: Docker / caddy
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: docker/setup-buildx-action@v3

      - name: Log in to Docker Hub
        uses: docker/login-action@v3
        with:
          username: ${{ secrets.DOCKER_USER }}
          password: ${{ secrets.DOCKER_TOKEN }}

      - name: Docker meta
        id: meta
        uses: docker/metadata-action@v5
        with:
          images: ${{ secrets.DOCKER_USER }}/libnovel-caddy
          tags: |
            type=semver,pattern={{version}}
            type=semver,pattern={{major}}.{{minor}}
            type=raw,value=latest

      - name: Build and push
        uses: docker/build-push-action@v6
        with:
          context: caddy
          push: true
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}
          cache-from: type=registry,ref=${{ secrets.DOCKER_USER }}/libnovel-caddy:latest
          cache-to: type=inline

  # ── Gitea release ─────────────────────────────────────────────────────────────
  release:
    name: Gitea Release
    runs-on: ubuntu-latest
    needs: [docker-backend, docker-runner, docker-ui, docker-caddy, upload-sourcemaps]
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Extract release notes from tag commit
        id: notes
        run: |
          set -euo pipefail
          # Subject line (first line of commit message) → release title
          SUBJECT=$(git log -1 --format="%s" "${{ gitea.sha }}")
          # Body (everything after the blank line) → release body
          BODY=$(git log -1 --format="%b" "${{ gitea.sha }}" | sed '/^Co-Authored-By:/d' | sed '/^[[:space:]]*$/{ N; /^\n$/d }' | sed 's/^[[:space:]]*$//' | awk 'NF || !p; {p = !NF}')
          echo "title=${SUBJECT}" >> "$GITHUB_OUTPUT"
          # Use a heredoc delimiter to safely handle multi-line body
          {
            echo "body<<RELEASE_BODY_EOF"
            echo "${BODY}"
            echo "RELEASE_BODY_EOF"
          } >> "$GITHUB_OUTPUT"

      - name: Create release
        uses: https://gitea.com/actions/gitea-release-action@v1
        with:
          token: ${{ secrets.GITEA_TOKEN }}
          title: ${{ steps.notes.outputs.title }}
          body: ${{ steps.notes.outputs.body }}
