# GitLab CI/CD Pipeline for WiFi-DensePose
# This pipeline provides an alternative to GitHub Actions for GitLab users

stages:
  - validate
  - test
  - security
  - build
  - deploy-staging
  - deploy-production
  - monitor

variables:
  DOCKER_DRIVER: overlay2
  DOCKER_TLS_CERTDIR: "/certs"
  REGISTRY: $CI_REGISTRY
  IMAGE_NAME: $CI_REGISTRY_IMAGE
  PYTHON_VERSION: "3.11"
  KUBECONFIG: /tmp/kubeconfig

# Global before_script
before_script:
  - echo "Pipeline started for $CI_COMMIT_REF_NAME"
  - export IMAGE_TAG=${CI_COMMIT_SHA:0:8}

# Code Quality and Validation
code-quality:
  stage: validate
  image: python:$PYTHON_VERSION
  before_script:
    - pip install --upgrade pip
    - pip install -r requirements.txt
    - pip install black flake8 mypy bandit safety
  script:
    - echo "Running code quality checks..."
    - black --check --diff src/ tests/
    - flake8 src/ tests/ --max-line-length=88 --extend-ignore=E203,W503
    - mypy src/ --ignore-missing-imports
    - bandit -r src/ -f json -o bandit-report.json || true
    - safety check --json --output safety-report.json || true
  artifacts:
    reports:
      junit: bandit-report.json
    paths:
      - bandit-report.json
      - safety-report.json
    expire_in: 1 week
  rules:
    - if: $CI_PIPELINE_SOURCE == "merge_request_event"
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH

# Unit Tests
unit-tests:
  stage: test
  image: python:$PYTHON_VERSION
  services:
    - postgres:15
    - redis:7
  variables:
    POSTGRES_DB: test_wifi_densepose
    POSTGRES_USER: postgres
    POSTGRES_PASSWORD: postgres
    DATABASE_URL: postgresql://postgres:postgres@postgres:5432/test_wifi_densepose
    REDIS_URL: redis://redis:6379/0
    ENVIRONMENT: test
  before_script:
    - pip install --upgrade pip
    - pip install -r requirements.txt
    - pip install pytest-cov pytest-xdist
  script:
    - echo "Running unit tests..."
    - pytest tests/unit/ -v --cov=src --cov-report=xml --cov-report=html --junitxml=junit.xml
  coverage: '/TOTAL.*\s+(\d+%)$/'
  artifacts:
    reports:
      junit: junit.xml
      coverage_report:
        coverage_format: cobertura
        path: coverage.xml
    paths:
      - htmlcov/
    expire_in: 1 week
  rules:
    - if: $CI_PIPELINE_SOURCE == "merge_request_event"
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH

# Integration Tests
integration-tests:
  stage: test
  image: python:$PYTHON_VERSION
  services:
    - postgres:15
    - redis:7
  variables:
    POSTGRES_DB: test_wifi_densepose
    POSTGRES_USER: postgres
    POSTGRES_PASSWORD: postgres
    DATABASE_URL: postgresql://postgres:postgres@postgres:5432/test_wifi_densepose
    REDIS_URL: redis://redis:6379/0
    ENVIRONMENT: test
  before_script:
    - pip install --upgrade pip
    - pip install -r requirements.txt
    - pip install pytest
  script:
    - echo "Running integration tests..."
    - pytest tests/integration/ -v --junitxml=integration-junit.xml
  artifacts:
    reports:
      junit: integration-junit.xml
    expire_in: 1 week
  rules:
    - if: $CI_PIPELINE_SOURCE == "merge_request_event"
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH

# Security Scanning
security-scan:
  stage: security
  image: python:$PYTHON_VERSION
  before_script:
    - pip install --upgrade pip
    - pip install -r requirements.txt
    - pip install bandit semgrep safety
  script:
    - echo "Running security scans..."
    - bandit -r src/ -f sarif -o bandit-results.sarif || true
    - semgrep --config=p/security-audit --config=p/secrets --config=p/python --sarif --output=semgrep.sarif src/ || true
    - safety check --json --output safety-report.json || true
  artifacts:
    reports:
      sast: 
        - bandit-results.sarif
        - semgrep.sarif
    paths:
      - safety-report.json
    expire_in: 1 week
  rules:
    - if: $CI_PIPELINE_SOURCE == "merge_request_event"
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH

# Container Security Scan
container-security:
  stage: security
  image: docker:latest
  services:
    - docker:dind
  before_script:
    - docker info
    - echo $CI_REGISTRY_PASSWORD | docker login -u $CI_REGISTRY_USER --password-stdin $CI_REGISTRY
  script:
    - echo "Building and scanning container..."
    - docker build -t $IMAGE_NAME:$IMAGE_TAG .
    - docker run --rm -v /var/run/docker.sock:/var/run/docker.sock -v $PWD:/tmp/.cache/ aquasec/trivy:latest image --format sarif --output /tmp/.cache/trivy-results.sarif $IMAGE_NAME:$IMAGE_TAG || true
  artifacts:
    reports:
      container_scanning: trivy-results.sarif
    expire_in: 1 week
  rules:
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
    - if: $CI_PIPELINE_SOURCE == "merge_request_event"

# Build and Push Docker Image
build-image:
  stage: build
  image: docker:latest
  services:
    - docker:dind
  before_script:
    - docker info
    - echo $CI_REGISTRY_PASSWORD | docker login -u $CI_REGISTRY_USER --password-stdin $CI_REGISTRY
  script:
    - echo "Building Docker image..."
    - docker build --target production -t $IMAGE_NAME:$IMAGE_TAG -t $IMAGE_NAME:latest .
    - docker push $IMAGE_NAME:$IMAGE_TAG
    - docker push $IMAGE_NAME:latest
    - echo "Image pushed: $IMAGE_NAME:$IMAGE_TAG"
  rules:
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
    - if: $CI_COMMIT_TAG

# Deploy to Staging
deploy-staging:
  stage: deploy-staging
  image: bitnami/kubectl:latest
  environment:
    name: staging
    url: https://staging.wifi-densepose.com
  before_script:
    - echo "$KUBE_CONFIG_STAGING" | base64 -d > $KUBECONFIG
    - kubectl config view
  script:
    - echo "Deploying to staging environment..."
    - kubectl set image deployment/wifi-densepose wifi-densepose=$IMAGE_NAME:$IMAGE_TAG -n wifi-densepose-staging
    - kubectl rollout status deployment/wifi-densepose -n wifi-densepose-staging --timeout=600s
    - kubectl get pods -n wifi-densepose-staging -l app=wifi-densepose
    - echo "Staging deployment completed"
  after_script:
    - sleep 30
    - curl -f https://staging.wifi-densepose.com/health || exit 1
  rules:
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
  when: manual
  allow_failure: false

# Deploy to Production
deploy-production:
  stage: deploy-production
  image: bitnami/kubectl:latest
  environment:
    name: production
    url: https://wifi-densepose.com
  before_script:
    - echo "$KUBE_CONFIG_PRODUCTION" | base64 -d > $KUBECONFIG
    - kubectl config view
  script:
    - echo "Deploying to production environment..."
    # Backup current deployment
    - kubectl get deployment wifi-densepose -n wifi-densepose -o yaml > backup-deployment.yaml
    # Blue-Green Deployment
    - kubectl patch deployment wifi-densepose -n wifi-densepose -p '{"spec":{"template":{"metadata":{"labels":{"version":"green"}}}}}'
    - kubectl set image deployment/wifi-densepose wifi-densepose=$IMAGE_NAME:$IMAGE_TAG -n wifi-densepose
    - kubectl rollout status deployment/wifi-densepose -n wifi-densepose --timeout=600s
    - kubectl wait --for=condition=ready pod -l app=wifi-densepose,version=green -n wifi-densepose --timeout=300s
    # Switch traffic
    - kubectl patch service wifi-densepose-service -n wifi-densepose -p '{"spec":{"selector":{"version":"green"}}}'
    - echo "Production deployment completed"
  after_script:
    - sleep 30
    - curl -f https://wifi-densepose.com/health || exit 1
  artifacts:
    paths:
      - backup-deployment.yaml
    expire_in: 1 week
  rules:
    - if: $CI_COMMIT_TAG
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
      when: manual
  allow_failure: false

# Post-deployment Monitoring
monitor-deployment:
  stage: monitor
  image: curlimages/curl:latest
  script:
    - echo "Monitoring deployment health..."
    - |
      if [ "$CI_ENVIRONMENT_NAME" = "production" ]; then
        BASE_URL="https://wifi-densepose.com"
      else
        BASE_URL="https://staging.wifi-densepose.com"
      fi
    - |
      for i in $(seq 1 10); do
        echo "Health check $i/10"
        curl -f $BASE_URL/health || exit 1
        curl -f $BASE_URL/api/v1/status || exit 1
        sleep 30
      done
    - echo "Monitoring completed successfully"
  rules:
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
      when: on_success
    - if: $CI_COMMIT_TAG
      when: on_success
  allow_failure: true

# Rollback Job (Manual)
rollback:
  stage: deploy-production
  image: bitnami/kubectl:latest
  environment:
    name: production
    url: https://wifi-densepose.com
  before_script:
    - echo "$KUBE_CONFIG_PRODUCTION" | base64 -d > $KUBECONFIG
  script:
    - echo "Rolling back deployment..."
    - kubectl rollout undo deployment/wifi-densepose -n wifi-densepose
    - kubectl rollout status deployment/wifi-densepose -n wifi-densepose --timeout=600s
    - kubectl get pods -n wifi-densepose -l app=wifi-densepose
    - echo "Rollback completed"
  rules:
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
      when: manual
  allow_failure: false

# Cleanup old images
cleanup:
  stage: monitor
  image: docker:latest
  services:
    - docker:dind
  before_script:
    - echo $CI_REGISTRY_PASSWORD | docker login -u $CI_REGISTRY_USER --password-stdin $CI_REGISTRY
  script:
    - echo "Cleaning up old images..."
    - |
      # Keep only the last 10 images
      IMAGES_TO_DELETE=$(docker images $IMAGE_NAME --format "table {{.Tag}}" | tail -n +2 | tail -n +11)
      for tag in $IMAGES_TO_DELETE; do
        if [ "$tag" != "latest" ] && [ "$tag" != "$IMAGE_TAG" ]; then
          echo "Deleting image: $IMAGE_NAME:$tag"
          docker rmi $IMAGE_NAME:$tag || true
        fi
      done
  rules:
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
      when: on_success
  allow_failure: true

# Notification
notify-success:
  stage: monitor
  image: curlimages/curl:latest
  script:
    - |
      if [ -n "$SLACK_WEBHOOK_URL" ]; then
        curl -X POST -H 'Content-type: application/json' \
          --data "{\"text\":\"✅ Pipeline succeeded for $CI_PROJECT_NAME on $CI_COMMIT_REF_NAME\"}" \
          $SLACK_WEBHOOK_URL
      fi
  rules:
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
      when: on_success
  allow_failure: true

notify-failure:
  stage: monitor
  image: curlimages/curl:latest
  script:
    - |
      if [ -n "$SLACK_WEBHOOK_URL" ]; then
        curl -X POST -H 'Content-type: application/json' \
          --data "{\"text\":\"❌ Pipeline failed for $CI_PROJECT_NAME on $CI_COMMIT_REF_NAME\"}" \
          $SLACK_WEBHOOK_URL
      fi
  rules:
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
      when: on_failure
  allow_failure: true

# Include additional pipeline configurations
include:
  - template: Security/SAST.gitlab-ci.yml
  - template: Security/Container-Scanning.gitlab-ci.yml
  - template: Security/Dependency-Scanning.gitlab-ci.yml
  - template: Security/License-Scanning.gitlab-ci.yml