name: El SDK CI - dev

on:
  push:
    branches:
      - dev
  pull_request:
    branches:
      - dev

jobs:
  build-and-test:
    runs-on: ubuntu-latest
    defaults:
      run:
        working-directory: lang

    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Install build dependencies
        run: |
          apt-get update -qq
          apt-get install -y gcc libcurl4-openssl-dev apt-transport-https ca-certificates
          echo "deb [trusted=yes] https://packages.cloud.google.com/apt cloud-sdk main" \
            > /etc/apt/sources.list.d/google-cloud-sdk.list
          apt-get update -qq && apt-get install -y google-cloud-cli

      # Seed: use the committed linux-amd64 binary as the bootstrap
      - name: Bootstrap from committed linux binary (seed)
        run: |
          chmod +x dist/platform/elc-linux-amd64
          echo "seed elc (committed linux-amd64 binary)"
          dist/platform/elc-linux-amd64 --version || true

      # Gen2: use seed to self-host compile the El compiler
      - name: Self-host compile El compiler (gen2)
        run: |
          dist/platform/elc-linux-amd64 elc-cli.el > dist/elc-gen2.c
          gcc -O2 \
            -I el-compiler/runtime \
            dist/elc-gen2.c \
            el-compiler/runtime/el_runtime.c \
            -lcurl -lssl -lcrypto -lpthread -lm \
            -o dist/platform/elc
          chmod +x dist/platform/elc
          echo "gen2 (self-hosted) elc built"
          dist/platform/elc --version || true

      # Build elb (needed for Artifact Registry publish and downstream CI)
      - name: Build elb
        run: |
          mkdir -p dist/bin
          dist/platform/elc elb.el > dist/elb.c
          gcc -O2 \
            -I el-compiler/runtime \
            dist/elb.c \
            el-compiler/runtime/el_runtime.c \
            -lcurl -lssl -lcrypto -lpthread -lm \
            -o dist/bin/elb
          chmod +x dist/bin/elb
          echo "elb built"

      - name: Run tests - text
        run: |
          ELC="$(pwd)/dist/platform/elc" \
          EL_HOME="$(pwd)" \
          bash tests/text/run.sh

      - name: Run tests - calendar
        run: |
          ELC="$(pwd)/dist/platform/elc" \
          EL_HOME="$(pwd)" \
          bash tests/calendar/run.sh

      - name: Run tests - time
        run: |
          ELC="$(pwd)/dist/platform/elc" \
          EL_HOME="$(pwd)" \
          bash tests/time/run.sh

      - name: Run tests - html_sanitizer
        run: |
          ELC="$(pwd)/dist/platform/elc" \
          EL_HOME="$(pwd)" \
          bash tests/html_sanitizer/run.sh

      # Native El test suites (elc --test, compile-link-run)
      # el_runtime.c is precompiled to .o once and reused by all 8 modules.
      - name: Precompile el_runtime.o
        run: |
          set -euo pipefail
          RUNTIME="$(pwd)/el-compiler/runtime"
          gcc -O2 -c -I "$RUNTIME" "$RUNTIME/el_runtime.c" \
            -o /tmp/el_runtime.o
          echo "el_runtime.o compiled"

      - name: Run tests - native (core)
        run: |
          set -euo pipefail
          ELC="$(pwd)/dist/platform/elc"
          RUNTIME="$(pwd)/el-compiler/runtime"
          "$ELC" --test tests/native/test_core.el > /tmp/el_native_core.c
          gcc -O2 -I "$RUNTIME" /tmp/el_native_core.c /tmp/el_runtime.o \
            -lcurl -lssl -lcrypto -lpthread -lm -o /tmp/el_native_core
          /tmp/el_native_core

      - name: Run tests - native (text)
        run: |
          set -euo pipefail
          ELC="$(pwd)/dist/platform/elc"
          RUNTIME="$(pwd)/el-compiler/runtime"
          "$ELC" --test tests/native/test_text.el > /tmp/el_native_text.c
          gcc -O2 -I "$RUNTIME" /tmp/el_native_text.c /tmp/el_runtime.o \
            -lcurl -lssl -lcrypto -lpthread -lm -o /tmp/el_native_text
          /tmp/el_native_text

      - name: Run tests - native (string)
        run: |
          set -euo pipefail
          ELC="$(pwd)/dist/platform/elc"
          RUNTIME="$(pwd)/el-compiler/runtime"
          "$ELC" --test tests/native/test_string.el > /tmp/el_native_string.c
          gcc -O2 -I "$RUNTIME" /tmp/el_native_string.c /tmp/el_runtime.o \
            -lcurl -lssl -lcrypto -lpthread -lm -o /tmp/el_native_string
          /tmp/el_native_string

      - name: Run tests - native (math)
        run: |
          set -euo pipefail
          ELC="$(pwd)/dist/platform/elc"
          RUNTIME="$(pwd)/el-compiler/runtime"
          "$ELC" --test tests/native/test_math.el > /tmp/el_native_math.c
          gcc -O2 -I "$RUNTIME" /tmp/el_native_math.c /tmp/el_runtime.o \
            -lcurl -lssl -lcrypto -lpthread -lm -o /tmp/el_native_math
          /tmp/el_native_math

      - name: Run tests - native (state)
        run: |
          set -euo pipefail
          ELC="$(pwd)/dist/platform/elc"
          RUNTIME="$(pwd)/el-compiler/runtime"
          "$ELC" --test tests/native/test_state.el > /tmp/el_native_state.c
          gcc -O2 -I "$RUNTIME" /tmp/el_native_state.c /tmp/el_runtime.o \
            -lcurl -lssl -lcrypto -lpthread -lm -o /tmp/el_native_state
          /tmp/el_native_state

      - name: Run tests - native (time)
        run: |
          set -euo pipefail
          ELC="$(pwd)/dist/platform/elc"
          RUNTIME="$(pwd)/el-compiler/runtime"
          "$ELC" --test tests/native/test_time.el > /tmp/el_native_time.c
          gcc -O2 -I "$RUNTIME" /tmp/el_native_time.c /tmp/el_runtime.o \
            -lcurl -lssl -lcrypto -lpthread -lm -o /tmp/el_native_time
          /tmp/el_native_time

      - name: Run tests - native (json)
        run: |
          set -euo pipefail
          ELC="$(pwd)/dist/platform/elc"
          RUNTIME="$(pwd)/el-compiler/runtime"
          "$ELC" --test tests/native/test_json.el > /tmp/el_native_json.c
          gcc -O2 -I "$RUNTIME" /tmp/el_native_json.c /tmp/el_runtime.o \
            -lcurl -lssl -lcrypto -lpthread -lm -o /tmp/el_native_json
          /tmp/el_native_json

      - name: Run tests - native (env)
        run: |
          set -euo pipefail
          ELC="$(pwd)/dist/platform/elc"
          RUNTIME="$(pwd)/el-compiler/runtime"
          "$ELC" --test tests/native/test_env.el > /tmp/el_native_env.c
          gcc -O2 -I "$RUNTIME" /tmp/el_native_env.c /tmp/el_runtime.o \
            -lcurl -lssl -lcrypto -lpthread -lm -o /tmp/el_native_env
          /tmp/el_native_env

      - name: Run tests - native (fs)
        run: |
          set -euo pipefail
          ELC="$(pwd)/dist/platform/elc"
          RUNTIME="$(pwd)/el-compiler/runtime"
          "$ELC" --test tests/native/test_fs.el > /tmp/el_native_fs.c
          gcc -O2 -I "$RUNTIME" /tmp/el_native_fs.c /tmp/el_runtime.o \
            -lcurl -lssl -lcrypto -lpthread -lm -o /tmp/el_native_fs
          /tmp/el_native_fs

      # Build epm binary using elb (epm lives at repo root, not inside lang/)
      - name: Build epm
        run: |
          ABS_ELB="$(pwd)/dist/bin/elb"
          ABS_ELC="$(pwd)/dist/platform/elc"
          ABS_RUNTIME="$(pwd)/el-compiler/runtime"
          ABS_OUT="$(pwd)/dist/bin"
          (cd ../epm && "$ABS_ELB" --clean --elc="$ABS_ELC" --runtime="$ABS_RUNTIME" --out="$ABS_OUT")
          chmod +x dist/bin/epm
          echo "epm built"

      # Build el-install binary using elb
      - name: Build el-install
        run: |
          ABS_ELB="$(pwd)/dist/bin/elb"
          ABS_ELC="$(pwd)/dist/platform/elc"
          ABS_RUNTIME="$(pwd)/el-compiler/runtime"
          ABS_OUT="$(pwd)/dist/bin"
          (cd tools/install && "$ABS_ELB" --clean --elc="$ABS_ELC" --runtime="$ABS_RUNTIME" --out="$ABS_OUT")
          chmod +x dist/bin/el-install
          echo "el-install built"

      # Publish only after merge (push event), not on PR validation runs
      - name: Publish El SDK to Artifact Registry (dev)
        if: github.event_name == 'push'
        env:
          GCP_SA_KEY: ${{ secrets.GCP_SA_KEY }}
        run: |
          echo "${GCP_SA_KEY}" > /tmp/gcp-key.json
          gcloud auth activate-service-account --key-file=/tmp/gcp-key.json
          gcloud config set project neuron-785695

          VERSION="${GITHUB_SHA:0:8}"

          gcloud artifacts generic upload \
            --repository=foundation-dev \
            --location=us-central1 \
            --project=neuron-785695 \
            --package=el-elc \
            --version="${VERSION}" \
            --source=dist/platform/elc

          gcloud artifacts generic upload \
            --repository=foundation-dev \
            --location=us-central1 \
            --project=neuron-785695 \
            --package=el-elb \
            --version="${VERSION}" \
            --source=dist/bin/elb

          gcloud artifacts generic upload \
            --repository=foundation-dev \
            --location=us-central1 \
            --project=neuron-785695 \
            --package=el-runtime-c \
            --version="${VERSION}" \
            --source=el-compiler/runtime/el_runtime.c

          gcloud artifacts generic upload \
            --repository=foundation-dev \
            --location=us-central1 \
            --project=neuron-785695 \
            --package=el-runtime-h \
            --version="${VERSION}" \
            --source=el-compiler/runtime/el_runtime.h

          gcloud artifacts generic upload \
            --repository=foundation-dev \
            --location=us-central1 \
            --project=neuron-785695 \
            --package=el-runtime-js \
            --version="${VERSION}" \
            --source=el-compiler/runtime/el_runtime.js

          echo "Published El SDK version=${VERSION} to foundation-dev"
          # Keep key alive for the ci-base rebuild step below
          # (deleted in that step after docker push)

      - name: Rebuild ci-base with fresh El SDK (dev)
        # Patches ci-base:dev in-place: pulls the existing image (which has all
        # system deps — Node, Go, gcloud, Docker CLI, etc.) and overlays the freshly
        # built El SDK on top. Keeps the full ci-base rebuild fast and incremental.
        if: github.event_name == 'push'
        env:
          GCP_SA_KEY: ${{ secrets.GCP_SA_KEY }}
        run: |
          set -euo pipefail
          CI_BASE="us-central1-docker.pkg.dev/neuron-785695/neuron-ci/ci-base"
          SHA="${GITHUB_SHA:0:8}"

          echo "${GCP_SA_KEY}" > /tmp/gcp-key.json
          gcloud auth activate-service-account --key-file=/tmp/gcp-key.json
          gcloud config set project neuron-785695
          gcloud auth configure-docker us-central1-docker.pkg.dev --quiet

          # Pull existing ci-base:dev (or fall back to :latest on first run)
          BASE_TAG="dev"
          docker pull "${CI_BASE}:dev" || { docker pull "${CI_BASE}:latest" && BASE_TAG="latest"; }

          # Inline Dockerfile — only replaces the El SDK layer
          cat > /tmp/Dockerfile.ci-base-patch << 'EOF'
          ARG BASE
          FROM ${BASE}
          COPY dist/platform/elc                 /opt/el/dist/platform/elc
          COPY dist/bin/elb                       /opt/el/dist/bin/elb
          COPY el-compiler/runtime/el_runtime.c  /opt/el/el-compiler/runtime/el_runtime.c
          COPY el-compiler/runtime/el_runtime.h  /opt/el/el-compiler/runtime/el_runtime.h
          COPY el-compiler/runtime/el_runtime.js /opt/el/el-compiler/runtime/el_runtime.js
          RUN chmod +x /opt/el/dist/platform/elc /opt/el/dist/bin/elb
          EOF

          docker build \
            --build-arg BASE="${CI_BASE}:${BASE_TAG}" \
            --build-arg BUILDKIT_INLINE_CACHE=1 \
            -f /tmp/Dockerfile.ci-base-patch \
            -t "${CI_BASE}:dev" \
            -t "${CI_BASE}:dev-${SHA}" \
            .

          docker push "${CI_BASE}:dev"
          docker push "${CI_BASE}:dev-${SHA}"

          echo "ci-base rebuilt: ${CI_BASE}:dev (${SHA})"
          rm -f /tmp/gcp-key.json
