/*
 * 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 <buildboxcasd_cmdlinespec.h>
#include <buildboxcasd_daemon.h>
#include <buildboxcasd_metricnames.h>

#include <buildboxcommon_connectionoptions.h>
#include <buildboxcommon_connectionoptions_commandline.h>
#include <buildboxcommon_logging.h>
#include <buildboxcommonmetrics_metricsconfigurator.h>
#include <buildboxcommonmetrics_scopedperiodicpublisherdaemon.h>
#include <buildboxcommonmetrics_statsdpublishercreator.h>

#include <csignal>
#include <cstring>
#include <iostream>
#include <signal.h>
#include <span>
#include <string>
#include <unistd.h>

using namespace buildboxcommon;
using namespace buildboxcasd;

int main(int argc, char *argv[])
{
    auto args = std::span(argv, argc);
    // Initialize logger
    buildboxcommon::logging::Logger::getLoggerInstance().initialize(args[0]);
    Daemon s_daemon;

    int received_signal = 0;

    try {
        // Block SIGINT and SIGTERM to allow graceful shutdown after
        // initialization
        sigset_t signal_mask;
        sigemptyset(&signal_mask);
        sigaddset(&signal_mask, SIGINT);
        sigaddset(&signal_mask, SIGTERM);
        if (sigprocmask(SIG_BLOCK, &signal_mask, nullptr) < 0) {
            BUILDBOX_LOG_ERROR("Failed to block signals: " << strerror(errno));
        }

        CmdLineSpec spec(
            buildboxcommon::ConnectionOptionsCommandLine("", ""),
            buildboxcommon::ConnectionOptionsCommandLine("CAS", "cas-"),
            buildboxcommon::ConnectionOptionsCommandLine("Remote Asset",
                                                         "ra-"),
            buildboxcommon::ConnectionOptionsCommandLine("Action Cache",
                                                         "ac-"),
            buildboxcommon::ConnectionOptionsCommandLine("Execution",
                                                         "exec-"));
        buildboxcommon::CommandLine commandLine(spec.d_spec);
        const bool success = commandLine.parse(argc, argv);
        if (!success) {
            commandLine.usage();
            return EXIT_FAILURE;
        }

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

        if (!s_daemon.configure(commandLine, spec.d_cachePath)) {
            commandLine.usage();
            return EXIT_FAILURE;
        }

        // Setup metrics configuration
        buildboxcommonmetrics::MetricsConfigType metricsConfig;
        if (commandLine.exists("metrics-mode")) {
            buildboxcommonmetrics::MetricsConfigurator::metricsParser(
                "metrics-mode", commandLine.getString("metrics-mode"),
                &metricsConfig);
        }

        if (commandLine.exists("metrics-publish-interval")) {
            buildboxcommonmetrics::MetricsConfigurator::metricsParser(
                "metrics-publish-interval",
                std::to_string(commandLine.getInt("metrics-publish-interval")),
                &metricsConfig);
        }

        if (commandLine.exists("metrics-prefix")) {
            buildboxcommonmetrics::MetricsConfigurator::metricsParser(
                "metrics-prefix", commandLine.getString("metrics-prefix"),
                &metricsConfig);
        }

        auto s_publisher = buildboxcommonmetrics::StatsdPublisherCreator::
            createStatsdPublisher(metricsConfig);

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

        s_daemon.runDaemon();

        // The server is up and running in the background, waiting for a
        // signal...
        sigwait(&signal_mask, &received_signal);
    }
    catch (const std::runtime_error &e) {
        BUILDBOX_LOG_ERROR(e.what());
        exit(EXIT_FAILURE);
    }

    BUILDBOX_LOG_INFO("Received signal [" << received_signal
                                          << "], stopping and exiting...");
    s_daemon.stop();
    return 0;
}
