# Base: python:3.14-bookworm (Debian 12, GCC 12). Migrated from
# python:3.13-bullseye (Debian 11, EOL June 2026) together with the 3.13->3.14
# Python bump. The rgbmatrix fork compiles cleanly here against the image's
# Python 3.14 headers with Cython >= 3.2.5 (verified by the Phase 0 arm64 spike).
# The GCC10 anonymous-param patch in pio_rp1.c compiles under GCC 12.
# Future optimization (deferred): multi-stage build copying only the compiled
# rgbmatrix .so into python:3.14-slim-bookworm (~200MB smaller).
FROM python:3.14-bookworm AS rgbmatrix

# rpi-rgb-led-matrix: jamesawesome/main — Pi5 RP1 support (hzeller#1886, now
# merged upstream) plus three patches: GCC10 anonymous-param fix (pio_rp1.c),
# Pillow shim (graphics.py), SubFill Python binding (core.pyx). Validated
# 2026-04-29 to run on both Pi 4 (BCM2711 GPIO) and Pi 5 (RP1 PIO/RIO).
# As of June 2026 the library defaults the Pi 5 backend to RP1 RIO; the
# backend-selection option is now rp1_pio (1 = force PIO; renamed from
# rp1_rio, which had the inverse meaning).

ENV DEBIAN_FRONTEND=noninteractive

WORKDIR /code

RUN apt-get update && \
    apt-get install -y build-essential git cmake && \
    rm -rf /var/lib/apt/lists/*

# Layer 1: rgbmatrix (only rebuilds if the pinned ref changes)
# Increment RGBMATRIX_CACHE_BUST to force a fresh clone when the fork's main
# branch changes but the clone instruction text hasn't — Docker caches by
# instruction hash, not by remote content.
ARG RGBMATRIX_CACHE_BUST=4
# The fork builds via scikit-build-core (PEP 517), so `pip install .` runs in an
# isolated build env that resolves its own Cython. A plain `pip install Cython`
# here would NOT control that build. PIP_CONSTRAINT is inherited by the isolated
# build-env pip, so it enforces the Cython>=3.2.5 floor (required for Python 3.14
# C-API support) where it actually matters.
RUN cd /opt && \
    git clone --depth=1 --branch main \
        https://github.com/jamesawesome/rpi-rgb-led-matrix.git rgbmatrix-src && \
    cd rgbmatrix-src && \
    printf 'Cython>=3.2.5\n' > /tmp/build-constraints.txt && \
    PIP_CONSTRAINT=/tmp/build-constraints.txt pip install .

# Layer 2: app dependencies (only rebuilds if pyproject.toml changes). After
# installing, snapshot the exact installed versions into a pip constraints file
# (constraints-core.txt) so plugin installs in Layer 2b can pull their own new
# deps but cannot move core's stack. `pip list --format=freeze` renders the
# editable led-ticker as `led-ticker==<v>` (a valid constraint), unlike
# `pip freeze` which emits an unusable `-e ...` line.
FROM rgbmatrix
WORKDIR /code
# pyproject now declares readme + license-files (PEP 639), which hatchling reads
# at install/metadata time — copy README.md + LICENSE alongside pyproject so the
# editable install in this deps-only layer doesn't fail on the missing files.
COPY pyproject.toml README.md LICENSE /code/
RUN pip install --no-cache-dir -e ".[dev]" \
 && pip list --format=freeze > /code/constraints-core.txt

# Layer 2b: external plugins, declared in config/requirements-plugins.txt
# (gitignored; copy config/requirements-plugins.example.txt to create it).
# Installed WITH dependency resolution but constrained to the core versions
# captured in Layer 2 (-c constraints-core.txt): led-ticker is already installed
# so it resolves without hitting PyPI, a plugin may pull its own genuinely-new
# transitive deps, but a plugin that tries to move a core dep fails loudly here
# at build rather than silently at runtime. Installs the live file only — if it
# is absent, no plugins are installed (no fallback to the example). The .tx[t]
# glob is the optional-file trick: it copies the live file if present and is
# skipped if not; the .example is always present so the COPY always succeeds.
# Editing the live file invalidates this cached layer and triggers a reinstall.
COPY config/requirements-plugins.example.txt config/requirements-plugins.tx[t] /code/config/
RUN if [ -f /code/config/requirements-plugins.txt ]; then \
        pip install --no-cache-dir -c /code/constraints-core.txt -r /code/config/requirements-plugins.txt; \
    else \
        echo "No config/requirements-plugins.txt; skipping plugin install (copy the .example to add plugins)"; \
    fi

# Layer 3: app source (rebuilds on any code change — but fast, no pip)
COPY . /code/
RUN pip install --no-deps .

CMD ["led-ticker", "--config", "/code/config/config.toml"]
