name: Deploy Stacks
on:
  push:
    branches:
      - dev
      - staging
  workflow_dispatch:

permissions:
  contents: read

jobs:
  detect-changes:
    name: Detect changed services
    runs-on: ${{ fromJson(format('["self-hosted", "{0}"]', github.ref_name)) }}
    outputs:
      matrix: ${{ steps.filter.outputs.matrix }}
    steps:
      - name: Checkout
        uses: actions/checkout@v4
        with:
          fetch-depth: 2

      - name: Determine changed services
        id: filter
        run: |
          set -euo pipefail

          ALL_SERVICES=(
            "freight-api"
            "freight-portal"
            "freight-backoffice"
            "gps-tracker"
            "passenger-api"
            "passenger-portal"
            "passenger-backoffice"
            "payment-api"
          )

          if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
            JSON=$(printf '%s\n' "${ALL_SERVICES[@]}" | jq -R . | jq -sc .)
            echo "matrix=${JSON}" >> "$GITHUB_OUTPUT"
            exit 0
          fi

          CHANGED=$(git diff --name-only HEAD~1 HEAD)
          echo "=== Changed files ==="
          echo "$CHANGED"
          echo "====================="

          SERVICES=()

          NON_DEPLOYABLE_PATTERN="^docs/|^README[.]md$|^DEPLOYMENT[.]md$|^CLAUDE[.]md$|^checkpoint[.]md$|^orgstructure[.]md$|^ITMLS_DB_Design[.]md$|.*[.]md$|^[.]eslintrc|^[.]prettierrc|^[.]editorconfig|^[.]gitignore|^[.]gitattributes|^commitlint[.]config[.]js$|^scripts/deploy/sync-env-from-server-jenkins[.]sh$"

          GLOBAL_PATTERN="^[.]github/|^docker-compose[.]yaml$|^turbo[.]json$|^tsconfig[.]json$|^tsconfig[.]base[.]json$|^pnpm-workspace[.]yaml$|^pnpm-lock[.]yaml$|^package[.]json$|^[.]env([.][a-z]+)?$|^packages/|^local-packages/|^infrastructure/|^scripts/deploy/|^wagon[.][^/]*[.]ts$|^cargo[.][^/]*[.]ts$|^container[.][^/]*[.]ts$|^use-[^/]*[.]ts$|^[^/]*[.]service[.]ts$|^[^/]*[.]entity[.]ts$|^[^/]*-types[.]ts$"

          DEPLOYABLE=$(echo "$CHANGED" | grep -vE "$NON_DEPLOYABLE_PATTERN" || true)
          if [ -z "$DEPLOYABLE" ]; then
            echo "Only non-deployable files changed. Skipping deploy."
            echo "matrix=[]" >> "$GITHUB_OUTPUT"
            exit 0
          fi

          if echo "$CHANGED" | grep -qE "$GLOBAL_PATTERN"; then
            echo "Global file(s) changed — deploying all services."
            JSON=$(printf '%s\n' "${ALL_SERVICES[@]}" | jq -R . | jq -sc .)
            echo "matrix=${JSON}" >> "$GITHUB_OUTPUT"
            exit 0
          fi

          echo "$CHANGED" | grep -q "^apps/edr-freight-api/"              && SERVICES+=("freight-api")
          echo "$CHANGED" | grep -q "^apps/edr-freight-web/portal/"       && SERVICES+=("freight-portal")
          echo "$CHANGED" | grep -q "^apps/edr-freight-web/backoffice/"   && SERVICES+=("freight-backoffice")
          echo "$CHANGED" | grep -q "^apps/edr-gps-tracker/"              && SERVICES+=("gps-tracker")
          echo "$CHANGED" | grep -q "^apps/edr-passenger-api/"            && SERVICES+=("passenger-api")
          echo "$CHANGED" | grep -q "^apps/edr-passenger-web/portal/"     && SERVICES+=("passenger-portal")
          echo "$CHANGED" | grep -q "^apps/edr-passenger-web/backoffice/" && SERVICES+=("passenger-backoffice")
          echo "$CHANGED" | grep -q "^apps/edr-payment-api/"              && SERVICES+=("payment-api")

          SERVICES=($(printf '%s\n' "${SERVICES[@]}" | sort -u))

          if [ ${#SERVICES[@]} -eq 0 ]; then
            echo "No deployable service changes detected."
            echo "matrix=[]" >> "$GITHUB_OUTPUT"
          else
            echo "Services to deploy: ${SERVICES[*]}"
            JSON=$(printf '%s\n' "${SERVICES[@]}" | jq -R . | jq -sc .)
            echo "matrix=${JSON}" >> "$GITHUB_OUTPUT"
          fi

  deploy:
    name: Deploy ${{ matrix.service }}
    needs: detect-changes
    if: ${{ needs.detect-changes.outputs.matrix != '[]' }}
    runs-on: ${{ fromJson(format('["self-hosted", "{0}"]', github.ref_name)) }}
    strategy:
      fail-fast: false
      matrix:
        service: ${{ fromJson(needs.detect-changes.outputs.matrix) }}
    env:
      BRANCH: ${{ github.ref_name }}
      DEPLOY_USER: tria
      DOCKER_BUILDKIT: "1"
      COMPOSE_DOCKER_CLI_BUILD: "1"

    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Resolve project and build env file
        run: |
          case "${{ matrix.service }}" in
            freight-api|freight-portal|freight-backoffice|gps-tracker)
              echo "PROJECT=edr-freight"                      >> "$GITHUB_ENV"
              echo "BUILD_ENV_FILE=freight-web.build.env"    >> "$GITHUB_ENV"
              ;;
            passenger-api|passenger-portal|passenger-backoffice)
              echo "PROJECT=edr-passenger"                   >> "$GITHUB_ENV"
              echo "BUILD_ENV_FILE=passenger-web.build.env"  >> "$GITHUB_ENV"
              ;;
            payment-api)
              echo "PROJECT=edr-payment"                     >> "$GITHUB_ENV"
              echo "BUILD_ENV_FILE=payment-web.build.env"    >> "$GITHUB_ENV"
              ;;
            *)
              echo "Unknown service: ${{ matrix.service }}" && exit 1
              ;;
          esac

      - name: Sync environment from server
        run: |
          chmod +x scripts/deploy/*.sh
          ./scripts/deploy/sync-env-from-server.sh "${{ matrix.service }}"

      - name: Set compose project name
        run: |
          set -euo pipefail
          branch_slug=$(echo "${BRANCH}" | tr "[:upper:]" "[:lower:]" | sed -E "s/[^a-z0-9]+/-/g; s/^-+//; s/-+$//")
          echo "COMPOSE_PROJECT_NAME=${PROJECT}-${branch_slug}" >> "${GITHUB_ENV}"

      - name: Configure npm auth for Docker builds
        env:
          NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
        run: ./scripts/deploy/create-npmrc.sh

      - name: Resolve env file path for ${{ matrix.service }}
        if: contains(fromJson('["passenger-api", "payment-api"]'), matrix.service)
        run: |
          case "${{ matrix.service }}" in
            passenger-api) echo "SERVICE_ENV_FILE=apps/edr-passenger-api/.env" >> "$GITHUB_ENV" ;;
            payment-api)   echo "SERVICE_ENV_FILE=apps/edr-payment-api/.env"   >> "$GITHUB_ENV" ;;
          esac

      - name: Build migration image for ${{ matrix.service }}
        if: contains(fromJson('["passenger-api", "payment-api"]'), matrix.service)
        run: |
          set -euo pipefail
          docker build \
            --secret id=npmrc,src=.npmrc \
            --target migration \
            -f "apps/edr-${{ matrix.service }}/Dockerfile" \
            -t "${COMPOSE_PROJECT_NAME}-${{ matrix.service }}-migration" \
            .

      - name: Run migrations for ${{ matrix.service }}
        if: contains(fromJson('["passenger-api", "payment-api"]'), matrix.service)
        run: |
          set -euo pipefail
          docker run --rm --env-file "${SERVICE_ENV_FILE}" "${COMPOSE_PROJECT_NAME}-${{ matrix.service }}-migration"

      - name: Build ${{ matrix.service }}
        run: |
          set -euo pipefail
          IMAGE_TAG="${COMPOSE_PROJECT_NAME}-${{ matrix.service }}:${GITHUB_SHA::8}"
          docker compose --project-name "${COMPOSE_PROJECT_NAME}" build "${{ matrix.service }}"
          # Tag with git SHA for rollback capability
          CONTAINER_NAME=$(docker compose --project-name "${COMPOSE_PROJECT_NAME}" config --services | grep "${{ matrix.service }}" | head -1)
          docker tag "${COMPOSE_PROJECT_NAME}-${{ matrix.service }}" "${IMAGE_TAG}" 2>/dev/null || true
          echo "IMAGE_TAG=${IMAGE_TAG}" >> "${GITHUB_ENV}"

      - name: Deploy ${{ matrix.service }}
        run: |
          set -euo pipefail
          docker compose --project-name "${COMPOSE_PROJECT_NAME}" up -d "${{ matrix.service }}" --force-recreate

      - name: Remove npm credentials from workspace
        if: always()
        run: rm -f .npmrc .npmrc_temp
