name: Docker images

on:
  push:
    branches:
      - main
  workflow_dispatch:

env:
  REGISTRY: git.shx.one
  GITEA_SERVER_URL: https://git.shx.one

jobs:
  prepare-release:
    runs-on: ubuntu-latest
    outputs:
      should_release: ${{ steps.release.outputs.should_release }}
      version: ${{ steps.release.outputs.version }}
      tag: ${{ steps.release.outputs.tag }}
      bump: ${{ steps.release.outputs.bump }}
      release_url: ${{ steps.release.outputs.release_url }}
    steps:
      - name: Checkout
        uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Compute release metadata
        id: release
        shell: bash
        env:
          GITEA_REPOSITORY: ${{ gitea.repository }}
          GITEA_SERVER_URL: ${{ env.GITEA_SERVER_URL }}
          BASE_VERSION: "1.0.0"
          RELEASE_OUTPUT_DIR: .ci/release
        run: |
          set -euo pipefail
          node .ci/scripts/compute-release.mjs
          test -f .ci/release/release-manifest.json

  backend-image:
    needs: prepare-release
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Configure backend image name
        shell: bash
        run: |
          set -euo pipefail
          owner=$(echo "${{ gitea.repository }}" | cut -d/ -f1 | tr '[:upper:]' '[:lower:]')
          name=$(echo "${{ gitea.repository }}" | cut -d/ -f2 | tr '[:upper:]' '[:lower:]')
          stem=$(printf '%s' "$name" | sed -E 's/-[0-9]+$//')
          echo "IMAGE_NAME=${{ env.REGISTRY }}/${owner}/${stem}-backend" >> "${GITHUB_ENV}"

      - name: Prepare backend build context
        shell: bash
        run: |
          set -euo pipefail
          STAGING=".ci/docker/backend"
          rm -rf "$STAGING"
          mkdir -p "$STAGING/packages/contracts" "$STAGING/backend"
          cp package-lock.json "$STAGING/"
          node <<'NODE'
          const fs = require("node:fs")
          const pkg = JSON.parse(fs.readFileSync("package.json", "utf8"))
          pkg.workspaces = ["packages/*", "backend"]
          pkg.dependencies = {}
          pkg.devDependencies = {}
          delete pkg.scripts
          fs.writeFileSync(".ci/docker/backend/package.json", `${JSON.stringify(pkg, null, 2)}\n`)
          NODE
          cp packages/contracts/package.json packages/contracts/tsconfig.json "$STAGING/packages/contracts/"
          cp -R packages/contracts/src "$STAGING/packages/contracts/"
          cp backend/package.json backend/tsconfig.json "$STAGING/backend/"
          cp -R backend/src "$STAGING/backend/"
          if [ -d backend/drizzle ]; then
            cp -R backend/drizzle "$STAGING/backend/"
          fi


      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3

      - name: Log in to Gitea Registry
        uses: docker/login-action@v3
        with:
          registry: git.shx.one
          username: ${{ gitea.actor }}
          password: ${{ secrets.ACTIONS_PAT }}

      - name: Build and push backend image
        uses: docker/build-push-action@v5
        with:
          context: .ci/docker/backend
          file: backend/Dockerfile
          platforms: linux/amd64
          push: true
          build-args: |
            APP_VERSION=${{ needs.prepare-release.outputs.version }}
          tags: |
            ${{ env.IMAGE_NAME }}:latest
            ${{ env.IMAGE_NAME }}:${{ gitea.sha }}
            ${{ env.IMAGE_NAME }}:${{ needs.prepare-release.outputs.version }}
          cache-from: type=gha,scope=backend
          cache-to: type=gha,mode=max,scope=backend
          labels: |
            org.opencontainers.image.title=mmapp-backend
            org.opencontainers.image.description=MikroTik Manager backend (Fastify)
            org.opencontainers.image.version=${{ needs.prepare-release.outputs.version }}
            org.opencontainers.image.revision=${{ gitea.sha }}
            org.opencontainers.image.created=${{ gitea.event.head_commit.timestamp }}

  frontend-image:
    needs: prepare-release
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Compute release metadata
        shell: bash
        env:
          GITEA_REPOSITORY: ${{ gitea.repository }}
          GITEA_SERVER_URL: ${{ env.GITEA_SERVER_URL }}
          BASE_VERSION: "1.0.0"
          RELEASE_OUTPUT_DIR: .ci/release
        run: |
          set -euo pipefail
          node .ci/scripts/compute-release.mjs
          test -f .ci/release/release-manifest.json

      - name: Configure frontend image name
        shell: bash
        run: |
          set -euo pipefail
          owner=$(echo "${{ gitea.repository }}" | cut -d/ -f1 | tr '[:upper:]' '[:lower:]')
          name=$(echo "${{ gitea.repository }}" | cut -d/ -f2 | tr '[:upper:]' '[:lower:]')
          stem=$(printf '%s' "$name" | sed -E 's/-[0-9]+$//')
          echo "IMAGE_NAME=${{ env.REGISTRY }}/${owner}/${stem}-frontend" >> "${GITHUB_ENV}"

      - name: Prepare frontend build context
        shell: bash
        run: |
          set -euo pipefail
          STAGING=".ci/docker/frontend"
          rm -rf "$STAGING"
          mkdir -p "$STAGING/packages/contracts" "$STAGING/public"
          cp package-lock.json next.config.ts tsconfig.json postcss.config.mjs eslint.config.mjs components.json "$STAGING/"
          node <<'NODE'
          const fs = require("node:fs")
          const pkg = JSON.parse(fs.readFileSync("package.json", "utf8"))
          pkg.workspaces = ["packages/*"]
          fs.writeFileSync(".ci/docker/frontend/package.json", `${JSON.stringify(pkg, null, 2)}\n`)
          NODE
          cp packages/contracts/package.json packages/contracts/tsconfig.json "$STAGING/packages/contracts/"
          cp -R packages/contracts/src "$STAGING/packages/contracts/"
          for dir in app components lib shared entities public hooks; do
            if [ -d "$dir" ]; then
              cp -R "$dir" "$STAGING/"
            fi
          done
          cp .ci/release/release-manifest.json "$STAGING/public/release-manifest.json"

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3

      - name: Log in to Gitea Registry
        uses: docker/login-action@v3
        with:
          registry: git.shx.one
          username: ${{ gitea.actor }}
          password: ${{ secrets.ACTIONS_PAT }}

      - name: Build and push frontend image
        uses: docker/build-push-action@v5
        with:
          context: .ci/docker/frontend
          file: Dockerfile.frontend
          platforms: linux/amd64
          push: true
          build-args: |
            BACKEND_INTERNAL_URL=http://backend:8000
            NEXT_PUBLIC_BACKEND_URL=same-origin
            NEXT_PUBLIC_DEFAULT_DATA_SOURCE=live
            NEXT_PUBLIC_ALLOW_MOCK_DATA=false
            NEXT_PUBLIC_AUTH_PORTAL_URL=https://auth.shnt.top
            NEXT_PUBLIC_APP_VERSION=${{ needs.prepare-release.outputs.version }}
            NEXT_PUBLIC_RELEASE_URL=${{ needs.prepare-release.outputs.release_url }}
          tags: |
            ${{ env.IMAGE_NAME }}:latest
            ${{ env.IMAGE_NAME }}:${{ gitea.sha }}
            ${{ env.IMAGE_NAME }}:${{ needs.prepare-release.outputs.version }}
          cache-from: type=gha,scope=frontend
          cache-to: type=gha,mode=max,scope=frontend
          labels: |
            org.opencontainers.image.title=mmapp-frontend
            org.opencontainers.image.description=MikroTik Manager frontend (Next.js)
            org.opencontainers.image.version=${{ needs.prepare-release.outputs.version }}
            org.opencontainers.image.revision=${{ gitea.sha }}
            org.opencontainers.image.created=${{ gitea.event.head_commit.timestamp }}

  updater-image:
    needs: prepare-release
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Configure updater image name
        shell: bash
        run: |
          set -euo pipefail
          owner=$(echo "${{ gitea.repository }}" | cut -d/ -f1 | tr '[:upper:]' '[:lower:]')
          name=$(echo "${{ gitea.repository }}" | cut -d/ -f2 | tr '[:upper:]' '[:lower:]')
          stem=$(printf '%s' "$name" | sed -E 's/-[0-9]+$//')
          echo "IMAGE_NAME=${{ env.REGISTRY }}/${owner}/${stem}-updater" >> "${GITHUB_ENV}"

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3

      - name: Log in to Gitea Registry
        uses: docker/login-action@v3
        with:
          registry: git.shx.one
          username: ${{ gitea.actor }}
          password: ${{ secrets.ACTIONS_PAT }}

      - name: Build and push updater image
        uses: docker/build-push-action@v5
        with:
          context: deploy/updater
          file: deploy/updater/Dockerfile
          platforms: linux/amd64
          push: true
          tags: |
            ${{ env.IMAGE_NAME }}:latest
            ${{ env.IMAGE_NAME }}:${{ gitea.sha }}
            ${{ env.IMAGE_NAME }}:${{ needs.prepare-release.outputs.version }}
          cache-from: type=gha,scope=updater
          cache-to: type=gha,mode=max,scope=updater
          labels: |
            org.opencontainers.image.title=mmapp-updater
            org.opencontainers.image.description=MikroTik Manager autonomous Docker updater
            org.opencontainers.image.version=${{ needs.prepare-release.outputs.version }}
            org.opencontainers.image.revision=${{ gitea.sha }}
            org.opencontainers.image.created=${{ gitea.event.head_commit.timestamp }}

  publish-release:
    needs:
      - prepare-release
      - backend-image
      - frontend-image
      - updater-image
    if: needs.prepare-release.outputs.should_release == 'true'
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Compute release metadata
        shell: bash
        env:
          GITEA_REPOSITORY: ${{ gitea.repository }}
          GITEA_SERVER_URL: ${{ env.GITEA_SERVER_URL }}
          BASE_VERSION: "1.0.0"
          RELEASE_OUTPUT_DIR: .ci/release
        run: |
          set -euo pipefail
          node .ci/scripts/compute-release.mjs
          test -f .ci/release/release_notes.md

      - name: Configure git author
        shell: bash
        run: |
          set -euo pipefail
          git config user.name "gitea-actions[bot]"
          git config user.email "gitea-actions[bot]@users.noreply.gitea"

      - name: Create and push release tag
        shell: bash
        env:
          TAG: ${{ needs.prepare-release.outputs.tag }}
        run: |
          set -euo pipefail
          if git rev-parse "$TAG" >/dev/null 2>&1; then
            echo "Tag $TAG already exists, skipping tag push"
            exit 0
          fi
          git tag -a "$TAG" -m "Release $TAG"
          git push origin "$TAG"

      - name: Publish Gitea release
        shell: bash
        env:
          TAG: ${{ needs.prepare-release.outputs.tag }}
          VERSION: ${{ needs.prepare-release.outputs.version }}
          REPOSITORY: ${{ gitea.repository }}
          TOKEN: ${{ secrets.ACTIONS_PAT }}
        run: |
          set -euo pipefail
          owner="${REPOSITORY%%/*}"
          repo="${REPOSITORY#*/}"
          payload=$(node <<'NODE'
          const fs = require("node:fs")
          const body = fs.readFileSync(".ci/release/release_notes.md", "utf8")
          const payload = {
            tag_name: process.env.TAG,
            target_commitish: "main",
            name: process.env.VERSION,
            body,
            draft: false,
            prerelease: false,
          }
          process.stdout.write(JSON.stringify(payload))
          NODE
          )
          status=$(curl --silent --show-error --output /tmp/gitea-release.json --write-out "%{http_code}" \
            --request POST \
            --header "Authorization: token ${TOKEN}" \
            --header "Content-Type: application/json" \
            --data "$payload" \
            "${{ env.GITEA_SERVER_URL }}/api/v1/repos/${owner}/${repo}/releases")
          if [ "$status" = "409" ] || [ "$status" = "422" ]; then
            echo "Release for ${TAG} already exists (${status}), skipping"
            exit 0
          fi
          if [ "$status" -lt 200 ] || [ "$status" -ge 300 ]; then
            cat /tmp/gitea-release.json
            exit 1
          fi

  notify-webhook:
    if: ${{ secrets.DEPLOY_WEBHOOK_URL != '' }}
    needs:
      - prepare-release
      - backend-image
      - frontend-image
    runs-on: ubuntu-latest
    steps:
      - name: Notify deploy webhook
        shell: bash
        env:
          WEBHOOK_URL: ${{ secrets.DEPLOY_WEBHOOK_URL }}
        run: |
          set -euo pipefail
          curl --fail --silent --show-error --request POST \
            --header "Content-Type: application/json" \
            --data "{\"repository\":\"${{ gitea.repository }}\",\"sha\":\"${{ gitea.sha }}\",\"ref\":\"${{ gitea.ref }}\",\"version\":\"${{ needs.prepare-release.outputs.version }}\",\"tag\":\"${{ needs.prepare-release.outputs.tag }}\",\"releaseUrl\":\"${{ needs.prepare-release.outputs.release_url }}\"}" \
            "$WEBHOOK_URL"
