# ============================================================
# CI Pipeline — lint + tests + Docker build
# Runs on every push and pull request
# ============================================================

name: CI

on:
  push:
    branches: ["**"]
  pull_request:
    branches: [main, master, develop]

concurrency:
  group: ci-${{ github.ref }}
  cancel-in-progress: true

env:
  PYTHON_VERSION: "3.12"
  REGISTRY: ghcr.io
  IMAGE_NAME: ${{ github.repository }}

jobs:
  # ----------------------------------------------------------
  # Job 1: Lint & Static Analysis
  # ----------------------------------------------------------
  lint:
    name: Lint & Security Scan
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Set up Python
        uses: actions/setup-python@v5
        with:
          python-version: ${{ env.PYTHON_VERSION }}
          cache: pip

      - name: Install lint tools
        run: pip install flake8 bandit safety

      - name: Flake8 linting
        run: |
          flake8 . \
            --exclude=venv,migrations,__pycache__,.git \
            --max-line-length=120 \
            --ignore=E501,W503,E302,E303 \
            --count --statistics
        continue-on-error: true

      - name: Bandit security scan
        run: |
          bandit -r . \
            --exclude ./venv,./migrations,./infra \
            -ll -ii \
            -f json -o bandit-report.json || true
          cat bandit-report.json

      - name: Safety dependency vulnerability check
        run: |
          safety check -r requirements.txt --json || true

  # ----------------------------------------------------------
  # Job 2: Tests
  # ----------------------------------------------------------
  test:
    name: Unit & Integration Tests
    runs-on: ubuntu-latest
    needs: lint

    services:
      postgres:
        image: postgres:16-alpine
        env:
          POSTGRES_DB: turf_test
          POSTGRES_USER: turf
          POSTGRES_PASSWORD: testpassword
        ports:
          - 5432:5432
        options: >-
          --health-cmd pg_isready
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5

    env:
      DATABASE_URL: postgresql://turf:testpassword@localhost:5432/turf_test
      POSTGRES_HOST: localhost
      POSTGRES_PORT: 5432
      POSTGRES_DB: turf_test
      POSTGRES_USER: turf
      POSTGRES_PASSWORD: testpassword
      FLASK_ENV: testing
      SECRET_KEY: test-secret-key-not-for-production
      DB_PATH: /tmp/turf_test.db
      LOG_LEVEL: WARNING

    steps:
      - uses: actions/checkout@v4

      - name: Set up Python
        uses: actions/setup-python@v5
        with:
          python-version: ${{ env.PYTHON_VERSION }}
          cache: pip

      - name: Install dependencies
        run: pip install -r requirements.txt pytest pytest-cov pytest-flask

      - name: Run Alembic migrations
        run: |
          if [ -f alembic.ini ]; then
            alembic upgrade head
          else
            echo "No alembic.ini found, skipping migrations"
          fi

      - name: Run tests
        run: |
          if [ -d tests ]; then
            pytest tests/ -v --cov=. --cov-report=xml --cov-report=term-missing
          else
            echo "No tests directory found — creating basic smoke test"
            python -c "
import sys, os
os.environ['FLASK_ENV'] = 'testing'
os.environ['SECRET_KEY'] = 'test'
os.environ['DB_PATH'] = '/tmp/smoke_test.db'
print('Import check...')
try:
    import combined_api
    print('combined_api: OK')
except Exception as e:
    print(f'combined_api: ERROR - {e}')
try:
    import dashboard_api
    print('dashboard_api: OK')
except Exception as e:
    print(f'dashboard_api: ERROR - {e}')
try:
    import portal_server
    print('portal_server: OK')
except Exception as e:
    print(f'portal_server: ERROR - {e}')
print('All checks done.')
"
          fi

      - name: Upload coverage report
        uses: codecov/codecov-action@v4
        if: hashFiles('coverage.xml') != ''
        with:
          file: ./coverage.xml
          fail_ci_if_error: false

  # ----------------------------------------------------------
  # Job 3: Docker Build
  # ----------------------------------------------------------
  docker-build:
    name: Docker Build & Push
    runs-on: ubuntu-latest
    needs: test
    permissions:
      contents: read
      packages: write

    steps:
      - uses: actions/checkout@v4

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

      - name: Log in to GHCR
        if: github.event_name != 'pull_request'
        uses: docker/login-action@v3
        with:
          registry: ${{ env.REGISTRY }}
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Extract Docker metadata
        id: meta
        uses: docker/metadata-action@v5
        with:
          images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
          tags: |
            type=ref,event=branch
            type=ref,event=pr
            type=sha,prefix=sha-
            type=raw,value=latest,enable={{is_default_branch}}

      - name: Build (and push on non-PR)
        uses: docker/build-push-action@v6
        with:
          context: .
          target: runner
          push: ${{ github.event_name != 'pull_request' }}
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}
          cache-from: type=gha
          cache-to: type=gha,mode=max

      - name: Verify image size
        if: github.event_name != 'pull_request'
        run: |
          SIZE=$(docker image inspect ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest --format='{{.Size}}' 2>/dev/null || echo "0")
          SIZE_MB=$((SIZE / 1024 / 1024))
          echo "Image size: ${SIZE_MB}MB"
          if [ "$SIZE_MB" -gt 500 ]; then
            echo "::warning::Image size ${SIZE_MB}MB exceeds 500MB limit"
          fi

  # ----------------------------------------------------------
  # Job 4: Notify on failure
  # ----------------------------------------------------------
  notify-failure:
    name: Notify on Failure
    runs-on: ubuntu-latest
    needs: [lint, test, docker-build]
    if: failure() && github.event_name == 'push'
    steps:
      - name: Notify Telegram
        if: vars.TELEGRAM_BOT_TOKEN != ''
        run: |
          curl -s -X POST \
            "https://api.telegram.org/bot${{ secrets.TELEGRAM_BOT_TOKEN }}/sendMessage" \
            -d chat_id="${{ secrets.TELEGRAM_CHAT_ID }}" \
            -d text="❌ CI FAILED: ${{ github.repository }} branch=${{ github.ref_name }} commit=${{ github.sha }}" \
            -d parse_mode="Markdown" || true

      - name: Notify Slack
        if: vars.SLACK_WEBHOOK_URL != ''
        run: |
          curl -s -X POST "${{ secrets.SLACK_WEBHOOK_URL }}" \
            -H 'Content-type: application/json' \
            --data "{\"text\":\"❌ CI FAILED: \`${{ github.repository }}\` branch=\`${{ github.ref_name }}\` commit=\`${{ github.sha }}\`\"}" || true
