/*
 * Copyright 2018 Bloomberg Finance LP
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <buildboxworker_cmdlinespec.h>
#include <buildboxworker_config.h>
#include <buildboxworker_worker.h>

#include <buildboxcommon_commandline.h>
#include <buildboxcommon_connectionoptions.h>
#include <buildboxcommon_digestgenerator.h>
#include <buildboxcommon_logging.h>
#include <buildboxcommonmetrics_metricsconfigurator.h>
#include <buildboxcommonmetrics_scopedperiodicpublisherdaemon.h>
#include <buildboxcommonmetrics_statsdpublishercreator.h>

#include <cstring>
#include <iostream>
#include <span>
#include <unistd.h>

using namespace buildboxcommon;
using namespace buildboxworker;

int main(int argc, char *argv[])
{
    // We provide the `--instance` CLI parameter to set an instance name for
    // all services. We'll want to make sure that individual options do not
    // conflict.

    CmdLineSpec workerSpec;
    CommandLine commandLine(workerSpec.d_spec);

    const bool success = commandLine.parse(argc, argv);
    if (!success) {
        commandLine.usage();
        return 1;
    }

    if (commandLine.exists("help") || commandLine.exists("version")) {
        return 0;
    }

    // Initializing the logger.
    // (If enabled, we need to redirect the output to files *before* calling
    // `initialize()` in order to being able to start writing log messages.)
    auto &logger = buildboxcommon::logging::Logger::getLoggerInstance();
    const auto logDestinationDirectory =
        commandLine.getString("log-directory");
    if (!logDestinationDirectory.empty()) {
        if (!FileUtils::isDirectory(logDestinationDirectory.c_str())) {
            std::cerr << "--log-directory: path does not exist "
                      << std::string(logDestinationDirectory) << '\n';
            return 1;
        }

        logger.setOutputDirectory(logDestinationDirectory.c_str());
    }
    auto args = std::span(argv, argc);
    logger.initialize(args[0]);

    buildboxcommon::DigestFunction_Value digestFunctionValue =
        commandLine.exists("digest-function")
            ? buildboxcommon::DigestGenerator::stringToDigestFunction(
                  commandLine.getString("digest-function"))
            : static_cast<buildboxcommon::DigestFunction_Value>(
                  BUILDBOXCOMMON_DIGEST_FUNCTION_VALUE);
    buildboxcommon::DigestGenerator::init(digestFunctionValue);

    try {
        buildboxcommonmetrics::MetricsConfigType metricsConfig;
        Worker worker(commandLine, workerSpec.d_botId, &metricsConfig);

        if (!worker.validateConfiguration()) {
            return 1;
        }

        // Setup metrics collection
        auto s_publisher = buildboxcommonmetrics::StatsdPublisherCreator::
            createStatsdPublisher(metricsConfig);

        buildboxcommonmetrics::ScopedPeriodicPublisherDaemon<
            buildboxcommonmetrics::StatsDPublisherType>
            statsDPublisherGuard(metricsConfig.enable(),
                                 metricsConfig.interval(), *s_publisher.get());

        worker.initLocalExecutionClient();

        BUILDBOX_LOG_INFO(
            "Calling runner command with `--validate-parameters`");
        if (!worker.testRunnerCommand()) {
            BUILDBOX_LOG_ERROR(
                "Parameter validation for runner command failed (see output "
                "above for details). Verify whether all the specified "
                "`--runner-arg` options are supported by the runner and that "
                "any that are required by it are given.");
            return 1;
        }

        worker.runWorker();
    }
    catch (const std::runtime_error &e) {
        BUILDBOX_LOG_ERROR(
            "caught std::runtime_error exception: " << e.what());
        return 1;
    }

    return 0;
}
