# Multi-stage build for tubekit-mcp (HTTP transport behind Traefik).
# Pattern: official uv Docker recommendations.
#
# Base images are pinned by @sha256: digest (audit #20) so rebuilds days apart
# pull byte-identical layers and a changed upstream tag is never ingested
# silently. The human-readable tag is kept for clarity. Refresh the digests
# deliberately (and re-test) to pick up base-OS security patches:
#   docker buildx imagetools inspect ghcr.io/astral-sh/uv:python3.12-bookworm-slim
#   docker buildx imagetools inspect python:3.12-slim-bookworm

# ---- Stage 1: builder ----
FROM ghcr.io/astral-sh/uv:python3.12-bookworm-slim@sha256:e5b65587bce7de595f299855d7385fe7fca39b8a74baa261ba1b7147afa78e58 AS builder

WORKDIR /app

# Bytecode compilation (faster cold start); copy mode so the venv is relocatable.
ENV UV_COMPILE_BYTECODE=1 \
    UV_LINK_MODE=copy

# Dependency layer first — cached until pyproject.toml/uv.lock change.
# README.md is referenced by [project].readme, so hatchling needs it to build.
COPY pyproject.toml uv.lock README.md ./
RUN --mount=type=cache,target=/root/.cache/uv \
    uv sync --frozen --no-install-project --no-dev

# Project layer — install tubekit itself.
COPY src/ ./src/
RUN --mount=type=cache,target=/root/.cache/uv \
    uv sync --frozen --no-dev

# ---- Stage 2: runtime ----
FROM python:3.12-slim-bookworm@sha256:76d4b7b6305788c6b4c6a19d6a22a3921bf802e9af4d5e1e5bd771208dba74bf AS runtime

WORKDIR /app

# Non-root runtime user (uid 1000 owns the bind-mounted /data state).
RUN groupadd --gid 1000 tubekit && \
    useradd --uid 1000 --gid 1000 --create-home --shell /bin/bash tubekit

# Application (venv + editable src) from the builder. The migrations live
# INSIDE the package (src/tubekit/migrations), so they arrive with it; the ini
# is only kept for the `alembic upgrade head` deploy step, and its
# script_location resolves the packaged copy (tubekit:migrations).
COPY --from=builder --chown=tubekit:tubekit /app /app
COPY --chown=tubekit:tubekit alembic.ini ./alembic.ini

ENV PATH="/app/.venv/bin:$PATH" \
    PYTHONUNBUFFERED=1 \
    PYTHONDONTWRITEBYTECODE=1 \
    TUBEKIT_STATE_DB_PATH=/data/state.db \
    TUBEKIT_MASTER_KEY_PATH=/data/master.key

USER tubekit

EXPOSE 8080

# /healthz is GET and answers 200 without a credential — a valid liveness probe.
# Anonymously it returns liveness only; the per-channel detail needs a Bearer
# (http_main.py). A bad credential degrades the payload rather than returning
# 401, precisely so this check keeps passing.
HEALTHCHECK --interval=30s --timeout=10s --start-period=10s --retries=3 \
    CMD python -c "import urllib.request; urllib.request.urlopen('http://localhost:8080/healthz')" || exit 1

# Default host is 127.0.0.1 in code; override to 0.0.0.0 for Traefik upstream.
CMD ["tubekit-mcp-http", "--host", "0.0.0.0", "--port", "8080", "--channels", "/data/channels"]
