# gRPC and Protobuf targets are provided by cmake/GrpcProtobuf.cmake which is included from ThirdPartyDependencies.cmake
# before this point.
#
# PROTOBUF VERSION REQUIREMENT: the fit-protocol schema uses proto3 "optional" fields. The
# has_*() accessors for those fields are only generated by protobuf >= 3.15, so building against
# an older system protobuf fails to compile (e.g. EL9's protobuf 3.14 -> "has_as_json_object()
# was not declared"). When relying on a system protobuf, ensure it is >= 3.15. This is why the RPM
# packaging (cmake/couchbase-cxx-client.spec.in) only enables the fit-performer subpackage on
# distributions that ship a new-enough protobuf; the whole tool can be disabled with
# -DCOUCHBASE_CXX_CLIENT_BUILD_FIT_PERFORMER=OFF.
if(NOT TARGET gRPC::grpc++)
  message(FATAL_ERROR "gRPC::grpc++ target not found. Ensure cmake/GrpcProtobuf.cmake was included.")
endif()
if(NOT TARGET protobuf::libprotobuf)
  message(FATAL_ERROR "protobuf::libprotobuf target not found. Ensure cmake/GrpcProtobuf.cmake was included.")
endif()

# Enforce the protobuf >= 3.15 requirement documented above, so an older system protobuf fails
# here with an actionable message instead of a cryptic missing-has_*()-accessor compile error.
if(DEFINED Protobuf_VERSION AND Protobuf_VERSION VERSION_LESS 3.15)
  message(FATAL_ERROR
    "fit_performer requires protobuf >= 3.15 for proto3 'optional' has_*() accessors, but found "
    "${Protobuf_VERSION}. Upgrade protobuf, or build with -DCOUCHBASE_CXX_CLIENT_BUILD_FIT_PERFORMER=OFF.")
endif()

# Fetch the FIT protocol files via CPM (DOWNLOAD_ONLY, it ships only .proto files, no
# CMakeLists) so they land in CPM_SOURCE_CACHE and get embedded in the release tarball.
# This lets offline package builds (e.g. inside the sbuild chroot, which runs with
# FETCHCONTENT_FULLY_DISCONNECTED=ON) build fit_performer without network access.
#
# WARNING: Do NOT add GIT_SHALLOW TRUE — it fails with "fatal: unable to read tree" for
# this commit because shallow clones only reach commits at/near the branch tip. CPM does
# a full clone by default, which is required here.
cpmaddpackage(
  NAME
  fit_protocol
  GITHUB_REPOSITORY
  "couchbaselabs/fit-protocol"
  GIT_TAG
  d7fb6ede7ee0f39cc39294b67d5b180674d61449
  DOWNLOAD_ONLY
  YES)

# generate the protobuf/grpc stuff
set(PROTO_IN "${fit_protocol_SOURCE_DIR}/operational")
set(PROTO_SRC_DIR "${CMAKE_CURRENT_BINARY_DIR}/proto_generated")
file(MAKE_DIRECTORY "${PROTO_SRC_DIR}")
file(GLOB PROTO_FILES "${PROTO_IN}/*.proto")
message("PROTO_FILES: ${PROTO_FILES}, PROTO_SRC_DIR: ${PROTO_SRC_DIR}")

include("${CMAKE_SOURCE_DIR}/cmake/protobuf_Generate.cmake")

protobuf_generate_cpp_optional(
  PROTO_SOURCES
  PROTO_HEADERS
  ${PROTO_SRC_DIR}
  ${PROTO_FILES})
message("PROTO_SOURCES: ${PROTO_SOURCES}")
set(GRPC_GENERATE_CPP_APPEND_PATH ON)
include("${CMAKE_SOURCE_DIR}/cmake/gRPC_Generate.cmake")
grpc_generate_cpp(
  GRPC_SOURCES
  GRPC_HEADERS
  ${PROTO_SRC_DIR}
  ${PROTO_FILES})

if(MSVC)
  set_source_files_properties(${PROTO_SOURCES} ${GRPC_SOURCES} PROPERTIES COMPILE_OPTIONS "/W0;/FS")
else()
  set_source_files_properties(${PROTO_SOURCES} ${GRPC_SOURCES} PROPERTIES COMPILE_OPTIONS "-w")
endif()

set(FIT_PERFORMER_SOURCES
    src/batcher.hxx
    src/bounds.cxx
    src/bounds.hxx
    src/commands/bucket_management.cxx
    src/commands/bucket_management.hxx
    src/commands/collection_management.cxx
    src/commands/collection_management.hxx
    src/commands/common.cxx
    src/commands/common.hxx
    src/commands/key_value.cxx
    src/commands/key_value.hxx
    src/commands/query.cxx
    src/commands/query.hxx
    src/commands/query_index_management.cxx
    src/commands/query_index_management.hxx
    src/commands/search.cxx
    src/commands/search.hxx
    src/commands/search_index_management.cxx
    src/commands/search_index_management.hxx
    src/connection.cxx
    src/connection.hxx
    src/counters.cxx
    src/counters.hxx
    src/exceptions.hxx
    src/fit.cxx
    src/hooks.cxx
    src/hooks.hxx
    src/metrics/metrics_reporter.cxx
    src/metrics/metrics_reporter.hxx
    src/observability/otel.cxx
    src/observability/otel.hxx
    src/observability/span_owner.cxx
    src/observability/span_owner.hxx
    src/service.cxx
    src/service.hxx
    src/stream.hxx
    src/utils.hxx)
add_executable(fit_performer ${FIT_PERFORMER_SOURCES} ${PROTO_SOURCES} ${GRPC_SOURCES})

set_target_properties(fit_performer PROPERTIES POSITION_INDEPENDENT_CODE ON)
set_project_options(fit_performer)

# fit_performer interoperates heavily with generated protobuf/gRPC code and third-party headers that trigger many
# warnings. Suppress all warnings for this target rather than fighting with set_project_warnings overrides.
set_target_properties(fit_performer PROPERTIES COMPILE_WARNING_AS_ERROR OFF)
if(MSVC)
  # /W0 suppresses all warnings. COMPILE_WARNING_AS_ERROR OFF handles TreatWarningAsError. On MSVC, proto_utils.h must
  # be force-included before gRPC streaming headers to work around non-conformant two-phase lookup that causes
  # SerializationTraits to be undefined when call_op_set.h is processed before proto_utils.h in generated gRPC source
  # files.
  set(_msvc_grpc_fix_header "${CMAKE_CURRENT_BINARY_DIR}/msvc_grpc_proto_utils_fix.h")
  file(WRITE "${_msvc_grpc_fix_header}" "#pragma once\n#include <grpcpp/impl/proto_utils.h>\n")
  target_compile_options(fit_performer PRIVATE /W0 /FS "/FI${_msvc_grpc_fix_header}")
else()
  # -w suppresses all warnings on GCC and Clang. -Wno-c++20-extensions is Clang-specific (GCC/other
  # front-ends may not recognise it), so gate it by compiler id to keep the target cross-platform.
  target_compile_options(
    fit_performer PRIVATE -w $<$<CXX_COMPILER_ID:Clang,AppleClang>:-Wno-c++20-extensions>)
endif()

target_include_directories(
  fit_performer
  PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src
          ${CMAKE_CURRENT_SOURCE_DIR}
          ${CMAKE_SOURCE_DIR}/couchbase
          ${CMAKE_SOURCE_DIR}/core)
target_include_directories(fit_performer SYSTEM PRIVATE ${PROTO_SRC_DIR})

# The generated couchbase/build_config.hxx records which optional components this build actually
# compiled -- performerCapsFetch reads it so the performer only claims capabilities that are really
# present. The client library does not export this directory, so it is added here explicitly.
target_include_directories(fit_performer PRIVATE ${PROJECT_BINARY_DIR}/generated)

target_link_libraries(
  fit_performer
  PRIVATE ${couchbase_cxx_tools_client_library}
          Microsoft.GSL::GSL
          system_metrics_cxx
          spdlog::spdlog
          gRPC::grpc++
          taocpp::json
          protobuf::libprotobuf
          asio::asio)
if(COUCHBASE_CXX_CLIENT_BUILD_OPENTELEMETRY)
  target_compile_definitions(fit_performer PRIVATE COUCHBASE_CXX_CLIENT_BUILD_OPENTELEMETRY)
  target_link_libraries(fit_performer PRIVATE opentelemetry_exporter_otlp_http opentelemetry_exporter_otlp_http_metric)
endif()
