# Three stages (react.md "The frontend coupling point"): the python stage dumps
# the OpenAPI schema, the node stage generates the TS client from it and builds
# the SPA, the runtime stage serves API + static SPA from one Python process.
# Nothing generated is ever committed — a committed client drifts silently.

# --- 1. dump the OpenAPI schema -------------------------------------------
FROM python:3.12-slim AS schema
WORKDIR /app_root
COPY requirements.txt ./
RUN pip install --no-cache-dir -r requirements.txt
COPY app/ ./app/
# config.py stays importable with no environment — this import is why.
RUN python -c "import json; from app.main import app; \
    open('/openapi.json','w').write(json.dumps(app.openapi()))"

# --- 2. generate the client + build the frontend --------------------------
FROM node:20-alpine AS frontend
WORKDIR /build
COPY frontend/package.json frontend/package-lock.json* ./
RUN npm ci || npm install
COPY frontend/ ./
COPY --from=schema /openapi.json /openapi.json
# Generated types land in src/api/schema.d.ts (gitignored; never committed).
RUN npx openapi-typescript /openapi.json -o src/api/schema.d.ts \
 && npm run build && mkdir -p /out && cp -r ../app/static /out/static

# --- 3. runtime ------------------------------------------------------------
FROM python:3.12-slim AS runtime
WORKDIR /app_root
ENV PYTHONUNBUFFERED=1 PYTHONDONTWRITEBYTECODE=1

COPY requirements.txt ./
RUN pip install --no-cache-dir -r requirements.txt

COPY app/ ./app/
COPY alembic.ini ./
COPY alembic/ alembic/
COPY --from=frontend /out/static ./app/static

# Data dir (mockup bundles, attachments; SQLite fallback outside compose).
ENV APP_DATA_DIR=/data
RUN mkdir -p /data

# Bind to all interfaces INSIDE the container; the gateway maps the published
# port to 172.17.0.1 on the host, so Caddy (and only Caddy) can reach it.
EXPOSE 3000
# Migrations run at container start, then the server. `exec` so uvicorn is PID 1.
CMD ["sh", "-c", "alembic upgrade head && exec uvicorn app.main:app --host 0.0.0.0 --port 3000"]
