# Smartbotic Database Service

# Find OpenSSL for encryption
find_package(OpenSSL REQUIRED)

# PkgConfig is REQUIRED because yyjson (v1.10+) is a hard build dependency.
# LZ4 and jemalloc remain optional, but if pkg-config itself is missing we
# fail at configure time rather than silently producing a yyjson-free binary
# via empty ${yyjson_LIBRARIES} expansion.
find_package(PkgConfig REQUIRED)

# yyjson — phase A v1.10 hot-path JSON parser. Hard dependency.
pkg_check_modules(yyjson REQUIRED yyjson)

# Optional LZ4 for compression
pkg_check_modules(LZ4 QUIET liblz4)

# v1.9.4 — jemalloc replaces glibc malloc for the smartbotic-database
# binary. The fragmentation pattern from per-RPC nlohmann::json node
# churn (107k 33-byte allocations observed on Zoe) defeats glibc's
# malloc_trim because the trailing region of each arena always has a
# live chunk; jemalloc's background-thread dirty-page decay sidesteps
# the contiguity requirement and reclaims unused pages on a wallclock
# timer regardless of allocation activity. Linked as a library so
# the binary picks up jemalloc's malloc/free symbols at link time;
# no LD_PRELOAD needed.
pkg_check_modules(JEMALLOC QUIET jemalloc)

# LMDB — v2.0 storage substrate. liblmdb does not ship a pkg-config file on
# Debian 13 (`pkg-config --modversion lmdb` fails), so resolve via find_path /
# find_library. Hard dependency.
find_path(LMDB_INCLUDE_DIR lmdb.h)
find_library(LMDB_LIBRARY NAMES lmdb)
if(NOT LMDB_INCLUDE_DIR OR NOT LMDB_LIBRARY)
    message(FATAL_ERROR "LMDB not found. Install liblmdb-dev.")
endif()

# Source files
set(DATABASE_SERVICE_SOURCES
    src/main.cpp
    src/database_service.cpp
    src/database_grpc_impl.cpp
    src/doc_binary.cpp
    src/json_parse.cpp
    src/memory_store.cpp
    src/persistence/persistence_manager.cpp
    src/persistence/wal.cpp
    src/persistence/snapshot.cpp
    src/persistence/history_store.cpp
    src/encryption/encryption_manager.cpp
    src/events/event_manager.cpp
    src/files/file_manager.cpp
    src/files/file_store.cpp
    src/replication/replication_manager.cpp
    src/replication/sync_protocol.cpp
    src/replication/conflict_resolver.cpp
    src/migrations/migration_runner.cpp
    src/views/projection.cpp
    src/views/view_manager.cpp
    src/config/collection_config_manager.cpp
    src/config/config_loader.cpp
    src/storage/lmdb_env.cpp
    src/storage/lmdb_txn.cpp
    src/storage/lmdb_dbi.cpp
    src/storage/document_store_lmdb.cpp
)

# Create executable
add_executable(smartbotic-database ${DATABASE_SERVICE_SOURCES})

target_include_directories(smartbotic-database PRIVATE
    ${CMAKE_CURRENT_SOURCE_DIR}/src
)

# Link dependencies
if(BUILD_SHARED_LIBS)
    target_link_libraries(smartbotic-database PRIVATE smartbotic-db-client)
else()
    target_link_libraries(smartbotic-database PRIVATE smartbotic_db_proto)
endif()
target_link_libraries(smartbotic-database PRIVATE
    OpenSSL::SSL
    OpenSSL::Crypto
)

if(TARGET nlohmann_json::nlohmann_json)
    target_link_libraries(smartbotic-database PRIVATE nlohmann_json::nlohmann_json)
else()
    target_include_directories(smartbotic-database PRIVATE ${NLOHMANN_JSON_INCLUDE_DIRS})
endif()

if(TARGET spdlog::spdlog)
    target_link_libraries(smartbotic-database PRIVATE spdlog::spdlog)
else()
    target_link_libraries(smartbotic-database PRIVATE ${SPDLOG_LIBRARIES})
    target_include_directories(smartbotic-database PRIVATE ${SPDLOG_INCLUDE_DIRS})
endif()

# LZ4 compression support
if(LZ4_FOUND)
    target_compile_definitions(smartbotic-database PRIVATE HAVE_LZ4)
    target_link_libraries(smartbotic-database PRIVATE ${LZ4_LIBRARIES})
    target_include_directories(smartbotic-database PRIVATE ${LZ4_INCLUDE_DIRS})
endif()

# yyjson — phase A v1.10 hot-path JSON parser
target_link_libraries(smartbotic-database PRIVATE ${yyjson_LIBRARIES})
target_include_directories(smartbotic-database PRIVATE ${yyjson_INCLUDE_DIRS})

# LMDB — v2.0 storage substrate
target_link_libraries(smartbotic-database PRIVATE ${LMDB_LIBRARY})
target_include_directories(smartbotic-database PRIVATE ${LMDB_INCLUDE_DIR})

# v1.9.4 — jemalloc allocator (overrides glibc malloc/free)
if(JEMALLOC_FOUND)
    target_compile_definitions(smartbotic-database PRIVATE HAVE_JEMALLOC)
    target_link_libraries(smartbotic-database PRIVATE ${JEMALLOC_LIBRARIES})
    target_include_directories(smartbotic-database PRIVATE ${JEMALLOC_INCLUDE_DIRS})
    # Position-sensitive: jemalloc must be linked early so its symbols win
    # over libc's. -Wl,-no-as-needed forces the linker to record the
    # dependency even though we don't reference any jemalloc symbol
    # directly (we just want its malloc to override).
    target_link_options(smartbotic-database PRIVATE -Wl,-no-as-needed)
    message(STATUS "Linking with jemalloc (${JEMALLOC_VERSION})")
else()
    message(STATUS "jemalloc not found — falling back to glibc malloc (degraded RSS reclaim under load)")
endif()

# Systemd support
if(SYSTEMD_FOUND)
    target_compile_definitions(smartbotic-database PRIVATE HAVE_SYSTEMD)
    target_link_libraries(smartbotic-database PRIVATE ${SYSTEMD_LIBRARIES})
    target_include_directories(smartbotic-database PRIVATE ${SYSTEMD_INCLUDE_DIRS})
endif()

# Installation
install(TARGETS smartbotic-database
    RUNTIME DESTINATION bin
)
