/*
 * Copyright 2020 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 <logstreamtail.h>

#include <buildboxcommon_commandline.h>
#include <buildboxcommon_connectionoptions_commandline.h>
#include <buildboxcommon_logging.h>
#include <buildboxcommon_logging_commandline.h>

#include <sys/types.h>
#include <unistd.h>

#include <iostream>
#include <span>
#include <string>
#include <vector>

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

        using buildboxcommon::CommandLineTypes;
        std::vector<CommandLineTypes::ArgumentSpec> d_spec = {
            {"resource-name", "LogStream write resource name",
             CommandLineTypes::TypeInfo(
                 CommandLineTypes::DataType::COMMANDLINE_DT_STRING),
             CommandLineTypes::ArgumentSpec::O_REQUIRED,
             CommandLineTypes::ArgumentSpec::C_WITH_ARG},
        };

        const auto connectionOptionsSpec =
            buildboxcommon::ConnectionOptionsCommandLine("", "");
        d_spec.insert(d_spec.end(), connectionOptionsSpec.spec().cbegin(),
                      connectionOptionsSpec.spec().cend());

        auto loggingSpec = buildboxcommon::loggingCommandLineSpec();
        d_spec.insert(d_spec.end(), loggingSpec.cbegin(), loggingSpec.cend());

        d_spec.emplace_back(
            "logstream-server", "[Deprecated] Use --remote instead",
            CommandLineTypes::TypeInfo(
                CommandLineTypes::DataType::COMMANDLINE_DT_STRING),
            CommandLineTypes::ArgumentSpec::O_OPTIONAL,
            CommandLineTypes::ArgumentSpec::C_WITH_ARG);

        buildboxcommon::CommandLine commandLine(d_spec);
        if (!commandLine.parse(argc, argv)) {
            commandLine.usage();
            return 1;
        }

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

        buildboxcommon::LogLevel logLevel = buildboxcommon::LogLevel::ERROR;
        if (!buildboxcommon::parseLoggingOptions(commandLine, logLevel)) {
            return 1;
        }
        BUILDBOX_LOG_SET_LEVEL(logLevel);

        try {
            buildboxcommon::ConnectionOptions connectionOptions;
            buildboxcommon::ConnectionOptionsCommandLine::configureChannel(
                commandLine, "", &connectionOptions);

            if (commandLine.exists("logstream-server")) {
                if (commandLine.exists("remote")) {
                    std::cerr
                        << "WARNING: --logstream-server and --remote are "
                           "specified together. Deprecated "
                           "--logstream-server option is ignored\n";
                }
                else {
                    connectionOptions.d_url =
                        commandLine.getString("logstream-server");
                }
            }

            const auto resourceName = commandLine.getString("resource-name");

            const LogStreamTail::DataAvailableCallback printFunction =
                [](const std::string &data) {
                    std::cout << data << std::flush;
                };

            const auto status = LogStreamTail::readLogStream(
                connectionOptions, resourceName, printFunction);

            BUILDBOX_LOG_INFO("Read() returned: ("
                              << status.error_code() << ", "
                              << status.error_message() << ")");
        }
        catch (const std::exception &e) {
            BUILDBOX_LOG_ERROR("Caught exception ["
                               << e.what() << "] while running " << args[0]);
            return -1;
        }
    }
    catch (const std::exception &exception) {
        // This catch should not be relied on and is here as a backstop.
        BUILDBOX_LOG_ERROR("Caught exception in main " << exception.what());
        return 1;
    }
}
