cmake_minimum_required(VERSION 3.20)
project(SmartBotic VERSION 1.0.0 LANGUAGES CXX C)

set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)

# Include CMake modules
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")

include(CompilerFlags)
include(Dependencies)
include(FindPackages)

# Common library
add_library(smartbotic_common STATIC
    lib/common/uuid.cpp
    lib/common/time_utils.cpp
    lib/common/error.cpp
    lib/common/string_utils.cpp
)
target_include_directories(smartbotic_common PUBLIC
    ${CMAKE_CURRENT_SOURCE_DIR}/lib
)
target_link_libraries(smartbotic_common PUBLIC
    nlohmann_json::nlohmann_json
    OpenSSL::Crypto
)

# Logging library
add_library(smartbotic_logging STATIC
    lib/logging/logger.cpp
)
target_include_directories(smartbotic_logging PUBLIC
    ${CMAKE_CURRENT_SOURCE_DIR}/lib
)
target_link_libraries(smartbotic_logging PUBLIC
    spdlog::spdlog
    smartbotic_common
)

# Config library
add_library(smartbotic_config STATIC
    lib/config/config_loader.cpp
)
target_include_directories(smartbotic_config PUBLIC
    ${CMAKE_CURRENT_SOURCE_DIR}/lib
)
target_link_libraries(smartbotic_config PUBLIC
    smartbotic_common
    smartbotic_logging
    nlohmann_json::nlohmann_json
)

# Storage client library
add_library(smartbotic_storage STATIC
    lib/storage/storage_client.cpp
)
target_include_directories(smartbotic_storage PUBLIC
    ${CMAKE_CURRENT_SOURCE_DIR}/lib
    ${CMAKE_CURRENT_BINARY_DIR}
)
target_link_libraries(smartbotic_storage PUBLIC
    smartbotic_common
    smartbotic_logging
    smartbotic_proto
)

# Crypto library
add_library(smartbotic_crypto STATIC
    lib/crypto/aes_gcm.cpp
)
target_include_directories(smartbotic_crypto PUBLIC
    ${CMAKE_CURRENT_SOURCE_DIR}/lib
)
target_link_libraries(smartbotic_crypto PUBLIC
    smartbotic_common
    OpenSSL::Crypto
    nlohmann_json::nlohmann_json
)

# Credentials library
add_library(smartbotic_credentials STATIC
    lib/credentials/credential_types.cpp
    lib/credentials/credential_store.cpp
    lib/credentials/credential_client.cpp
)
target_include_directories(smartbotic_credentials PUBLIC
    ${CMAKE_CURRENT_SOURCE_DIR}/lib
    ${CMAKE_CURRENT_BINARY_DIR}
)
target_link_libraries(smartbotic_credentials PUBLIC
    smartbotic_common
    smartbotic_logging
    smartbotic_crypto
    smartbotic_storage
    smartbotic_proto
    CURL::libcurl
)

# Proto library
add_library(smartbotic_proto STATIC
    ${CMAKE_CURRENT_BINARY_DIR}/proto/common.pb.cc
    ${CMAKE_CURRENT_BINARY_DIR}/proto/common.grpc.pb.cc
    ${CMAKE_CURRENT_BINARY_DIR}/proto/storage.pb.cc
    ${CMAKE_CURRENT_BINARY_DIR}/proto/storage.grpc.pb.cc
    ${CMAKE_CURRENT_BINARY_DIR}/proto/workflow.pb.cc
    ${CMAKE_CURRENT_BINARY_DIR}/proto/workflow.grpc.pb.cc
    ${CMAKE_CURRENT_BINARY_DIR}/proto/runner.pb.cc
    ${CMAKE_CURRENT_BINARY_DIR}/proto/runner.grpc.pb.cc
    ${CMAKE_CURRENT_BINARY_DIR}/proto/credentials.pb.cc
    ${CMAKE_CURRENT_BINARY_DIR}/proto/credentials.grpc.pb.cc
)
target_include_directories(smartbotic_proto PUBLIC
    ${CMAKE_CURRENT_BINARY_DIR}
)
target_link_libraries(smartbotic_proto PUBLIC
    gRPC::grpc++
    protobuf::libprotobuf
)

# Generate proto files
find_program(PROTOC protoc REQUIRED)
find_program(GRPC_CPP_PLUGIN grpc_cpp_plugin REQUIRED)

set(PROTO_DIR ${CMAKE_CURRENT_SOURCE_DIR}/proto)
set(PROTO_OUT_DIR ${CMAKE_CURRENT_BINARY_DIR}/proto)
file(MAKE_DIRECTORY ${PROTO_OUT_DIR})

set(PROTO_FILES common storage workflow runner credentials)
foreach(PROTO ${PROTO_FILES})
    add_custom_command(
        OUTPUT
            ${PROTO_OUT_DIR}/${PROTO}.pb.cc
            ${PROTO_OUT_DIR}/${PROTO}.pb.h
            ${PROTO_OUT_DIR}/${PROTO}.grpc.pb.cc
            ${PROTO_OUT_DIR}/${PROTO}.grpc.pb.h
        COMMAND ${PROTOC}
            --proto_path=${PROTO_DIR}
            --cpp_out=${PROTO_OUT_DIR}
            --grpc_out=${PROTO_OUT_DIR}
            --plugin=protoc-gen-grpc=${GRPC_CPP_PLUGIN}
            ${PROTO_DIR}/${PROTO}.proto
        DEPENDS ${PROTO_DIR}/${PROTO}.proto
        COMMENT "Generating protobuf files for ${PROTO}.proto"
    )
endforeach()

# Database service
add_executable(smartbotic-database
    src/database/main.cpp
    src/database/memory_store.cpp
    src/database/database_service.cpp
    src/database/persistence/wal.cpp
    src/database/persistence/snapshot.cpp
)
target_include_directories(smartbotic-database PRIVATE
    ${CMAKE_CURRENT_SOURCE_DIR}/src
    ${CMAKE_CURRENT_BINARY_DIR}
)
target_link_libraries(smartbotic-database PRIVATE
    smartbotic_common
    smartbotic_logging
    smartbotic_config
    smartbotic_proto
    gRPC::grpc++
)

# WebServer service
add_executable(smartbotic-webserver
    src/webserver/main.cpp
    src/webserver/http_server.cpp
    src/webserver/websocket_server.cpp
    src/webserver/webserver_service.cpp
    src/webserver/auth/bcrypt_utils.cpp
    src/webserver/auth/jwt_utils.cpp
    src/webserver/auth/auth_store.cpp
    src/webserver/auth/auth_middleware.cpp
    src/webserver/runners/runner_registry.cpp
    src/webserver/runners/load_balancer.cpp
    src/webserver/nodes/node_store.cpp
    src/webserver/scheduler/workflow_scheduler.cpp
    src/webserver/grpc/node_sync_service.cpp
    src/webserver/grpc/credential_service.cpp
    src/webserver/api/auth_controller.cpp
    src/webserver/api/credential_controller.cpp
    src/webserver/api/user_controller.cpp
    src/webserver/api/workflow_controller.cpp
    src/webserver/api/workflow_group_controller.cpp
    src/webserver/api/execution_controller.cpp
    src/webserver/api/node_controller.cpp
    src/webserver/api/runner_controller.cpp
    src/webserver/api/webhook_controller.cpp
    src/webserver/api/database_controller.cpp
)
target_include_directories(smartbotic-webserver PRIVATE
    ${CMAKE_CURRENT_SOURCE_DIR}/src
    ${CMAKE_CURRENT_BINARY_DIR}
)
target_link_libraries(smartbotic-webserver PRIVATE
    smartbotic_common
    smartbotic_logging
    smartbotic_config
    smartbotic_storage
    smartbotic_credentials
    smartbotic_proto
    httplib::httplib
    websockets
    bcrypt_lib
    gRPC::grpc++
    OpenSSL::SSL
    OpenSSL::Crypto
)

# Runner service
set(RUNNER_SOURCES
    src/runner/main.cpp
    src/runner/workflow_engine.cpp
    src/runner/node_registry.cpp
    src/runner/error_handler.cpp
    src/runner/runner_service.cpp
    src/runner/collection_permissions.cpp
    src/runner/engine/script_engine.cpp
    src/runner/imap/imap_client.cpp
)

# Add MySQL client if library is found
if(MYSQL_CLIENT_FOUND)
    list(APPEND RUNNER_SOURCES src/runner/mysql/mysql_client.cpp)
endif()

# Add PostgreSQL client if library is found
if(POSTGRESQL_CLIENT_FOUND)
    list(APPEND RUNNER_SOURCES src/runner/postgresql/postgresql_client.cpp)
endif()

add_executable(smartbotic-runner ${RUNNER_SOURCES})
target_include_directories(smartbotic-runner PRIVATE
    ${CMAKE_CURRENT_SOURCE_DIR}/src
    ${CMAKE_CURRENT_BINARY_DIR}
)
target_link_libraries(smartbotic-runner PRIVATE
    smartbotic_common
    smartbotic_logging
    smartbotic_config
    smartbotic_storage
    smartbotic_credentials
    smartbotic_proto
    quickjs_lib
    CURL::libcurl
    gRPC::grpc++
)

# Add MySQL client library if found
if(MYSQL_CLIENT_FOUND)
    target_link_libraries(smartbotic-runner PRIVATE mysql_client)
    target_compile_definitions(smartbotic-runner PRIVATE MYSQL_SUPPORT=1)
endif()

# Add PostgreSQL client library if found
if(POSTGRESQL_CLIENT_FOUND)
    target_link_libraries(smartbotic-runner PRIVATE postgresql_client)
    target_compile_definitions(smartbotic-runner PRIVATE POSTGRESQL_SUPPORT=1)
endif()

# Migration tool
add_executable(smartbotic-migrate-nodes
    src/tools/migrate_nodes.cpp
)
target_include_directories(smartbotic-migrate-nodes PRIVATE
    ${CMAKE_CURRENT_SOURCE_DIR}/lib
)
target_link_libraries(smartbotic-migrate-nodes PRIVATE
    nlohmann_json::nlohmann_json
    CURL::libcurl
)

# Install targets
install(TARGETS
    smartbotic-database
    smartbotic-webserver
    smartbotic-runner
    smartbotic-migrate-nodes
    RUNTIME DESTINATION bin
)

install(DIRECTORY nodes/ DESTINATION share/smartbotic/nodes)
install(DIRECTORY config/ DESTINATION etc/smartbotic)
