# syntax=docker/dockerfile:1
# ─────────────────────────────────────────────────────────────────────────────
# Sanad Package 2 — Premium Communication.
#
# SELF-CONTAINED: builds from a public base with NO sanad-base / Sanad_Core / sibling
# checkout. The engine is vendored from SanadV3 at ./vendor/Sanad, the license/bus
# lib at ./vendor/sanad_pkg, and the flat BLE Mask lib at ./vendor/mask.
#
#   Build context MUST be THIS package dir:
#     docker build -t sanad-p2:latest .
#   (docker-compose.yml uses context: .  ;  Jetson Docker without buildx:
#     DOCKER_BUILDKIT=0 docker build -t sanad-p2:latest .)
# ─────────────────────────────────────────────────────────────────────────────
ARG BASE_OS_IMAGE=python:3.10-slim-bookworm
FROM ${BASE_OS_IMAGE}

ENV DEBIAN_FRONTEND=noninteractive \
    PYTHONUNBUFFERED=1 \
    PYTHONDONTWRITEBYTECODE=1 \
    PYTHONPATH=/app
WORKDIR /app

# System deps: audio (shared) + PortAudio/toolchain (pyaudio) + BlueZ/D-Bus (bleak
# talks to the host BlueZ over D-Bus for the LED mask) + iproute2 (`ip`).
# `ip` is REQUIRED for chest ('builtin') audio: voice/audio_io.py _find_g1_local_ip()
# runs `ip -4 -o addr` to find the host's 192.168.123.x address for the G1 chest-mic
# UDP multicast — without it the live voice subprocess crashes on FileNotFoundError.
RUN apt-get update && apt-get install -y --no-install-recommends \
        ca-certificates libsndfile1 alsa-utils pulseaudio-utils iproute2 \
        portaudio19-dev libportaudio2 build-essential python3-dev \
        bluez libdbus-1-3 libglib2.0-0 \
    && rm -rf /var/lib/apt/lists/*

# Python deps (P1 comms + bleak==0.22.3 + Pillow).
COPY requirements.txt /tmp/requirements.txt
RUN python3 -m pip install --no-cache-dir --upgrade pip \
 && python3 -m pip install --no-cache-dir -r /tmp/requirements.txt

# ── Optional: Unitree SDK — G1 chest (builtin) audio + arm over DDS ───────────
# WITH_UNITREE_SDK=1 (default) builds CycloneDDS + installs unitree_sdk2_python.
# Wrapped so a failure NEVER breaks the image — chest audio is then unavailable
# (use SANAD_AUDIO_PROFILE=plugged); plugged USB audio always works.
# NOTE: build the FULL CycloneDDS (do NOT pass -DBUILD_IDLC=NO) — the `cyclonedds`
# Python binding's find_package(CycloneDDS) needs idlc, else "Could not locate
# cyclonedds". Pin the binding to match the 0.10.x C library.
ARG WITH_UNITREE_SDK=1
ENV CYCLONEDDS_HOME=/usr/local \
    LD_LIBRARY_PATH=/usr/local/lib
RUN if [ "$WITH_UNITREE_SDK" = "1" ]; then \
      ( set -eux; \
        apt-get update; \
        apt-get install -y --no-install-recommends git cmake build-essential; \
        git clone --depth 1 -b releases/0.10.x https://github.com/eclipse-cyclonedds/cyclonedds /tmp/cyclonedds; \
        cmake -S /tmp/cyclonedds -B /tmp/cyclonedds/build -DCMAKE_INSTALL_PREFIX=/usr/local; \
        cmake --build /tmp/cyclonedds/build --target install -j"$(nproc)"; \
        CYCLONEDDS_HOME=/usr/local CMAKE_PREFIX_PATH=/usr/local python3 -m pip install --no-cache-dir "cyclonedds==0.10.2"; \
        git clone --depth 1 https://github.com/unitreerobotics/unitree_sdk2_python /opt/unitree_sdk2_python; \
        python3 -m pip install --no-cache-dir -e /opt/unitree_sdk2_python; \
        python3 -c "import unitree_sdk2py; print('unitree_sdk2py OK')"; \
        rm -rf /tmp/cyclonedds /var/lib/apt/lists/*; \
      ) || echo "WARN[P2]: Unitree SDK build failed — chest (builtin) audio unavailable; use SANAD_AUDIO_PROFILE=plugged"; \
    else echo "WITH_UNITREE_SDK=0 — skipping Unitree SDK (USB/plugged audio only)"; fi

# License/bus shim + PUBLIC verification key (vendored).
COPY vendor/sanad_pkg /app/sanad_pkg
RUN mkdir -p /etc/sanad && cp /app/sanad_pkg/pubkey.ed25519 /etc/sanad/pubkey.ed25519

# Flat BLE Mask lib on its OWN path (it uses flat imports: `import mask`, `faces`).
COPY vendor/mask /app/mask

# Canonical engine (vendored from SanadV3 — face/ + evolved voice/audio/arm).
COPY vendor/Sanad /app/Sanad

# P2 launcher + routes + entrypoint + config.
COPY app_p2.py     /app/app_p2.py
COPY routes_p2.py  /app/routes_p2.py
COPY entrypoint.sh /app/entrypoint.sh
COPY config        /app/pkg2_config
RUN chmod +x /app/entrypoint.sh

# Ship KEYLESS — blank any Gemini key baked into the vendored config.
COPY strip_key.py /tmp/strip_key.py
RUN python3 /tmp/strip_key.py && rm -f /tmp/strip_key.py

# Sanity: vendored namespace + mask lib import paths resolve.
RUN python3 - <<'PY'
import importlib.util as u, sys
ok = all(u.find_spec(m) for m in ("sanad_pkg.license", "sanad_pkg.bus", "Sanad"))
sys.path.insert(0, "/app/mask")
mask_ok = all(u.find_spec(m) for m in ("mask", "faces", "faceanim"))
print("P2 self-contained: vendored Sanad+sanad_pkg importable:", ok, "| mask lib:", mask_ok)
sys.exit(0 if (ok and mask_ok) else 1)
PY

ENV SANAD_PACKAGE=P2 \
    SANAD_DASHBOARD_PORT=8012 \
    SANAD_DASHBOARD_HOST=0.0.0.0 \
    SANAD_MASK_DIR=/app/mask \
    SANAD_LICENSE=/etc/sanad/sanad.lic \
    SANAD_PUBKEY=/etc/sanad/pubkey.ed25519
EXPOSE 8012
ENTRYPOINT ["/app/entrypoint.sh"]
