name: Build & Deploy

on:
  push:
    branches: [develop]
  workflow_dispatch:

concurrency:
  group: build-${{ gitea.ref }}
  cancel-in-progress: false

env:
  NODE_VERSION: "20"
  # TODO: Remove after installing internal CA certificate on the runner
  GIT_SSL_NO_VERIFY: "true"
  REGISTRY_HOST: 192.168.3.80
  DEPLOY_REGISTRY_HOST: 10.0.0.4
  DOCKERFILE_PATH: Dockerfile.production
  DOCKER_PLATFORM: linux/amd64
  DEPLOY_ROOT: /opt/rentaldrivego

jobs:
  build-image:
    name: Build & Push Docker Image
    runs-on: ubuntu-latest
    outputs:
      image_repository: ${{ steps.image-meta.outputs.repository }}
      docker_image: ${{ steps.image-meta.outputs.full }}
    steps:
      - uses: actions/checkout@v4

      - name: Ensure Docker CLI is available
        run: |
          if ! command -v docker >/dev/null 2>&1; then
            apt-get update -qq
            apt-get install -y -qq docker.io
          fi
          docker --version
          docker info >/dev/null

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3
        with:
          buildkitd-config-inline: |
            [registry."192.168.3.80"]
              insecure = true

      - name: Docker image metadata
        id: image-meta
        run: |
          REPO="${{ gitea.repository }}"
          TAG="${{ gitea.sha }}"
          echo "repository=${REPO}" >> "$GITHUB_OUTPUT"
          echo "full=${REGISTRY_HOST}/${REPO}:${TAG}" >> "$GITHUB_OUTPUT"
          echo "latest=${REGISTRY_HOST}/${REPO}:latest" >> "$GITHUB_OUTPUT"

      - name: Check Docker registry credentials
        id: registry-check
        run: |
          if [ -n "${{ secrets.REGISTRY_USERNAME }}" ] && [ -n "${{ secrets.REGISTRY_PASSWORD }}" ]; then
            echo "available=true" >> "$GITHUB_OUTPUT"
          else
            echo "::warning::REGISTRY_USERNAME or REGISTRY_PASSWORD secrets not set — push will be skipped"
            echo "available=false" >> "$GITHUB_OUTPUT"
          fi

      - name: Validate public URLs
        shell: bash
        env:
          NEXT_PUBLIC_API_URL: ${{ secrets.NEXT_PUBLIC_API_URL }}
          NEXT_PUBLIC_HOMEPAGE_URL: ${{ secrets.NEXT_PUBLIC_HOMEPAGE_URL }}
          NEXT_PUBLIC_STOREFRONT_URL: ${{ secrets.NEXT_PUBLIC_STOREFRONT_URL }}
          NEXT_PUBLIC_DASHBOARD_URL: ${{ secrets.NEXT_PUBLIC_DASHBOARD_URL }}
          NEXT_PUBLIC_ADMIN_URL: ${{ secrets.NEXT_PUBLIC_ADMIN_URL }}
          SITE_ORIGIN: ${{ secrets.SITE_ORIGIN }}
        run: |
          for variable in \
            NEXT_PUBLIC_API_URL \
            NEXT_PUBLIC_HOMEPAGE_URL \
            NEXT_PUBLIC_STOREFRONT_URL \
            NEXT_PUBLIC_DASHBOARD_URL \
            NEXT_PUBLIC_ADMIN_URL \
            SITE_ORIGIN
          do
            value="${!variable}"
            if [ -z "$value" ]; then
              echo "::error::$variable is missing — add it to Gitea Actions secrets"
              exit 1
            fi
            case "$value" in
              http://*|https://*) ;;
              *)
                echo "::error::$variable must be an absolute URL (got: $value)"
                exit 1
                ;;
            esac
          done

      - name: Log in to Gitea Container Registry
        if: steps.registry-check.outputs.available == 'true'
        uses: docker/login-action@v3
        with:
          registry: ${{ env.REGISTRY_HOST }}
          username: ${{ secrets.REGISTRY_USERNAME }}
          password: ${{ secrets.REGISTRY_PASSWORD }}

      - name: Build and push
        uses: docker/build-push-action@v6
        env:
          BUILDKIT_NO_CLIENT_TOKEN: "true"
        with:
          context: .
          file: ${{ env.DOCKERFILE_PATH }}
          platforms: ${{ env.DOCKER_PLATFORM }}
          push: ${{ steps.registry-check.outputs.available == 'true' }}
          tags: |
            ${{ steps.image-meta.outputs.full }}
            ${{ steps.image-meta.outputs.latest }}
          build-args: |
            API_INTERNAL_URL=http://api:4000/api/v1
            DASHBOARD_INTERNAL_URL=http://dashboard:3001
            ADMIN_INTERNAL_URL=http://admin:3002
            NEXT_PUBLIC_API_URL=${{ secrets.NEXT_PUBLIC_API_URL }}
            NEXT_PUBLIC_HOMEPAGE_URL=${{ secrets.NEXT_PUBLIC_HOMEPAGE_URL }}
            NEXT_PUBLIC_STOREFRONT_URL=${{ secrets.NEXT_PUBLIC_STOREFRONT_URL }}
            NEXT_PUBLIC_DASHBOARD_URL=${{ secrets.NEXT_PUBLIC_DASHBOARD_URL }}
            NEXT_PUBLIC_ADMIN_URL=${{ secrets.NEXT_PUBLIC_ADMIN_URL }}
            SITE_ORIGIN=${{ secrets.SITE_ORIGIN }}

  deploy:
    name: Deploy to VPS
    runs-on: ubuntu-latest
    needs: [build-image]
    env:
      DOCKER_IMAGE: ${{ needs.build-image.outputs.docker_image }}
      IMAGE_REPOSITORY: ${{ needs.build-image.outputs.image_repository }}
    steps:
      - uses: actions/checkout@v4

      - name: Check deploy credentials
        id: check-creds
        run: |
          if [ -z "${{ secrets.VPS_SSH_KEY }}" ] || \
             [ -z "${{ secrets.VPS_IP }}" ] || \
             [ -z "${{ secrets.VPS_USER }}" ]; then
            echo "VPS secrets not fully configured — skipping deploy"
            echo "skip=true" >> "$GITHUB_OUTPUT"
          else
            echo "skip=false" >> "$GITHUB_OUTPUT"
          fi

      - name: Install SSH client
        if: steps.check-creds.outputs.skip == 'false'
        run: |
          apt-get update -qq && apt-get install -y -qq openssh-client

      - name: Set up SSH key
        if: steps.check-creds.outputs.skip == 'false'
        run: |
          mkdir -p ~/.ssh && chmod 700 ~/.ssh
          echo "${{ secrets.VPS_SSH_KEY }}" > ~/.ssh/id_rsa
          chmod 600 ~/.ssh/id_rsa
          ssh-keyscan -H "${{ secrets.VPS_IP }}" >> ~/.ssh/known_hosts 2>/dev/null
          chmod 644 ~/.ssh/known_hosts

      - name: Sync deployment assets
        if: steps.check-creds.outputs.skip == 'false'
        run: |
          ssh "${{ secrets.VPS_USER }}@${{ secrets.VPS_IP }}" \
            "mkdir -p '$DEPLOY_ROOT/scripts' '$DEPLOY_ROOT/docker/pgmanage' '$DEPLOY_ROOT/docker/registry/auth' '$DEPLOY_ROOT/dynamic'"
          scp docker-compose.production.yml \
              docker-compose.portainer.production.yml \
              docker-compose.registry.production.yml \
              docker-compose.registry.local.yml \
              traefik.yaml \
              "${{ secrets.VPS_USER }}@${{ secrets.VPS_IP }}:$DEPLOY_ROOT/"
          scp scripts/docker-prod-common.sh \
              scripts/docker-prod-deploy.sh \
              scripts/docker-prod-up-registry.sh \
              scripts/docker-registry-local-up.sh \
              "${{ secrets.VPS_USER }}@${{ secrets.VPS_IP }}:$DEPLOY_ROOT/scripts/"
          scp docker/pgmanage/override.py \
              "${{ secrets.VPS_USER }}@${{ secrets.VPS_IP }}:$DEPLOY_ROOT/docker/pgmanage/"

      - name: Run deploy script on VPS
        if: steps.check-creds.outputs.skip == 'false'
        run: |
          REGISTRY_PASSWORD_B64="$(printf '%s' "${{ secrets.REGISTRY_PASSWORD }}" | base64 | tr -d '\n')"
          ssh "${{ secrets.VPS_USER }}@${{ secrets.VPS_IP }}" "
            set -e
            cd '$DEPLOY_ROOT'
            APP_IMAGE='$DEPLOY_REGISTRY_HOST/$IMAGE_REPOSITORY' \
            APP_VERSION='${{ gitea.sha }}' \
            IMAGE_TAG='${{ gitea.sha }}' \
            REGISTRY_HOST='$DEPLOY_REGISTRY_HOST' \
            REGISTRY_USER='${{ secrets.REGISTRY_USERNAME }}' \
            REGISTRY_PASSWORD=\$(printf '%s' '$REGISTRY_PASSWORD_B64' | base64 -d) \
            bash scripts/docker-prod-deploy.sh
          "
