# Operator reference template; generated app deployment files are owned by DownloadAgent.
# ============================================================================
# {{APP_NAME}} — Multi-stage Docker image
# ============================================================================
# Stage 1 builds the React/Vite SPA using the web_shell source bundled in the
#   mozaiks pip package (MANIFEST.in ships vite source + chat-ui/src; dist is
#   excluded so we always build fresh with the correct PLATFORM_PATH).
# Stage 2 installs Python dependencies.
# Stage 3 assembles the runtime: nginx (serves SPA + proxies API) + uvicorn.
#
# Template variables substituted at generation time:
#   {{APP_NAME}}       — App slug used in image tags and container names
#   {{MOZAIKS_VERSION}} — Pinned mozaiks package version (e.g. 0.1.10)
#   {{OIDC_CLIENT_ID}} — OIDC client ID baked into the Vite bundle
#
# Required build args (baked into the JS bundle at build time):
#   VITE_OIDC_AUTHORITY or VITE_OIDC_DISCOVERY_URL — OIDC authority/discovery URL
#   VITE_OIDC_REDIRECT_URI — post-login callback (e.g. https://app.host/auth/callback)
#
# Optional build args:
#   VITE_API_URL        — API base URL; empty = same-origin (default)
#   VITE_OIDC_CLIENT_ID — OIDC client (default: {{OIDC_CLIENT_ID}})
#   VITE_OIDC_DISCOVERY_URL — explicit OIDC discovery URL; may replace authority
#   VITE_OIDC_SCOPE     — browser login scope (default: openid profile email)
#   MOZAIKS_VERSION     — fallback mozaiks pip version when requirements.txt
#                         does not declare a mozaiks dependency (default: {{MOZAIKS_VERSION}})
#   LOCAL_MOZAIKS_WHEEL — path to a pre-built .whl in the build context;
#                         use for pre-release or local-dev smoke tests


# ── Stage 1: Frontend build ────────────────────────────────────────────────────
FROM node:20-slim AS frontend-builder

RUN apt-get update && apt-get install -y --no-install-recommends \
        python3-minimal python3-pip ca-certificates git \
    && rm -rf /var/lib/apt/lists/*

# Install mozaiks to access web_shell Vite source, chat-ui/src, and factory_app/.
# MANIFEST.in ships these sources but prunes dist/ and node_modules/.
ARG MOZAIKS_VERSION={{MOZAIKS_VERSION}}
# LOCAL_MOZAIKS_WHEEL: when set, install from the wheel file in the build context
# instead of PyPI. Used for pre-release or local-dev smoke tests.
ARG LOCAL_MOZAIKS_WHEEL=
COPY . /build-context-src/
RUN if [ -n "$LOCAL_MOZAIKS_WHEEL" ]; then \
        pip3 install --no-cache-dir --break-system-packages "/build-context-src/${LOCAL_MOZAIKS_WHEEL}" 2>/dev/null \
        || pip3 install --no-cache-dir "/build-context-src/${LOCAL_MOZAIKS_WHEEL}"; \
    else \
        MOZAIKS_REQUIREMENT="$(grep -E '^(mozaiks==|mozaiks[[:space:]]*@)' /build-context-src/requirements.txt | head -n 1)" \
        && if [ -z "$MOZAIKS_REQUIREMENT" ]; then MOZAIKS_REQUIREMENT="mozaiks==${MOZAIKS_VERSION}"; fi \
        && pip3 install --no-cache-dir --break-system-packages "$MOZAIKS_REQUIREMENT" 2>/dev/null \
        || pip3 install --no-cache-dir "$MOZAIKS_REQUIREMENT"; \
    fi

# Locate the installed web_shell package root and site-packages directory.
RUN python3 -c \
        "import web_shell, os; print(os.path.dirname(os.path.abspath(web_shell.__file__)))" \
        > /tmp/ws_src \
    && python3 -c "import site; print(site.getsitepackages()[0])" > /tmp/site_pkgs

# Copy web_shell source to a writable workspace so npm can write node_modules.
RUN cp -r "$(cat /tmp/ws_src)" /build-frontend

# Install Vite and all frontend dependencies.
RUN cd /build-frontend && npm install

# Add app-specific npm packages here if your app UI requires them.
# Example: RUN cd /build-frontend && npm install --no-save --package-lock=false some-package@1.0.0

# Copy the app bundle — PLATFORM_PATH resolves @platform/extensions from here.
COPY app/ /build-app/
RUN ln -s /build-frontend/node_modules /build-app/node_modules

# VITE vars are baked into the JS bundle at build time.
ARG VITE_API_URL=
ARG VITE_OIDC_AUTHORITY
ARG VITE_OIDC_CLIENT_ID={{OIDC_CLIENT_ID}}
ARG VITE_OIDC_DISCOVERY_URL=
ARG VITE_OIDC_SCOPE="openid profile email"
ARG VITE_OIDC_REDIRECT_URI
ARG VITE_MOCK_MODE=false

# Build the SPA.
# PLATFORM_PATH          → vite.config.js resolves @platform/extensions to app/ui/index.js
# MOZAIKS_FACTORY_APP_PATH / MOZAIKS_CHAT_UI_PATH → explicit paths for pip-installed assets.
# The wheel installs chat-ui as `mozaiks_chat_ui/`; editable installs may use `chat-ui/`.
# Detect whichever exists at the site-packages root.
RUN SITE="$(cat /tmp/site_pkgs)" \
    && if [ -d "${SITE}/chat-ui/src" ]; then \
         CHAT_UI_PATH="${SITE}/chat-ui"; \
       elif [ -d "${SITE}/mozaiks_chat_ui/src" ]; then \
         CHAT_UI_PATH="${SITE}/mozaiks_chat_ui"; \
       else \
         echo "ERROR: could not find chat-ui source at ${SITE}/chat-ui or ${SITE}/mozaiks_chat_ui" >&2; \
         exit 1; \
       fi \
    && cd /build-frontend \
    && PLATFORM_PATH=/build-app \
       MOZAIKS_FACTORY_APP_PATH="${SITE}/factory_app" \
       MOZAIKS_CHAT_UI_PATH="${CHAT_UI_PATH}" \
       VITE_API_URL="${VITE_API_URL}" \
       VITE_OIDC_AUTHORITY="${VITE_OIDC_AUTHORITY}" \
       VITE_OIDC_CLIENT_ID="${VITE_OIDC_CLIENT_ID}" \
       VITE_OIDC_DISCOVERY_URL="${VITE_OIDC_DISCOVERY_URL}" \
       VITE_OIDC_SCOPE="${VITE_OIDC_SCOPE}" \
       VITE_OIDC_REDIRECT_URI="${VITE_OIDC_REDIRECT_URI}" \
       VITE_MOCK_MODE="${VITE_MOCK_MODE}" \
       npx vite build


# ── Stage 2: Python dependency installer ─────────────────────────────────────
FROM python:3.11-slim AS py-builder

WORKDIR /build

RUN apt-get update && apt-get install -y --no-install-recommends git ca-certificates \
    && rm -rf /var/lib/apt/lists/* \
    && pip install --upgrade pip

COPY requirements.txt .
# LOCAL_MOZAIKS_WHEEL: optional path to a pre-built .whl inside the build context.
# When set, that wheel is installed directly instead of fetching from PyPI.
# This allows staging smoke tests against an editable-install version that has
# not yet been published. Leave empty for standard production builds.
ARG LOCAL_MOZAIKS_WHEEL=
COPY . /build-src/
RUN if [ -n "$LOCAL_MOZAIKS_WHEEL" ]; then \
        pip install --prefix=/opt/venv --no-cache-dir "/build-src/${LOCAL_MOZAIKS_WHEEL}" \
        && pip install --prefix=/opt/venv --no-cache-dir \
            $(grep -Ev '^(mozaiks==|mozaiks[[:space:]]*@|#|$)' requirements.txt | tr '\n' ' '); \
    else \
        pip install --prefix=/opt/venv --no-cache-dir -r requirements.txt; \
    fi


# ── Stage 3: Runtime ──────────────────────────────────────────────────────────
FROM python:3.11-slim AS runtime

RUN apt-get update && apt-get install -y --no-install-recommends nginx curl \
    && rm -rf /var/lib/apt/lists/*

# Keep the Python packaging toolchain in the final runtime image current.
RUN python -m pip install --no-cache-dir --upgrade pip setuptools wheel

ENV PYTHONDONTWRITEBYTECODE=1 \
    PYTHONUNBUFFERED=1 \
    PATH="/opt/venv/bin:$PATH" \
    PYTHONPATH="/opt/venv/lib/python3.11/site-packages" \
    PORT=8000 \
    ENV=production \
    LOGS_BASE_DIR=/app/logs \
    LOGS_AS_JSON=true \
    LOG_LEVEL=INFO \
    MOZAIKS_STARTUP_CHECKS=strict \
    MOZAIKS_DATABASE_STARTUP_POLICY=required

WORKDIR /app

# Python packages
COPY --from=py-builder /opt/venv /opt/venv

# Pre-built SPA
COPY --from=frontend-builder /build-frontend/dist /opt/web_shell_dist/

# Application source
COPY app/           ./app/
COPY workflows/     ./workflows/
COPY build_context/ ./build_context/

# nginx: replace main config and install the SPA + API/WS proxy server block.
COPY infra/nginx-main.conf /etc/nginx/nginx.conf
COPY infra/nginx.conf /etc/nginx/conf.d/{{APP_NAME}}.conf
RUN rm -f /etc/nginx/sites-enabled/default \
    && mkdir -p /var/cache/nginx /run/nginx /app/logs \
    && chown -R www-data:www-data /var/cache/nginx /run/nginx /var/log/nginx

# Non-root user for the application
RUN useradd --no-create-home --shell /bin/false {{APP_NAME}} \
    && chown -R {{APP_NAME}}:{{APP_NAME}} /app /opt/web_shell_dist

COPY infra/entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh

EXPOSE 8000

# Health check goes through nginx → uvicorn to validate the full request path.
HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=5 \
    CMD curl -sf http://localhost:8000/api/health || exit 1

# nginx runs as root (needs /run/nginx); uvicorn subprocess runs as {{APP_NAME}}.
CMD ["/entrypoint.sh"]
