name: Build and Deploy

on:
  push:
    branches:
      - main
  workflow_dispatch:

env:
  REGISTRY: ghcr.io
  VITE_API_BASE_URL: ${{ vars.VITE_API_BASE_URL || '' }}
jobs:
  build:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      packages: write
    strategy:
      fail-fast: false
      matrix:
        service:
          - name: web
            dockerfile: apps/web/Dockerfile
          - name: api
            dockerfile: apps/api/Dockerfile
          - name: ml
            dockerfile: apps/ml-service/Dockerfile
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4

      - name: Set image prefix
        run: echo "IMAGE_PREFIX=ghcr.io/${GITHUB_REPOSITORY,,}" >> "$GITHUB_ENV"

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

      - name: Set up Python for model download & export
        if: matrix.service.name == 'ml'
        uses: actions/setup-python@v5
        with:
          python-version: '3.11'

      - name: Download ONNX model from Hugging Face
        if: matrix.service.name == 'ml'
        working-directory: Machine_Learning
        env:
          HF_TOKEN: ${{ secrets.HF_TOKEN }}
        run: |
          set -eu
          echo "::group::Install huggingface_hub"
          python -m pip install --upgrade pip -q
          python -m pip install huggingface_hub -q
          echo "::endgroup::"

          echo "::group::Check HF_TOKEN"
          if [ -z "${HF_TOKEN:-}" ]; then
            echo "ERROR: HF_TOKEN secret is not set."
            echo "Add it: https://github.com/ATLAS-PJK-GM007/ZeaVis-Edu/settings/secrets/actions"
            exit 1
          fi
          echo "HF_TOKEN is set (length: ${#HF_TOKEN})"
          echo "::endgroup::"

          echo "::group::Download model files from Hugging Face"
          python -c "
          from huggingface_hub import hf_hub_download
          import os, shutil
          repo = 'MythEclipse2737/zeavis-edu-corn-leaf-classifier'
          token = os.environ['HF_TOKEN']
          base = os.path.abspath('.')

          # Files sit at root of HF repo → copy to correct subdirs
          # model.onnx goes to model/ for Docker COPY
          os.makedirs(os.path.join(base, 'model'), exist_ok=True)
          os.makedirs(os.path.join(base, 'best_model'), exist_ok=True)

          # ONNX → model/model.onnx (Docker expects this path)
          p = hf_hub_download(repo_id=repo, filename='model.onnx', token=token)
          shutil.copy2(p, os.path.join(base, 'model', 'model.onnx'))
          print('model/model.onnx OK')

          # TFLite (optional, for edge)
          p = hf_hub_download(repo_id=repo, filename='model.tflite', token=token)
          shutil.copy2(p, os.path.join(base, 'model', 'model.tflite'))
          print('model/model.tflite OK')

          # Labels
          p = hf_hub_download(repo_id=repo, filename='labels.json', token=token)
          shutil.copy2(p, os.path.join(base, 'model', 'labels.json'))
          print('model/labels.json OK')

          # Keras model + calibration for re-export
          p = hf_hub_download(repo_id=repo, filename='best_model.keras', token=token)
          shutil.copy2(p, os.path.join(base, 'best_model', 'best_model.keras'))
          print('best_model/best_model.keras OK')

          p = hf_hub_download(repo_id=repo, filename='calibration.json', token=token)
          shutil.copy2(p, os.path.join(base, 'best_model', 'calibration.json'))
          print('best_model/calibration.json OK')
          "
          ls -lh model/model.onnx model/model.tflite model/labels.json best_model/best_model.keras 2>/dev/null
          echo "::endgroup::"

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

      - name: Extract metadata
        id: meta
        uses: docker/metadata-action@v5
        with:
          images: ${{ env.IMAGE_PREFIX }}/${{ matrix.service.name }}
          tags: |
            type=ref,event=branch
            type=sha

      - name: Build and push image
        uses: docker/build-push-action@v5
        with:
          context: .
          file: ${{ matrix.service.dockerfile }}
          push: true
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}
          build-args: |
            VITE_API_BASE_URL=${{ env.VITE_API_BASE_URL }}
          cache-from: type=gha,scope=${{ matrix.service.name }}
          cache-to: type=gha,mode=max,scope=${{ matrix.service.name }}

  deploy:
    needs: build
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/main'
    permissions:
      contents: read
      packages: read
    steps:
      - name: Validate deploy secrets
        env:
          VPS_HOST: ${{ secrets.VPS_HOST }}
          VPS_USER: ${{ secrets.VPS_USER }}
          VPS_SSH_KEY: ${{ secrets.VPS_SSH_KEY }}
        run: |
          if [ -z "$VPS_HOST" ] || [ -z "$VPS_USER" ] || [ -z "$VPS_SSH_KEY" ]; then
            echo "Missing deploy secrets: VPS_HOST, VPS_USER, VPS_SSH_KEY." >&2
            exit 1
          fi

      - name: Deploy to VPS
        uses: appleboy/ssh-action@v1.0.3
        with:
          host: ${{ secrets.VPS_HOST }}
          username: ${{ secrets.VPS_USER }}
          key: ${{ secrets.VPS_SSH_KEY }}
          passphrase: ${{ secrets.VPS_SSH_PASSPHRASE }}
          port: ${{ secrets.VPS_PORT || 22 }}
          script: |
            set -e
            DEPLOY_PATH="${DEPLOY_PATH:-/opt/ZeaVis-Edu}"
            REPO_SLUG="$(echo "${{ github.repository }}" | tr '[:upper:]' '[:lower:]')"

            if [ ! -d "$DEPLOY_PATH/.git" ]; then
              mkdir -p "$DEPLOY_PATH"
              git clone https://github.com/${{ github.repository }}.git "$DEPLOY_PATH"
            fi

            cd "$DEPLOY_PATH"
            git fetch origin main
            git reset --hard origin/main

            {
              printf 'GITHUB_REPOSITORY=%s\n' "$REPO_SLUG"
              cat << 'ENVEOF'
            DATABASE_URL=${{ secrets.DATABASE_URL }}
            SESSION_SECRET=${{ secrets.SESSION_SECRET }}
            WEB_APP_URL=https://zeavisedu.asepharyana.my.id
            ML_SERVICE_URL=http://zeavis-ml:8000
            TS_IP=${{ vars.TS_IP || '100.96.248.86' }}
            GOOGLE_CLIENT_ID=${{ secrets.GOOGLE_CLIENT_ID }}
            GOOGLE_CLIENT_SECRET=${{ secrets.GOOGLE_CLIENT_SECRET }}
            GOOGLE_REDIRECT_URI=https://zeavisedu.asepharyana.my.id/api/v1/auth/google/callback
            ENVEOF
            } > .env

            docker login ghcr.io -u ${{ github.actor }} -p ${{ secrets.GITHUB_TOKEN }}
            docker network create app-shared-net 2>/dev/null || true
            docker network create telemetry-net 2>/dev/null || true
            docker compose down --remove-orphans || true
            docker rm -f zeavis-web zeavis-api zeavis-ml zeavis-node-exporter 2>/dev/null || true
            docker compose pull
            docker compose up -d
            # Wait for containers to be healthy (up to 60s)
            wait_container() {
              local name=$1
              for i in $(seq 1 30); do
                docker compose ps | grep -q "${name}.*Up" && return 0
                sleep 2
              done
              return 1
            }
            wait_container zeavis-web || exit 1
            wait_container zeavis-api || exit 1
            wait_container zeavis-ml || exit 1
            wait_container zeavis-node-exporter || echo "⚠️  node_exporter not running (non-fatal)"
            docker compose ps
