# Multi-stage Dockerfile for SousChef UI - Production Ready
# Optimised for security, robustness, and Docker registry publishing

ARG PYTHON_VERSION=3.13
ARG POETRY_VERSION=2.3.4

# ============================================================================
# Base Stage - Common configuration for all stages
# ============================================================================
FROM python:${PYTHON_VERSION}-alpine3.22 AS base

ARG PYTHON_VERSION

# Metadata for Docker registry and CI/CD
LABEL org.opencontainers.image.title="SousChef" \
      org.opencontainers.image.description="Web UI for converting Chef cookbooks to Ansible playbooks" \
      org.opencontainers.image.authors="SousChef Contributors" \
      org.opencontainers.image.licenses="MIT" \
      org.opencontainers.image.vendor="SousChef Project" \
      org.opencontainers.image.url="https://github.com/kpeacocke/souschef" \
      org.opencontainers.image.documentation="https://kpeacocke.github.io/souschef/" \
      org.opencontainers.image.source="https://github.com/kpeacocke/souschef" \
    org.opencontainers.image.base.name="python:${PYTHON_VERSION}-alpine3.22"

# Set environment variables for Python and Streamlit
ENV PYTHONUNBUFFERED=1 \
    PYTHONDONTWRITEBYTECODE=1 \
    PIP_NO_CACHE_DIR=1 \
    PIP_DISABLE_PIP_VERSION_CHECK=1 \
    PIP_ROOT_USER_ACTION=ignore \
    PYTHONPATH=/app \
    STREAMLIT_SERVER_HEADLESS=true \
    STREAMLIT_BROWSER_GATHER_USAGE_STATS=false \
    STREAMLIT_SERVER_ENABLE_CORS=true \
    STREAMLIT_SERVER_ENABLE_XSRF_PROTECTION=true \
    STREAMLIT_SERVER_ENABLE_STATIC_SERVING=false \
    STREAMLIT_LOGGER_LEVEL=INFO \
    STREAMLIT_SERVER_LOGGER_LEVEL=INFO \
    STREAMLIT_SERVER_MAX_UPLOAD_SIZE=200

# Install runtime dependencies only (Alpine)
RUN apk update && apk add --no-cache \
    ca-certificates \
    curl \
    # Runtime libraries (not -dev packages)
    libffi \
    libpq \
    && apk upgrade --no-cache \
    && addgroup -g 1001 -S app \
    && adduser -u 1001 -S app -G app \
    && mkdir -p /app \
    && chown -R app:app /app

# Set work directory
WORKDIR /app

# ============================================================================
# Builder Stage - Install dependencies in isolated layer
# ============================================================================
FROM base AS builder

ARG POETRY_VERSION
ARG PYTHON_VERSION

# Install build-time dependencies (not needed in runtime image)
RUN apk add --no-cache \
    gcc \
    git \
    libffi-dev \
    musl-dev \
    postgresql17-dev

# Copy dependency files first (for better layer caching)
COPY pyproject.toml poetry.lock ./

# Upgrade pip to pick up security fixes (pinned for reproducibility)
RUN python -m pip install --no-cache-dir --only-binary :all: "pip==26.1.1"

# Install Poetry with pinned version for reproducibility
RUN pip install --no-cache-dir --only-binary :all: "poetry==$POETRY_VERSION"

# Configure poetry to not create virtual environment (install globally)
RUN poetry config virtualenvs.create false

# Install production dependencies with all required extras
# Use --no-interaction for automated environments
# --only=main installs only main dependencies (excludes dev), --extras specifies optional packages
RUN poetry install \
    --only=main \
    --extras "ui ai storage" \
    --no-interaction \
    --no-root && \
    poetry cache clear pypi --all || true

# Copy site-packages to a predictable location for the runtime stage
# This avoids glob expansion issues in COPY commands
RUN PYTHON_MAJOR_MINOR=$(python3 -c 'import sys; print(f"{sys.version_info.major}.{sys.version_info.minor}")') && \
    cp -r "/usr/local/lib/python${PYTHON_MAJOR_MINOR}/site-packages" /tmp/runtime-site-packages

# ============================================================================
# Runtime Stage - Minimal production image
# ============================================================================
FROM base AS production

ARG PYTHON_VERSION

# Upgrade pip to pick up security fixes (pinned for reproducibility)
RUN python -m pip install --no-cache-dir --only-binary :all: "pip==26.1.1"

# Copy site-packages from builder (at predictable location, no glob expansion)
COPY --from=builder --chown=root:root /tmp/runtime-site-packages /tmp/site-packages

# Install to final location
RUN PYTHON_MAJOR_MINOR=$(python3 -c 'import sys; print(f"{sys.version_info.major}.{sys.version_info.minor}")') && \
    rm -rf "/usr/local/lib/python${PYTHON_MAJOR_MINOR}/site-packages" && \
    mv /tmp/site-packages "/usr/local/lib/python${PYTHON_MAJOR_MINOR}/site-packages" && \
    \
    # Remove test files and compiled artifacts, but KEEP dist-info (required for package discovery) \
    find "/usr/local/lib/python${PYTHON_MAJOR_MINOR}/site-packages" \
        -type d -name "tests" -exec rm -rf {} + && \
    find "/usr/local/lib/python${PYTHON_MAJOR_MINOR}/site-packages" \
        -maxdepth 1 -type d -name "poetry*" -exec rm -rf {} + && \
    find "/usr/local/lib/python${PYTHON_MAJOR_MINOR}/site-packages" \
        -type d -name "*.egg-info" -exec rm -rf {} + && \
    find "/usr/local/lib/python${PYTHON_MAJOR_MINOR}/site-packages" \
        -maxdepth 1 -type d -name "poetry-*.dist-info" -exec rm -rf {} + && \
    find "/usr/local/lib/python${PYTHON_MAJOR_MINOR}/site-packages" \
        -type f \( -name "*.pyc" -o -name "*.pyo" -o -name "*.dist-info/RECORD" \) -delete

# Copy application code (keep root-owned for security)
COPY souschef/ ./souschef/

# Copy Streamlit configuration
COPY .streamlit/ ./.streamlit/

# Create application directories with restricted permissions
RUN mkdir -p /app/.streamlit && \
    mkdir -p /app/.cache && \
    mkdir -p /app/.tmp && \
    chmod 755 /app && \
    chmod 755 /app/souschef && \
    chmod 755 /app/.streamlit && \
    chmod 755 /app/.cache && \
    chmod 755 /app/.tmp && \
    find /app -type f -name '*.py' -exec chmod 644 {} \; && \
    chown -R app:app /app/.streamlit /app/.cache /app/.tmp

# Switch to non-root user for security
USER app

# Expose Streamlit port
EXPOSE 9999

# Health check - robust implementation with timeout
# Note: Must override ENTRYPOINT to avoid streamlit run being prepended
HEALTHCHECK \
    --interval=30s \
    --timeout=10s \
    --start-period=5s \
    --retries=3 \
    CMD /bin/sh -c "python -m souschef.ui.health_check" || exit 1

# Use ENTRYPOINT for proper signal handling
# This ensures Ctrl+C and container stop signals work correctly
ENTRYPOINT ["python", "-m", "streamlit", "run"]

# CMD provides the default arguments
CMD ["souschef/ui/app.py", \
     "--server.address", "0.0.0.0", \
     "--server.port", "9999", \
    "--client.showErrorDetails", "false", \
     "--logger.level", "info", \
     "--server.headless", "true", \
     "--server.runOnSave", "false", \
     "--client.toolbarMode", "auto"]
