# Alpine-based MUSL static binary builder for Didactyl
# Produces truly portable binaries with zero runtime dependencies

ARG DEBUG_BUILD=false

FROM alpine:3.19 AS builder

# Re-declare build argument in this stage
ARG DEBUG_BUILD=false

# Install build dependencies (with retry logic for transient mirror/network failures)
RUN set -eux; \
    printf '%s\n' \
      'https://dl-cdn.alpinelinux.org/alpine/v3.19/main' \
      'https://dl-cdn.alpinelinux.org/alpine/v3.19/community' \
      > /etc/apk/repositories; \
    APK_OK=0; \
    for attempt in 1 2 3 4 5; do \
      if apk update && apk add --no-cache \
        build-base \
        musl-dev \
        git \
        cmake \
        pkgconfig \
        autoconf \
        automake \
        libtool \
        openssl-dev \
        openssl-libs-static \
        zlib-dev \
        zlib-static \
        curl-dev \
        curl-static \
        nghttp2-dev \
        nghttp2-static \
        c-ares-dev \
        c-ares-static \
        libpsl-dev \
        libpsl-static \
        libidn2-dev \
        libidn2-static \
        libunistring-dev \
        libunistring-static \
        brotli-dev \
        brotli-static \
        zstd-dev \
        zstd-static \
        sqlite-dev \
        sqlite-static \
        linux-headers \
        wget \
        bash; then \
        APK_OK=1; \
        break; \
      fi; \
      echo "apk dependency install failed (attempt ${attempt}/5), retrying..."; \
      sleep $((attempt * 2)); \
    done; \
    if [ "$APK_OK" -ne 1 ]; then \
      echo "ERROR: failed to install Alpine dependencies after 5 attempts"; \
      exit 33; \
    fi

# Set working directory
WORKDIR /build

# Build libsecp256k1 static (cached layer - only rebuilds if Alpine version changes)
RUN cd /tmp && \
    git clone https://github.com/bitcoin-core/secp256k1.git && \
    cd secp256k1 && \
    ./autogen.sh && \
    ./configure --enable-static --disable-shared --prefix=/usr \
        CFLAGS="-fPIC" && \
    make -j$(nproc) && \
    make install && \
    rm -rf /tmp/secp256k1

# Copy nostr_core_lib source files (cached unless nostr_core_lib changes)
# NOTE: nostr_core_lib currently compiles request_validator, which includes <sqlite3.h>,
# so sqlite-dev/sqlite-static are required in this image even if didactyl does not use sqlite directly.
COPY nostr_core_lib /build/nostr_core_lib/

# Build nostr_core_lib with all NIPs
# Disable fortification in build.sh to prevent __*_chk symbol issues
RUN cd nostr_core_lib && \
    chmod +x build.sh && \
    sed -i 's/CFLAGS="-Wall -Wextra -std=c99 -fPIC -O2"/CFLAGS="-U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=0 -Wall -Wextra -std=c99 -fPIC -O2"/' build.sh && \
    sed -i 's/CC="aarch64-linux-gnu-gcc"/CC="gcc"/' build.sh && \
    rm -f *.o *.a 2>/dev/null || true && \
    ./build.sh --nips=all

# Copy didactyl source files LAST (only this layer rebuilds on source changes)
COPY src/ /build/src/
COPY Makefile /build/Makefile

# Build didactyl with full static linking (only rebuilds when src/ changes)
# Disable fortification to avoid __*_chk symbols that don't exist in MUSL
# Use conditional compilation flags based on DEBUG_BUILD argument
RUN NOSTR_LIB=$(ls /build/nostr_core_lib/libnostr_core_*.a 2>/dev/null | head -1) && \
    if [ -z "$NOSTR_LIB" ]; then echo "ERROR: nostr_core_lib .a not found"; exit 1; fi && \
    echo "Using nostr library: $NOSTR_LIB" && \
    if [ "$DEBUG_BUILD" = "true" ]; then \
        CFLAGS="-g -O2 -DDEBUG"; \
        STRIP_CMD="echo 'Keeping debug symbols'"; \
        echo "Building with DEBUG symbols enabled (optimized with -O2)"; \
    else \
        CFLAGS="-O2"; \
        STRIP_CMD="strip /build/didactyl_static"; \
        echo "Building optimized production binary (symbols stripped)"; \
    fi && \
    CURL_LIBS="$(pkg-config --static --libs libcurl)" && \
    OPENSSL_LIBS="$(pkg-config --static --libs openssl)" && \
    gcc -static $CFLAGS -Wall -Wextra -std=c99 \
    -D_GNU_SOURCE -D_DEFAULT_SOURCE -D_POSIX_C_SOURCE=200809L -DMG_TLS=MG_TLS_BUILTIN \
    -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=0 \
    -I. -Isrc -Isrc/tools -Inostr_core_lib -Inostr_core_lib/nostr_core \
    -Inostr_core_lib/cjson -Inostr_core_lib/nostr_websocket \
    src/main.c src/config.c src/context.c src/llm.c \
    src/nostr_handler.c src/agent.c src/tools/tools_common.c src/tools/tools_schema.c src/tools/tools_dispatch.c \
    src/tools/tool_agent.c src/tools/tool_meta.c src/tools/tool_model.c \
    src/tools/tool_nostr_query.c src/tools/tool_nostr_my_events.c src/tools/tool_nostr_identity.c src/tools/tool_nostr_social.c \
    src/tools/tool_nostr_relay.c src/tools/tool_nostr_dm.c src/tools/tool_admin.c \
    src/tools/tool_task.c src/tools/tool_nostr_list.c src/tools/tool_nostr_block.c src/tools/tool_local.c \
    src/tools/tool_skill.c src/tools/tool_nostr_post.c src/tools/tool_memory.c src/tools/tool_config.c src/tools/tool_cashu_wallet.c src/tools/tool_blossom.c src/trigger_manager.c \
    src/cashu_wallet.c src/nostr_block_list.c src/prompt_template.c src/http_api.c src/setup_wizard.c src/mongoose.c src/debug.c \
    -o /build/didactyl_static \
    $NOSTR_LIB \
    -lsecp256k1 \
    $OPENSSL_LIBS \
    $CURL_LIBS \
    -lpthread -lm -ldl && \
    eval "$STRIP_CMD"

# Verify it's truly static
RUN echo "=== Binary Information ===" && \
    file /build/didactyl_static && \
    ls -lh /build/didactyl_static && \
    echo "=== Checking for dynamic dependencies ===" && \
    (ldd /build/didactyl_static 2>&1 || echo "Binary is static") && \
    echo "=== Build complete ==="

# Output stage - just the binary
FROM scratch AS output
COPY --from=builder /build/didactyl_static /didactyl_static
