# syntax=docker/dockerfile:1.7
#
# Multi-stage build for the sanoptis_admin Reflex app.
#
#   Builder stage — python:3.12 + pixi
#     Installs the `admin` pixi environment and uses `reflex export` to
#     produce the static SPA bundle under /app/.web/build/client.
#
#   Runtime stage — python:3.12-slim + Caddy
#     Installs the `.[admin]` extras plus azure-identity via pip into the
#     system site-packages (no pixi in runtime — pixi envs bake absolute
#     paths, so copying them between stages is brittle and ~1 GB larger
#     than pip install of the same wheels). Caddy fronts the public port
#     and reverse-proxies WebSocket + upload endpoints to the Reflex
#     backend on localhost:8001.
#
# Build:   docker build -t sanoptis-admin:latest .
# Run:     docker run -p 8000:8000 \
#              -e DATABASE_URL=postgresql+psycopg://... \
#              -e ENTRA_CLIENT_SECRET=... \
#              -e SESSION_SECRET=... \
#              -e DATA_ENCRYPTION_KEY=... \
#              -e ADMIN_UPNS=matthias@example.com,other@example.com \
#              -e ENTRA_TENANT_ID=... -e ADMIN_APP_CLIENT_ID=... \
#              -e API_SCOPE=api://<client>/access_as_user \
#              -e PUBLIC_BASE_URL=https://... \
#              sanoptis-admin:latest
#
# In Azure, every secret-backed env var comes from a Container App native
# Key Vault secret reference (see infra/azure/admin_app.tf). The older
# Azure Files SMB share + /app/state volume mount has been removed — there
# is no per-container persistent disk; all state lives in Postgres.

# --- Build stage: compile frontend -------------------------------------
FROM python:3.12 AS builder

# pixi requires curl + ca-certificates + git; unzip is handy for extracting
# the installer archive on arm/glibc platforms.
RUN apt-get update && \
    apt-get install -y --no-install-recommends curl ca-certificates git unzip && \
    rm -rf /var/lib/apt/lists/*

# Install pixi to a well-known location so we can invoke it unambiguously.
ENV PIXI_HOME=/opt/pixi \
    PATH=/opt/pixi/bin:$PATH
RUN curl -fsSL https://pixi.sh/install.sh | bash -s -- --no-modify-path

WORKDIR /app

# Copy lockfile + manifest first so the solve layer is cached when only
# source changes.
COPY pyproject.toml pixi.toml pixi.lock README.md LICENSE ./
COPY alembic.ini ./
COPY rxconfig.py ./

# Copy the source packages that the pixi install step needs (editable
# install of sanoptis-auth).
COPY sanoptis_auth/ sanoptis_auth/
COPY sanoptis_admin/ sanoptis_admin/

# Solve + install the `admin` pixi environment.
RUN pixi install -e admin

# Reflex requires DATABASE_URL to be importable at rxconfig-load time even
# for a frontend-only export. Use a throwaway SQLite URL — the builder
# never writes a row.
ENV DATABASE_URL=sqlite:////tmp/build.db

# Initialise Reflex (bun + node toolchain) and export the production SPA.
# `reflex init` reads rxconfig.py to discover the app; the source tree is
# already copied in above, so this is an "ensure toolchain installed"
# step, not a scaffolding one.
RUN pixi run -e admin reflex init --loglevel info
RUN pixi run -e admin reflex export --frontend-only --no-zip

# Normalise the exported bundle path: Reflex writes to .web/build/client;
# keep that canonical layout.
RUN test -d /app/.web/build/client


# --- Runtime stage: slim image with Caddy + Python backend -------------
FROM python:3.12-slim AS runtime

# Caddy fronts the public port; curl is used by the Docker HEALTHCHECK.
RUN apt-get update && \
    apt-get install -y --no-install-recommends caddy curl ca-certificates && \
    rm -rf /var/lib/apt/lists/*

WORKDIR /app

# Copy the source tree required by the pip install below. README and
# LICENSE are required by hatchling's build step.
COPY pyproject.toml README.md LICENSE ./
COPY alembic.ini rxconfig.py ./
COPY sanoptis_auth/ sanoptis_auth/
COPY sanoptis_admin/ sanoptis_admin/

# Install the package + admin extras + azure-identity (used by the
# entrypoint to fetch Key Vault secrets via managed identity). No default
# version pin on azure-identity — floated to the latest compatible with
# azure-core >=1.30 which the library already depends on.
RUN pip install --no-cache-dir ".[admin]" "azure-identity>=1.17,<2"

# Pre-built static frontend from the builder.
COPY --from=builder /app/.web/build/client /srv

# Caddy configuration.
COPY Caddyfile /etc/caddy/Caddyfile

# Boot-time scripts (KV fetch + combined Caddy + Reflex launch).
COPY scripts/ /app/scripts/
RUN chmod +x /app/scripts/entrypoint.sh

# Non-root user. State directory is a volume mount point in production
# (Azure Files) so the container can restart without losing the SQLite DB.
RUN useradd --create-home --uid 1000 appuser && \
    mkdir -p /app/state /app/config /app/.web/build /app/.states \
             /data/caddy /config/caddy && \
    cp -r /srv /app/.web/build/client && \
    chown -R appuser:appuser /app /data/caddy /config/caddy /etc/caddy

USER appuser

# Caddy + XDG dirs; PYTHONPATH so imports work from /app without editable install.
ENV XDG_DATA_HOME=/data \
    XDG_CONFIG_HOME=/config \
    PYTHONPATH=/app \
    WEBSITES_PORT=8000

EXPOSE 8000

HEALTHCHECK --interval=30s --timeout=5s --start-period=30s --retries=3 \
    CMD curl -fsS http://localhost:8000/ping || exit 1

# Reflex does not consistently forward SIGTERM to the backend process; the
# Container Apps runtime will still terminate the pod via SIGKILL after
# the grace period, so we fall through to that deliberately.
STOPSIGNAL SIGKILL

ENTRYPOINT ["/app/scripts/entrypoint.sh"]
