# =========================================================================
# Servicio de cálculo OmniAstro
# =========================================================================
#
# Dos etapas, y la razón de la primera es concreta: `pyswisseph` compila
# extensiones en C. Compilarlas exige gcc y cabeceras de Python que pesan
# ~250 MB y que la imagen final no necesita para nada.
#
# La segunda razón, más importante: aquí es donde se descargan los ficheros
# .se1. Sin ellos, Swiss Ephemeris cae a Moshier EN SILENCIO y Quirón y los
# asteroides pasan a lanzar excepción en tiempo de ejecución. Ver
# app/services/ephemeris/config.py.

# ---------- etapa de compilación ----------
FROM python:3.11-slim AS builder

RUN apt-get update && apt-get install -y --no-install-recommends \
        gcc python3-dev \
    && rm -rf /var/lib/apt/lists/*

WORKDIR /build
COPY requirements.txt .
RUN pip install --no-cache-dir --prefix=/install -r requirements.txt

# ---------- ficheros de efemérides ----------
FROM debian:bookworm-slim AS ephemeris

RUN apt-get update && apt-get install -y --no-install-recommends curl ca-certificates \
    && rm -rf /var/lib/apt/lists/*

WORKDIR /ephe

# 1800–2399 cubre cualquier nacimiento humano que vayas a servir. Unos 2 MB.
# Si necesitas rango histórico más amplio, añade sepl_12.se1 / semo_12.se1
# (1200–1800) y sus equivalentes; cada bloque son otros ~2 MB.
ARG EPHE_BASE=https://www.astro.com/ftp/swisseph/ephe
RUN for f in sepl_18.se1 semo_18.se1 seas_18.se1; do \
      curl -fsSL --retry 3 -o "$f" "$EPHE_BASE/$f"; \
    done \
    # Comprobación de integridad: un fichero .se1 válido no es HTML de error
    # ni un archivo vacío. Sin esto, un 404 del CDN produciría una imagen que
    # arranca bien y calcula mal.
    && for f in *.se1; do \
         test -s "$f" || { echo "FALLO: $f vacío"; exit 1; }; \
         head -c 4 "$f" | grep -q '<' && { echo "FALLO: $f es HTML"; exit 1; }; \
       done \
    && ls -l /ephe

# ---------- imagen final ----------
FROM python:3.11-slim

COPY --from=builder /install /usr/local
COPY --from=ephemeris /ephe /usr/share/swisseph

ENV SWISSEPH_PATH=/usr/share/swisseph \
    PYTHONUNBUFFERED=1 \
    PYTHONDONTWRITEBYTECODE=1

# Usuario sin privilegios. El servicio no escribe en disco ni guarda datos
# personales: recibe un instante, devuelve una carta y olvida.
RUN useradd --create-home --shell /usr/sbin/nologin omniastro
WORKDIR /app
COPY --chown=omniastro:omniastro app ./app
USER omniastro

EXPOSE 8000

# El healthcheck comprueba `degraded`, no solo que responda. Un despliegue sin
# ficheros .se1 devuelve 200 a todo y calcula cartas de aspecto correcto hasta
# que alguien pide Quirón — justo el fallo que este Dockerfile evita.
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
  CMD python -c "import urllib.request, json, sys; \
d = json.load(urllib.request.urlopen('http://localhost:8000/health')); \
sys.exit(1 if d['ephemeris']['degraded'] else 0)"

CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]
