# Find eligible builder and runner images on Docker Hub. We use Debian
# instead of Alpine to avoid DNS resolution issues in production.
#
# https://hub.docker.com/r/hexpm/elixir/tags?page=1&name=debian
# https://hub.docker.com/_/debian?tab=tags
#
# This file is based on these images:
#
#   - https://hub.docker.com/r/hexpm/elixir/tags - for the build image
#   - https://hub.docker.com/_/ubuntu - for the release image
#   - https://pkgs.org/ - resource for finding needed packages
#   - Ex: hexpm/elixir:1.19.5-erlang-28.0-ubuntu-noble-20260324
#
ARG ELIXIR_VERSION=1.19.5
ARG OTP_VERSION=28.1.1
ARG UBUNTU_VERSION=noble-20260324
ARG RELEASE_NAME=elektrine
ARG ELEKTRINE_RELEASE_MODULES=all
ARG PRIMARY_DOMAIN=example.com
ARG EMAIL_DOMAIN=example.com
ARG SUPPORTED_DOMAINS=example.com
ARG PROFILE_BASE_DOMAINS=example.com
ARG NPM_VERSION=11.13.0

ARG BUILDER_IMAGE="hexpm/elixir:${ELIXIR_VERSION}-erlang-${OTP_VERSION}-ubuntu-${UBUNTU_VERSION}@sha256:1486c932fb7576bea9becaaa33da9946205161a2d16938a3351c455d59ce6c3a"
ARG RUNNER_IMAGE="ubuntu:${UBUNTU_VERSION}@sha256:84e77dee7d1bc93fb029a45e3c6cb9d8aa4831ccfcc7103d36e876938d28895b"

FROM ${BUILDER_IMAGE} AS builder

ARG RELEASE_NAME
ARG ELEKTRINE_RELEASE_MODULES
ARG PRIMARY_DOMAIN
ARG EMAIL_DOMAIN
ARG SUPPORTED_DOMAINS
ARG PROFILE_BASE_DOMAINS
ARG NPM_VERSION

# Retry helper function for flaky network operations
SHELL ["/bin/bash", "-c"]

# install build dependencies with retry
RUN for i in 1 2 3 4 5; do \
      if apt-get update -y && \
           apt-get install -y build-essential git libvips-dev; then \
        apt-get clean; \
        rm -rf /var/lib/apt/lists/*; \
        break; \
      fi; \
      if [ "$i" = "5" ]; then \
        echo "Retry $i failed, giving up." >&2; \
        exit 1; \
      fi; \
      echo "Retry $i failed, waiting..."; \
      sleep 5; \
    done

# prepare build dir
WORKDIR /app

# install hex + rebar with retry
RUN for i in 1 2 3 4 5; do \
      if mix local.hex --force && \
           mix local.rebar --force; then \
        break; \
      fi; \
      if [ "$i" = "5" ]; then \
        echo "Retry $i failed, giving up." >&2; \
        exit 1; \
      fi; \
      echo "Retry $i failed, waiting..."; \
      sleep 5; \
    done

# set build ENV
ENV MIX_ENV="prod"
ENV ELEKTRINE_RELEASE_MODULES="${ELEKTRINE_RELEASE_MODULES}"
ENV PRIMARY_DOMAIN="${PRIMARY_DOMAIN}"
ENV EMAIL_DOMAIN="${EMAIL_DOMAIN}"
ENV SUPPORTED_DOMAINS="${SUPPORTED_DOMAINS}"
ENV PROFILE_BASE_DOMAINS="${PROFILE_BASE_DOMAINS}"
ENV NPM_CONFIG_UPDATE_NOTIFIER=false

# install mix dependencies
COPY mix.lock ./
COPY release_builder/mix.exs release_builder/mix.exs
COPY release_builder/module_selection.exs release_builder/module_selection.exs
COPY release_builder/config/config.exs release_builder/config/config.exs
COPY release_builder/config/runtime.exs release_builder/config/runtime.exs
COPY apps/elektrine/mix.exs apps/elektrine/mix.exs
COPY apps/arblarg/mix.exs apps/arblarg/mix.exs
COPY apps/atomine/mix.exs apps/atomine/mix.exs
COPY apps/kairo/mix.exs apps/kairo/mix.exs
COPY apps/paige/mix.exs apps/paige/mix.exs
COPY apps/elektrine_web/mix.exs apps/elektrine_web/mix.exs
COPY apps/elektrine_email/mix.exs apps/elektrine_email/mix.exs
COPY apps/elektrine_social/mix.exs apps/elektrine_social/mix.exs
COPY apps/elektrine_dns/mix.exs apps/elektrine_dns/mix.exs
COPY apps/elektrine_nerve/mix.exs apps/elektrine_nerve/mix.exs
COPY apps/elektrine_vpn/mix.exs apps/elektrine_vpn/mix.exs
COPY apps/elektrine_uptime/mix.exs apps/elektrine_uptime/mix.exs
COPY config/config.exs config/prod.exs config/
WORKDIR /app/release_builder
RUN for i in 1 2 3 4 5; do \
      if mix deps.get --only $MIX_ENV; then \
        break; \
      fi; \
      if [ "$i" = "5" ]; then \
        echo "Retry $i failed, giving up." >&2; \
        exit 1; \
      fi; \
      echo "Retry $i failed, waiting..."; \
      sleep 5; \
    done
RUN mix deps.compile
WORKDIR /app

COPY apps apps
COPY release_builder release_builder
COPY config/runtime.exs config/runtime.exs
COPY config/runtime config/runtime
COPY --chmod=755 scripts/release/deploy_release.sh scripts/release/deploy_release.sh
COPY scripts/lib scripts/lib

# Install Node.js 20.x (required for Tailwind v4)
RUN for i in 1 2 3 4 5; do \
      if apt-get update -y && \
           apt-get install -y ca-certificates curl gnupg && \
           mkdir -p /etc/apt/keyrings && \
           curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg && \
           echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_20.x nodistro main" | tee /etc/apt/sources.list.d/nodesource.list && \
           apt-get update -y && \
           apt-get install -y nodejs; then \
        apt-get clean; \
        rm -rf /var/lib/apt/lists/*; \
        break; \
      fi; \
      if [ "$i" = "5" ]; then \
        echo "Retry $i failed, giving up." >&2; \
        exit 1; \
      fi; \
      echo "Retry $i failed, waiting..."; \
      sleep 5; \
    done

# Update npm before installing asset dependencies.
RUN for i in 1 2 3 4 5; do \
      if npm install -g npm@"${NPM_VERSION}"; then \
        break; \
      fi; \
      if [ "$i" = "5" ]; then \
        echo "Retry $i failed, giving up." >&2; \
        exit 1; \
      fi; \
      echo "Retry $i failed, waiting..."; \
      sleep 5; \
    done

# Install npm dependencies for assets with retry (includes tailwindcss)
RUN for i in 1 2 3 4 5; do \
      if (cd apps/elektrine/assets && npm ci); then \
        break; \
      fi; \
      if [ "$i" = "5" ]; then \
        echo "Retry $i failed, giving up." >&2; \
        exit 1; \
      fi; \
      echo "Retry $i failed, waiting..."; \
      sleep 5; \
    done

# Install esbuild with retry (tailwind is now installed via npm)
RUN for i in 1 2 3 4 5; do \
      if (cd release_builder && mix esbuild.install); then \
        break; \
      fi; \
      if [ "$i" = "5" ]; then \
        echo "Retry $i failed, giving up." >&2; \
        exit 1; \
      fi; \
      echo "Retry $i failed, waiting..."; \
      sleep 5; \
    done

# Build the selected hoster release through release_builder/
RUN ./scripts/release/deploy_release.sh --release-name "${RELEASE_NAME}" --mix-env "${MIX_ENV}"

# start a new build stage so that the final image will only contain
# the compiled release and other runtime necessities
FROM ${RUNNER_IMAGE}

ARG RELEASE_NAME
ARG ELEKTRINE_RELEASE_MODULES

SHELL ["/bin/bash", "-c"]

RUN for i in 1 2 3 4 5; do \
      if apt-get update -y && \
           apt-get install -y libstdc++6 openssl libncurses6 ca-certificates curl libvips42 tor gpg gnupg2 dirmngr wireguard-tools iproute2 shadowsocks-libev; then \
        apt-get clean; \
        rm -rf /var/lib/apt/lists/*; \
        break; \
      fi; \
      if [ "$i" = "5" ]; then \
        echo "Retry $i failed, giving up." >&2; \
        exit 1; \
      fi; \
      echo "Retry $i failed, waiting..."; \
      sleep 5; \
    done

RUN which gpg && gpg --version

# Setup data directories (will be mounted as volume in production)
RUN mkdir -p /data/tor/elektrine /data/tor/data /data/certs && \
    chown -R nobody:nogroup /data

# Copy Tor config with proper permissions
COPY --chmod=644 deploy/onion/torrc /etc/tor/torrc

# Use a built-in UTF-8 locale available on Ubuntu images
ENV LANG=C.UTF-8
ENV LANGUAGE=C.UTF-8
ENV LC_ALL=C.UTF-8

WORKDIR "/app"
RUN chown nobody /app

# Copy startup scripts
COPY --chmod=755 deploy/docker/start.sh /app/start.sh
COPY --chmod=755 deploy/docker/docker-entrypoint.sh /app/docker-entrypoint.sh
COPY --chmod=755 scripts/acme /app/scripts/acme

# set runner ENV
ENV MIX_ENV="prod"
ENV PATH="/usr/bin:$PATH"
ENV RELEASE_NAME="${RELEASE_NAME}"
ENV ELEKTRINE_RELEASE_MODULES="${ELEKTRINE_RELEASE_MODULES}"

# Only copy the final release from the build stage
COPY --from=builder --chown=nobody:root /app/_deploy_release/${RELEASE_NAME} ./

# Expose ports for HTTP and HTTPS (non-privileged ports, fly.toml maps external 80/443)
EXPOSE 8080 8443

CMD ["/app/docker-entrypoint.sh"]
