# Syndarix CI/CD Pipeline
# Gitea Actions workflow for continuous integration and deployment
#
# Pipeline Structure:
# - lint: Fast feedback (linting and type checking)
# - test: Run test suites (depends on lint)
# - build: Build Docker images (depends on test)
# - deploy: Deploy to production (depends on build, only on main)

name: CI/CD Pipeline

on:
  push:
    branches:
      - main
      - dev
      - 'feature/**'
  pull_request:
    branches:
      - main
      - dev

env:
  PYTHON_VERSION: "3.12"
  NODE_VERSION: "20"
  UV_VERSION: "0.4.x"

jobs:
  # ===========================================================================
  # LINT JOB - Fast feedback first
  # ===========================================================================
  lint:
    name: Lint & Type Check
    runs-on: ubuntu-latest
    strategy:
      matrix:
        component: [backend, frontend]
    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      # ----- Backend Linting -----
      - name: Set up Python
        if: matrix.component == 'backend'
        uses: actions/setup-python@v5
        with:
          python-version: ${{ env.PYTHON_VERSION }}

      - name: Install uv
        if: matrix.component == 'backend'
        uses: astral-sh/setup-uv@v4
        with:
          version: ${{ env.UV_VERSION }}

      - name: Cache uv dependencies
        if: matrix.component == 'backend'
        uses: actions/cache@v4
        with:
          path: |
            ~/.cache/uv
            backend/.venv
          key: uv-${{ runner.os }}-${{ hashFiles('backend/uv.lock') }}
          restore-keys: |
            uv-${{ runner.os }}-

      - name: Install backend dependencies
        if: matrix.component == 'backend'
        working-directory: backend
        run: uv sync --extra dev --frozen

      - name: Run ruff linting
        if: matrix.component == 'backend'
        working-directory: backend
        run: uv run ruff check app

      - name: Run ruff format check
        if: matrix.component == 'backend'
        working-directory: backend
        run: uv run ruff format --check app

      - name: Run mypy type checking
        if: matrix.component == 'backend'
        working-directory: backend
        run: uv run mypy app

      # ----- Frontend Linting -----
      - name: Set up Node.js
        if: matrix.component == 'frontend'
        uses: actions/setup-node@v4
        with:
          node-version: ${{ env.NODE_VERSION }}

      - name: Cache npm dependencies
        if: matrix.component == 'frontend'
        uses: actions/cache@v4
        with:
          path: |
            ~/.npm
            frontend/node_modules
          key: npm-${{ runner.os }}-${{ hashFiles('frontend/package-lock.json') }}
          restore-keys: |
            npm-${{ runner.os }}-

      - name: Install frontend dependencies
        if: matrix.component == 'frontend'
        working-directory: frontend
        run: npm ci

      - name: Run ESLint
        if: matrix.component == 'frontend'
        working-directory: frontend
        run: npm run lint

      - name: Run TypeScript type check
        if: matrix.component == 'frontend'
        working-directory: frontend
        run: npm run type-check

      - name: Run Prettier format check
        if: matrix.component == 'frontend'
        working-directory: frontend
        run: npm run format:check

  # ===========================================================================
  # TEST JOB - Run test suites
  # ===========================================================================
  test:
    name: Test
    runs-on: ubuntu-latest
    needs: lint
    strategy:
      matrix:
        component: [backend, frontend]
    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      # ----- Backend Tests -----
      - name: Set up Python
        if: matrix.component == 'backend'
        uses: actions/setup-python@v5
        with:
          python-version: ${{ env.PYTHON_VERSION }}

      - name: Install uv
        if: matrix.component == 'backend'
        uses: astral-sh/setup-uv@v4
        with:
          version: ${{ env.UV_VERSION }}

      - name: Cache uv dependencies
        if: matrix.component == 'backend'
        uses: actions/cache@v4
        with:
          path: |
            ~/.cache/uv
            backend/.venv
          key: uv-${{ runner.os }}-${{ hashFiles('backend/uv.lock') }}
          restore-keys: |
            uv-${{ runner.os }}-

      - name: Install backend dependencies
        if: matrix.component == 'backend'
        working-directory: backend
        run: uv sync --extra dev --frozen

      - name: Run pytest with coverage
        if: matrix.component == 'backend'
        working-directory: backend
        env:
          IS_TEST: "True"
        run: |
          uv run pytest --cov=app --cov-report=xml --cov-report=term-missing --cov-fail-under=90

      - name: Upload backend coverage report
        if: matrix.component == 'backend'
        uses: actions/upload-artifact@v4
        with:
          name: backend-coverage
          path: backend/coverage.xml
          retention-days: 7

      # ----- Frontend Tests -----
      - name: Set up Node.js
        if: matrix.component == 'frontend'
        uses: actions/setup-node@v4
        with:
          node-version: ${{ env.NODE_VERSION }}

      - name: Cache npm dependencies
        if: matrix.component == 'frontend'
        uses: actions/cache@v4
        with:
          path: |
            ~/.npm
            frontend/node_modules
          key: npm-${{ runner.os }}-${{ hashFiles('frontend/package-lock.json') }}
          restore-keys: |
            npm-${{ runner.os }}-

      - name: Install frontend dependencies
        if: matrix.component == 'frontend'
        working-directory: frontend
        run: npm ci

      - name: Run Jest unit tests
        if: matrix.component == 'frontend'
        working-directory: frontend
        run: npm test -- --coverage --passWithNoTests

      - name: Upload frontend coverage report
        if: matrix.component == 'frontend'
        uses: actions/upload-artifact@v4
        with:
          name: frontend-coverage
          path: frontend/coverage/
          retention-days: 7

  # ===========================================================================
  # BUILD JOB - Build Docker images
  # ===========================================================================
  build:
    name: Build
    runs-on: ubuntu-latest
    needs: test
    strategy:
      matrix:
        component: [backend, frontend]
    steps:
      - name: Checkout code
        uses: actions/checkout@v4

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

      - name: Cache Docker layers
        uses: actions/cache@v4
        with:
          path: /tmp/.buildx-cache
          key: docker-${{ matrix.component }}-${{ github.sha }}
          restore-keys: |
            docker-${{ matrix.component }}-

      - name: Build backend Docker image
        if: matrix.component == 'backend'
        uses: docker/build-push-action@v5
        with:
          context: ./backend
          file: ./backend/Dockerfile
          target: production
          push: false
          tags: syndarix-backend:${{ github.sha }}
          cache-from: type=local,src=/tmp/.buildx-cache
          cache-to: type=local,dest=/tmp/.buildx-cache-new,mode=max

      - name: Build frontend Docker image
        if: matrix.component == 'frontend'
        uses: docker/build-push-action@v5
        with:
          context: ./frontend
          file: ./frontend/Dockerfile
          target: runner
          push: false
          tags: syndarix-frontend:${{ github.sha }}
          build-args: |
            NEXT_PUBLIC_API_URL=http://localhost:8000
          cache-from: type=local,src=/tmp/.buildx-cache
          cache-to: type=local,dest=/tmp/.buildx-cache-new,mode=max

      # Prevent cache from growing indefinitely
      - name: Move cache
        run: |
          rm -rf /tmp/.buildx-cache
          mv /tmp/.buildx-cache-new /tmp/.buildx-cache

  # ===========================================================================
  # DEPLOY JOB - Deploy to production (only on main branch)
  # ===========================================================================
  deploy:
    name: Deploy
    runs-on: ubuntu-latest
    needs: build
    if: github.ref == 'refs/heads/main' && github.event_name == 'push'
    environment: production
    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Deploy notification
        run: |
          echo "Deployment to production would happen here"
          echo "Branch: ${{ github.ref }}"
          echo "Commit: ${{ github.sha }}"
          echo "Actor: ${{ github.actor }}"

      # TODO: Add actual deployment steps when infrastructure is ready
      # Options:
      # - SSH to production server and run docker-compose pull && docker-compose up -d
      # - Use Kubernetes deployment
      # - Use cloud provider deployment (AWS ECS, GCP Cloud Run, etc.)
      # - Trigger webhook to deployment orchestrator

  # ===========================================================================
  # SECURITY SCAN JOB - Run on main and dev branches
  # ===========================================================================
  security:
    name: Security Scan
    runs-on: ubuntu-latest
    needs: lint
    if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/dev'
    steps:
      - name: Checkout code
        uses: actions/checkout@v4

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

      - name: Install uv
        uses: astral-sh/setup-uv@v4
        with:
          version: ${{ env.UV_VERSION }}

      - name: Install backend dependencies
        working-directory: backend
        run: uv sync --extra dev --frozen

      - name: Run Bandit security scan (via ruff)
        working-directory: backend
        run: |
          # Ruff includes flake8-bandit (S rules) for security scanning
          # Run with explicit security rules only
          uv run ruff check app --select=S --ignore=S101,S104,S105,S106,S603,S607

      - name: Run pip-audit for dependency vulnerabilities
        working-directory: backend
        run: |
          # pip-audit checks for known vulnerabilities in Python dependencies
          uv run pip-audit --require-hashes --disable-pip -r <(uv pip compile pyproject.toml) || true
          # Note: Using || true temporarily while setting up proper remediation

      - name: Check for secrets in code
        run: |
          # Basic check for common secret patterns
          # In production, use tools like gitleaks or trufflehog
          echo "Checking for potential hardcoded secrets..."
          ! grep -rn --include="*.py" --include="*.ts" --include="*.tsx" --include="*.js" \
            -E "(api_key|apikey|secret_key|secretkey|password|passwd|token)\s*=\s*['\"][^'\"]{8,}['\"]" \
            backend/app frontend/src || echo "No obvious secrets found"

      - name: Set up Node.js
        uses: actions/setup-node@v4
        with:
          node-version: ${{ env.NODE_VERSION }}

      - name: Install frontend dependencies
        working-directory: frontend
        run: npm ci

      - name: Run npm audit
        working-directory: frontend
        run: |
          npm audit --audit-level=high || true
          # Note: Using || true to not fail on moderate vulnerabilities
          # In production, consider stricter settings

  # ===========================================================================
  # E2E TEST JOB - Run end-to-end tests with Playwright
  # ===========================================================================
  e2e-tests:
    name: E2E Tests
    runs-on: ubuntu-latest
    needs: [lint, test]
    if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/dev' || github.event_name == 'pull_request'
    services:
      postgres:
        image: pgvector/pgvector:pg17
        env:
          POSTGRES_USER: postgres
          POSTGRES_PASSWORD: postgres
          POSTGRES_DB: syndarix_test
        ports:
          - 5432:5432
        options: >-
          --health-cmd "pg_isready -U postgres"
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5
      redis:
        image: redis:7-alpine
        ports:
          - 6379:6379
        options: >-
          --health-cmd "redis-cli ping"
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5
    steps:
      - name: Checkout code
        uses: actions/checkout@v4

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

      - name: Install uv
        uses: astral-sh/setup-uv@v4
        with:
          version: ${{ env.UV_VERSION }}

      - name: Install backend dependencies
        working-directory: backend
        run: uv sync --extra dev --frozen

      - name: Set up Node.js
        uses: actions/setup-node@v4
        with:
          node-version: ${{ env.NODE_VERSION }}

      - name: Install frontend dependencies
        working-directory: frontend
        run: npm ci

      - name: Install Playwright browsers
        working-directory: frontend
        run: npx playwright install --with-deps chromium

      - name: Start backend server
        working-directory: backend
        env:
          DATABASE_URL: postgresql://postgres:postgres@localhost:5432/syndarix_test
          REDIS_URL: redis://localhost:6379/0
          SECRET_KEY: test-secret-key-for-e2e-tests-only
          ENVIRONMENT: test
          IS_TEST: "True"
        run: |
          # Run migrations
          uv run python -c "from app.database import create_tables; import asyncio; asyncio.run(create_tables())" || true
          # Start backend in background
          uv run uvicorn app.main:app --host 0.0.0.0 --port 8000 &
          # Wait for backend to be ready
          sleep 10

      - name: Run Playwright E2E tests
        working-directory: frontend
        env:
          NEXT_PUBLIC_API_URL: http://localhost:8000
        run: |
          npm run build
          npm run test:e2e -- --project=chromium

      - name: Upload Playwright report
        uses: actions/upload-artifact@v4
        if: always()
        with:
          name: playwright-report
          path: frontend/playwright-report/
          retention-days: 7
