# syntax=docker/dockerfile:1.7
#
# neuronetz-gateway — multi-stage image.
#
#   builder stage : installs dependencies into a self-contained virtualenv using uv.
#   runtime stage : copies the venv + source, drops to a NON-ROOT user, contains
#                   no build tools, and runs `python -m neuronetz_gateway`.
#
# uv is pulled from the official distroless image so we don't need network access
# to `pip install uv`. Dependencies come from pyproject.toml (+ uv.lock if present).

# ----------------------------------------------------------------------------
# Stage 1 — builder
# ----------------------------------------------------------------------------
FROM python:3.12-slim AS builder

# Bring in the `uv` binary from its official image.
COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/uv

ENV UV_LINK_MODE=copy \
    UV_COMPILE_BYTECODE=1 \
    UV_PYTHON_DOWNLOADS=never \
    # Create the project venv at a stable, copyable location.
    VIRTUAL_ENV=/opt/venv \
    PATH=/opt/venv/bin:$PATH

WORKDIR /app

# Create the target virtualenv up front so uv installs into it.
RUN uv venv /opt/venv

# Dependency layer: copy only the manifest(s) first for better caching.
# uv.lock is optional in Phase 1 — the wildcard makes COPY succeed either way.
COPY pyproject.toml ./
COPY uv.loc[k] ./

# Install dependencies. If a lockfile is present `uv sync` honours it; otherwise
# we fall back to resolving straight from pyproject.toml. Either way the build
# does NOT fail when the lock is absent.
RUN --mount=type=cache,target=/root/.cache/uv \
    if [ -f uv.lock ]; then \
        uv sync --frozen --no-install-project --no-dev ; \
    else \
        uv pip install --python /opt/venv/bin/python -r pyproject.toml ; \
    fi

# Now copy the application source and install the project itself into the venv.
# README.md + LICENSE are required by the build backend (pyproject `readme`/license).
COPY README.md LICENSE ./
COPY src ./src
COPY alembi[c] ./alembic
COPY alembic.in[i] ./
RUN --mount=type=cache,target=/root/.cache/uv \
    uv pip install --python /opt/venv/bin/python --no-deps .

# ----------------------------------------------------------------------------
# Stage 2 — runtime
# ----------------------------------------------------------------------------
FROM python:3.12-slim AS runtime

# Runtime-only OS packages: curl is used by the compose healthcheck.
RUN apt-get update \
    && apt-get install -y --no-install-recommends curl \
    && rm -rf /var/lib/apt/lists/*

# Non-root user.
RUN groupadd --system --gid 10001 gateway \
    && useradd --system --uid 10001 --gid gateway --home-dir /app --shell /usr/sbin/nologin gateway

ENV VIRTUAL_ENV=/opt/venv \
    PATH=/opt/venv/bin:$PATH \
    PYTHONUNBUFFERED=1 \
    PYTHONDONTWRITEBYTECODE=1 \
    GATEWAY_BIND_HOST=0.0.0.0 \
    GATEWAY_BIND_PORT=8080

WORKDIR /app

# Copy the fully-populated virtualenv and the application from the builder.
COPY --from=builder /opt/venv /opt/venv
COPY --from=builder /app/src ./src
# alembic assets are optional during early scaffolding; copy if present.
COPY --from=builder /app/alembi[c] ./alembic
COPY --from=builder /app/alembic.in[i] ./

# Drop privileges. No build tools are present in this stage.
USER gateway

EXPOSE 8080

# Liveness probe target lives at /healthz (see SPEC §6.4).
HEALTHCHECK --interval=15s --timeout=3s --start-period=20s --retries=5 \
    CMD curl -fsS "http://127.0.0.1:${GATEWAY_BIND_PORT}/healthz" || exit 1

# Default command: run the server. Compose overrides this in dev to run
# `alembic upgrade head` first (see docker-compose.dev.yml).
CMD ["python", "-m", "neuronetz_gateway"]
