stages:
  - test
  - build
  - deploy

variables:
  DOCKERFILE_PATH: Dockerfile.production
  # Required: disable TLS so the docker client can reach the dind daemon
  DOCKER_TLS_CERTDIR: ""
  DOCKER_HOST: tcp://docker:2375

# ====================================
# TEST STAGE
# Runs on every push and merge request
# ====================================
unit_tests:
  stage: test
  image: node:20-bookworm
  before_script:
    - npm ci
    - npm run db:generate
  script:
    - npm run type-check
    - npm run test:api
  rules:
    - if: $CI_PIPELINE_SOURCE == "merge_request_event"
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH

integration_tests:
  stage: test
  image: node:20-bookworm
  services:
    - name: postgres:16-alpine
      alias: postgres
      variables:
        POSTGRES_DB: rentaldrivego_test
        POSTGRES_USER: postgres
        POSTGRES_PASSWORD: password
    - name: redis:7-alpine
      alias: redis
  variables:
    # Used by db:deploy (Prisma migrations) and any code reading DATABASE_URL directly
    DATABASE_URL: "postgresql://postgres:password@postgres:5432/rentaldrivego_test"
    REDIS_URL: "redis://redis:6379"
    NODE_ENV: test
  before_script:
    - npm ci
    - npm run db:generate
    # Overwrite the local .env.test so vitest integration config gets CI service hostnames
    # (the file uses postgres/redis aliases, not localhost)
    - |
      cat > apps/api/.env.test << 'EOF'
      DATABASE_URL=postgresql://postgres:password@postgres:5432/rentaldrivego_test
      REDIS_URL=redis://redis:6379
      JWT_SECRET=test-secret
      JWT_EXPIRY=8h
      NODE_ENV=test
      FILE_STORAGE_ROOT=/tmp/rentaldrivego-test-storage
      EOF
    - npm run db:deploy
  script:
    - npm run test:api:integration
  rules:
    - if: $CI_PIPELINE_SOURCE == "merge_request_event"
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH

# ====================================
# BUILD STAGE
# ====================================
build_image:
  stage: build
  image: docker:24
  services:
    - name: docker:24-dind
      alias: docker
      command: ["--tls=false"]
      variables:
        HEALTHCHECK_TCP_PORT: "2375"
  before_script:
    - until docker info >/dev/null 2>&1; do echo "Waiting for Docker daemon..."; sleep 1; done
    # Prefer GitLab's built-in registry variables, but allow explicit fallbacks
    # so the pipeline can publish to any OCI-compatible registry.
    - export REGISTRY_HOST="${CI_REGISTRY:-${REGISTRY_HOST:-}}"
    - export REGISTRY_USER="${CI_REGISTRY_USER:-${REGISTRY_USER:-}}"
    - export REGISTRY_PASSWORD="${CI_REGISTRY_PASSWORD:-${REGISTRY_PASSWORD:-}}"
    - export IMAGE_REPOSITORY="${CI_REGISTRY_IMAGE:-${REGISTRY_IMAGE:-}}"
    - test -n "$REGISTRY_HOST" || { echo "Registry host is not set. Configure CI_REGISTRY or REGISTRY_HOST."; exit 1; }
    - test -n "$REGISTRY_USER" || { echo "Registry user is not set. Configure CI_REGISTRY_USER or REGISTRY_USER."; exit 1; }
    - test -n "$REGISTRY_PASSWORD" || { echo "Registry password is not set. Configure CI_REGISTRY_PASSWORD or REGISTRY_PASSWORD."; exit 1; }
    - test -n "$IMAGE_REPOSITORY" || { echo "Image repository is not set. Configure CI_REGISTRY_IMAGE or REGISTRY_IMAGE."; exit 1; }
    - export DOCKER_IMAGE="$IMAGE_REPOSITORY:$CI_COMMIT_SHORT_SHA"
    - export DOCKER_IMAGE_LATEST="$IMAGE_REPOSITORY:latest"
    - echo "$REGISTRY_PASSWORD" | docker login "$REGISTRY_HOST" -u "$REGISTRY_USER" --password-stdin
  script:
    - echo "--- Building Docker Image ---"
    # NEXT_PUBLIC_* vars are inlined into Next.js bundles at build time — must be passed as ARGs
    - |
      docker build \
        --build-arg NEXT_PUBLIC_API_URL=$NEXT_PUBLIC_API_URL \
        --build-arg NEXT_PUBLIC_MARKETPLACE_URL=$NEXT_PUBLIC_MARKETPLACE_URL \
        --build-arg NEXT_PUBLIC_DASHBOARD_URL=$NEXT_PUBLIC_DASHBOARD_URL \
        --build-arg NEXT_PUBLIC_ADMIN_URL=$NEXT_PUBLIC_ADMIN_URL \
        -t $DOCKER_IMAGE \
        -t $DOCKER_IMAGE_LATEST \
        -f $DOCKERFILE_PATH .
    - echo "--- Pushing to Registry ---"
    - docker push $DOCKER_IMAGE
    - docker push $DOCKER_IMAGE_LATEST
  rules:
    # Skip the container publish step unless either the GitLab registry or an
    # explicit fallback registry configuration is available.
    - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH && (($CI_REGISTRY && $CI_REGISTRY_USER && $CI_REGISTRY_PASSWORD && $CI_REGISTRY_IMAGE) || ($REGISTRY_HOST && $REGISTRY_USER && $REGISTRY_PASSWORD && $REGISTRY_IMAGE))'
    - when: never

# ====================================
# DEPLOY STAGE
# ====================================
deploy_to_vps:
  stage: deploy
  image: alpine:latest
  needs:
    - build_image
  before_script:
    - export REGISTRY_HOST="${CI_REGISTRY:-${REGISTRY_HOST:-}}"
    - export REGISTRY_USER="${CI_REGISTRY_USER:-${REGISTRY_USER:-}}"
    - export REGISTRY_PASSWORD="${CI_REGISTRY_PASSWORD:-${REGISTRY_PASSWORD:-}}"
    - export IMAGE_REPOSITORY="${CI_REGISTRY_IMAGE:-${REGISTRY_IMAGE:-}}"
    - test -n "$REGISTRY_HOST" || { echo "Registry host is not set. Configure CI_REGISTRY or REGISTRY_HOST."; exit 1; }
    - test -n "$REGISTRY_USER" || { echo "Registry user is not set. Configure CI_REGISTRY_USER or REGISTRY_USER."; exit 1; }
    - test -n "$REGISTRY_PASSWORD" || { echo "Registry password is not set. Configure CI_REGISTRY_PASSWORD or REGISTRY_PASSWORD."; exit 1; }
    - test -n "$IMAGE_REPOSITORY" || { echo "Image repository is not set. Configure CI_REGISTRY_IMAGE or REGISTRY_IMAGE."; exit 1; }
    - export DOCKER_IMAGE="$IMAGE_REPOSITORY:$CI_COMMIT_SHORT_SHA"
    - apk add --no-cache openssh-client
    # Load the SSH private key stored in the CI variable SSH_PRIVATE_KEY
    - eval $(ssh-agent -s)
    - echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add -
    - mkdir -p ~/.ssh && chmod 700 ~/.ssh
    # Scan and trust the VPS host key (avoids manual known_hosts management)
    - ssh-keyscan -H $VPS_IP >> ~/.ssh/known_hosts
    - chmod 644 ~/.ssh/known_hosts
  script:
    - echo "--- Deploying $DOCKER_IMAGE to VPS ---"
    - |
      ssh $VPS_USER@$VPS_IP "
        set -e
        cd /opt/rentaldrivego

        echo '-- Pulling latest code --'
        git pull

        echo '-- Logging into registry --'
        test -n '$REGISTRY_HOST' || { echo 'Registry host is not set.'; exit 1; }
        test -n '$REGISTRY_USER' || { echo 'Registry user is not set.'; exit 1; }
        test -n '$REGISTRY_PASSWORD' || { echo 'Registry password is not set.'; exit 1; }
        echo '$REGISTRY_PASSWORD' | docker login '$REGISTRY_HOST' -u '$REGISTRY_USER' --password-stdin

        echo '-- Pulling pre-built image --'
        docker pull $DOCKER_IMAGE

        echo '-- Ensuring uploads volume exists --'
        docker volume create rentaldrivego-prod_api_uploads >/dev/null

        echo '-- Restarting services --'
        APP_IMAGE=$IMAGE_REPOSITORY \
        APP_VERSION=$CI_COMMIT_SHORT_SHA \
        docker compose -p rentaldrivego-prod \
          --env-file .env.docker.production \
          -f docker-compose.production.yml \
          up -d --no-build

        echo '-- Pruning old images --'
        docker image prune -f
      "
  rules:
    # Deploy only when both registry access and VPS credentials are available.
    - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH && ((($CI_REGISTRY && $CI_REGISTRY_USER && $CI_REGISTRY_PASSWORD && $CI_REGISTRY_IMAGE) || ($REGISTRY_HOST && $REGISTRY_USER && $REGISTRY_PASSWORD && $REGISTRY_IMAGE)) && $SSH_PRIVATE_KEY && $VPS_IP && $VPS_USER)'
    - when: never
