/*
 * Copyright 2022 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 <buildboxcommon_digestgenerator.h>
#include <buildboxcommon_logging.h>
#include <buildboxcommon_logging_commandline.h>
#include <trexe_cmdlinespec.h>

namespace trexe {

using ArgumentSpec = buildboxcommon::CommandLineTypes::ArgumentSpec;
using DataType = buildboxcommon::CommandLineTypes::DataType;
using TypeInfo = buildboxcommon::CommandLineTypes::TypeInfo;
using DefaultValue = buildboxcommon::CommandLineTypes::DefaultValue;

CmdLineSpec::CmdLineSpec(
    buildboxcommon::ConnectionOptionsCommandLine const &connectionOptionsSpec,
    buildboxcommon::ConnectionOptionsCommandLine const
        &connectionOptionsCasSpec,
    buildboxcommon::ConnectionOptionsCommandLine const
        &connectionOptionsAcSpec,
    buildboxcommon::ConnectionOptionsCommandLine const
        &connectionOptionsExecSpec,
    buildboxcommon::ConnectionOptionsCommandLine const
        &connectionOptionsLogStreamSpec)
{
    d_spec.insert(d_spec.end(), connectionOptionsSpec.spec().cbegin(),
                  connectionOptionsSpec.spec().cend());

    d_spec.insert(d_spec.end(), connectionOptionsCasSpec.spec().cbegin(),
                  connectionOptionsCasSpec.spec().cend());

    d_spec.insert(d_spec.end(), connectionOptionsAcSpec.spec().cbegin(),
                  connectionOptionsAcSpec.spec().cend());

    d_spec.insert(d_spec.end(), connectionOptionsExecSpec.spec().cbegin(),
                  connectionOptionsExecSpec.spec().cend());

    d_spec.insert(d_spec.end(), connectionOptionsLogStreamSpec.spec().cbegin(),
                  connectionOptionsLogStreamSpec.spec().cend());

    d_spec.emplace_back("config-file",
                        "Config file to specify all options in TOML.",
                        TypeInfo(DataType::COMMANDLINE_DT_STRING),
                        ArgumentSpec::O_OPTIONAL, ArgumentSpec::C_WITH_ARG);

    d_spec.emplace_back("d",
                        "Download completed action outputs to this path. "
                        "Leave empty for no download.",
                        TypeInfo(DataType::COMMANDLINE_DT_STRING),
                        ArgumentSpec::O_OPTIONAL, ArgumentSpec::C_WITH_ARG);

    d_spec.emplace_back(
        "digest-function",
        "Set a custom digest function. Supported functions: " +
            buildboxcommon::DigestGenerator::supportedDigestFunctionsList(),
        TypeInfo(DataType::COMMANDLINE_DT_STRING), ArgumentSpec::O_OPTIONAL,
        ArgumentSpec::C_WITH_ARG,
        DefaultValue(buildboxcommon::DigestFunction_Value_Name(
            BUILDBOXCOMMON_DIGEST_FUNCTION_VALUE)));

    d_spec.emplace_back("working-dir", "Path to working directory.",
                        TypeInfo(DataType::COMMANDLINE_DT_STRING),
                        ArgumentSpec::O_OPTIONAL, ArgumentSpec::C_WITH_ARG);

    d_spec.emplace_back("input-root-digest", "Input root digest for Action. ",
                        TypeInfo(DataType::COMMANDLINE_DT_STRING),
                        ArgumentSpec::O_OPTIONAL, ArgumentSpec::C_WITH_ARG);

    d_spec.emplace_back(
        "input-path",
        "Input path for Action. "
        "--input-path=<path> for each input. "
        "Use `local-path:remote-path[:PROPERTIES]` to map an input to a "
        "specific path remotely with optional properties. PROPERTIES is a "
        "comma-separated list of 'ro', 'rw', 'unix_mode', 'mtime'",
        TypeInfo(DataType::COMMANDLINE_DT_STRING_ARRAY),
        ArgumentSpec::O_OPTIONAL, ArgumentSpec::C_WITH_ARG);

    d_spec.emplace_back("output-path",
                        "Output path to capture. "
                        "--output-path=<path> for each output",
                        TypeInfo(DataType::COMMANDLINE_DT_STRING_ARRAY),
                        ArgumentSpec::O_OPTIONAL, ArgumentSpec::C_WITH_ARG);

    d_spec.emplace_back("output-node-properties",
                        "A list of keys that indicate what additional file "
                        "attributes should be captured "
                        "--output-node-properties=<name> of each property",
                        TypeInfo(DataType::COMMANDLINE_DT_STRING_ARRAY),
                        ArgumentSpec::O_OPTIONAL, ArgumentSpec::C_WITH_ARG);

    d_spec.emplace_back("stdout-file", "Filepath to store stdout",
                        TypeInfo(DataType::COMMANDLINE_DT_STRING),
                        ArgumentSpec::O_OPTIONAL, ArgumentSpec::C_WITH_ARG);

    d_spec.emplace_back("stderr-file", "Filepath to store stderr",
                        TypeInfo(DataType::COMMANDLINE_DT_STRING),
                        ArgumentSpec::O_OPTIONAL, ArgumentSpec::C_WITH_ARG);

    d_spec.emplace_back(
        "platform-properties",
        "The platform requirements for the execution environment "
        "--platform-properties=<key>=<value>,<key>=... for each property",
        TypeInfo(DataType::COMMANDLINE_DT_STRING), ArgumentSpec::O_OPTIONAL,
        ArgumentSpec::C_WITH_ARG);

    d_spec.emplace_back("environment", "[Deprecated] Use --env instead",
                        TypeInfo(DataType::COMMANDLINE_DT_STRING),
                        ArgumentSpec::O_OPTIONAL, ArgumentSpec::C_WITH_ARG);

    d_spec.emplace_back(
        "env",
        "Environment variables to set in the running program's environment. "
        "To specify multiple ones, use `--env=K1=V1 --env=K2=V2 ...`",
        TypeInfo(DataType::COMMANDLINE_DT_STRING_ARRAY),
        ArgumentSpec::O_OPTIONAL);

    d_spec.emplace_back(
        "exec-timeout",
        "The timeout after which the execution of an Action should be killed "
        "--exec-timeout=<seconds>",
        TypeInfo(DataType::COMMANDLINE_DT_INT), ArgumentSpec::O_OPTIONAL,
        ArgumentSpec::C_WITH_ARG);

    d_spec.emplace_back("priority",
                        "The priority (relative importance) of this action. A "
                        "priority of 0 means the *default* priority "
                        "--priority=<priority>",
                        TypeInfo(DataType::COMMANDLINE_DT_INT),
                        ArgumentSpec::O_OPTIONAL, ArgumentSpec::C_WITH_ARG);

    d_spec.emplace_back("skip-cache-lookup",
                        "Flag indicating that the server should not check the "
                        "cache when executing an action",
                        TypeInfo(DataType::COMMANDLINE_DT_BOOL),
                        ArgumentSpec::O_OPTIONAL, ArgumentSpec::C_WITHOUT_ARG);

    d_spec.emplace_back(
        "do-not-cache",
        "Flag indicating that ActionResults should not be cached, and "
        "duplicate Action executions will not be merged",
        TypeInfo(DataType::COMMANDLINE_DT_BOOL), ArgumentSpec::O_OPTIONAL,
        ArgumentSpec::C_WITHOUT_ARG);

    d_spec.emplace_back(
        "follow-symlinks",
        "Follow symlinks when building input trees. "
        "If enabled, each input cannot be circular. Enabled by default",
        TypeInfo(DataType::COMMANDLINE_DT_BOOL), ArgumentSpec::O_OPTIONAL,
        ArgumentSpec::C_WITHOUT_ARG);
    d_spec.emplace_back("do-not-follow-symlinks", "Do not follow symlinks",
                        TypeInfo(DataType::COMMANDLINE_DT_BOOL),
                        ArgumentSpec::O_OPTIONAL);

    d_spec.emplace_back(
        "correlated-invocations-id",
        "An identifier to tie multiple tool invocations together. "
        "For example, runs of foo_test, bar_test and baz_test on a "
        "post-submit of a given patch. "
        "--correlated-invocations-id=<str>",
        TypeInfo(DataType::COMMANDLINE_DT_STRING), ArgumentSpec::O_OPTIONAL,
        ArgumentSpec::C_WITH_ARG);

    d_spec.emplace_back(
        "tool-invocation-id",
        "An identifier that ties multiple actions together to a "
        "final result. For example, multiple actions are required"
        " to build and run foo_test. "
        "--tool-invocation-id=<str>",
        TypeInfo(DataType::COMMANDLINE_DT_STRING), ArgumentSpec::O_OPTIONAL,
        ArgumentSpec::C_WITH_ARG);

    d_spec.emplace_back("action-mnemonic",
                        "A brief description of the kind of action, for "
                        "example CppCompile or GoLink. "
                        "--action-mnemonic=<str>",
                        TypeInfo(DataType::COMMANDLINE_DT_STRING),
                        ArgumentSpec::O_OPTIONAL, ArgumentSpec::C_WITH_ARG);

    d_spec.emplace_back("target-id",
                        "An identifier for the target which produced the "
                        "action that trexe will send. "
                        "--target-id=<str>",
                        TypeInfo(DataType::COMMANDLINE_DT_STRING),
                        ArgumentSpec::O_OPTIONAL, ArgumentSpec::C_WITH_ARG);

    d_spec.emplace_back(
        "configuration-id",
        "An identifier for the configuration in which the target was built, "
        "to distinguish between e.g. different sets of host tools or "
        "different target platforms. "
        "--configuration-id=<str>",
        TypeInfo(DataType::COMMANDLINE_DT_STRING), ArgumentSpec::O_OPTIONAL,
        ArgumentSpec::C_WITH_ARG);

    d_spec.emplace_back(
        "tool-name",
        "Additional string to append to the tool-name (trexe) sent in the "
        "metadata. Useful to append additional information about "
        "invocation e.x. python to indicate python was used"
        "--tool-name=<str>",
        TypeInfo(DataType::COMMANDLINE_DT_STRING), ArgumentSpec::O_OPTIONAL,
        ArgumentSpec::C_WITH_ARG);

    d_spec.emplace_back(
        "tool-version",
        "Additional string to append to the tool-version (e.x. 0.0.1) sent "
        "in the metadata. Useful to append additional information about "
        "invocation e.x. 2.3.4 to append 2.3.4 (version of a package named in"
        "tool-name) to the tool-version metadata"
        "--tool-version=<str>",
        TypeInfo(DataType::COMMANDLINE_DT_STRING), ArgumentSpec::O_OPTIONAL,
        ArgumentSpec::C_WITH_ARG);

    d_spec.emplace_back(
        "locality-hint",
        "A hint to the scheduler about the locality of the execution. "
        "This is used to schedule the execution on a worker that is close to "
        "the data of a previous execution. This only works if the remote "
        "scheduler is BuildGrid.",
        TypeInfo(DataType::COMMANDLINE_DT_STRING), ArgumentSpec::O_OPTIONAL,
        ArgumentSpec::C_WITH_ARG);

    d_spec.emplace_back(
        "salt",
        "An optional value to place the `Action` into a separate cache "
        "namespace from other instances having the same field contents"
        "--salt=<str>",
        TypeInfo(DataType::COMMANDLINE_DT_STRING), ArgumentSpec::O_OPTIONAL,
        ArgumentSpec::C_WITH_ARG);

    d_spec.emplace_back(
        "wait",
        "Flag indicating that trexe should block until execution is complete. "
        "This is the default behavior if --operation is not set.",
        TypeInfo(DataType::COMMANDLINE_DT_BOOL), ArgumentSpec::O_OPTIONAL,
        ArgumentSpec::C_WITHOUT_ARG);

    d_spec.emplace_back(
        "no-wait",
        "Flag indicating that trexe should _not_ block until execution is "
        "complete. If --operation is not set, this will cause trexe to exit "
        "with code 0 upon successful job submission and write the operation "
        "id to `stdout` in plaintext (e.g. app-cr/6283a9c6-85b2-4b03-92bd). "
        "If --operation is set, this will cause trexe to exit with non-zero "
        "code. This is the default if --operation is not set.",
        TypeInfo(DataType::COMMANDLINE_DT_BOOL), ArgumentSpec::O_OPTIONAL,
        ArgumentSpec::C_WITHOUT_ARG);

    d_spec.emplace_back(
        "operation",
        "Operation ID to lookup and download outputs from (to the "
        " specified directory) if -d is set.",
        TypeInfo(DataType::COMMANDLINE_DT_STRING), ArgumentSpec::O_OPTIONAL,
        ArgumentSpec::C_WITH_ARG);

    d_spec.emplace_back(
        "cancel", "Cancels the specified operation. Requires --operation.",
        TypeInfo(DataType::COMMANDLINE_DT_BOOL), ArgumentSpec::O_OPTIONAL,
        ArgumentSpec::C_WITHOUT_ARG);

    d_spec.emplace_back("result-metadata-file",
                        "The metadata of trexe execution in JSON will be "
                        "written into this file",
                        TypeInfo(DataType::COMMANDLINE_DT_STRING),
                        ArgumentSpec::O_OPTIONAL, ArgumentSpec::C_WITH_ARG);

    d_spec.emplace_back(
        "action-result-json",
        "The full action result in JSON will be written into this file ",
        TypeInfo(DataType::COMMANDLINE_DT_STRING), ArgumentSpec::O_OPTIONAL,
        ArgumentSpec::C_WITH_ARG);

    d_spec.emplace_back("cache-only",
                        "If not already cached, execute the action in a local "
                        "BuildBox runner, caching the result",
                        TypeInfo(DataType::COMMANDLINE_DT_BOOL),
                        ArgumentSpec::O_OPTIONAL, ArgumentSpec::C_WITHOUT_ARG);

    d_spec.emplace_back("buildbox-run", "Runner command for local execution",
                        TypeInfo(DataType::COMMANDLINE_DT_STRING),
                        ArgumentSpec::O_OPTIONAL, ArgumentSpec::C_WITH_ARG);

    d_spec.emplace_back(
        "runner-arg",
        "Arguments to pass to buildbox-run for local execution\n"
        "This can be useful if the buildbox-run implementation you're using "
        "supports non-standard options",
        TypeInfo(DataType::COMMANDLINE_DT_STRING_ARRAY),
        ArgumentSpec::O_OPTIONAL, ArgumentSpec::C_WITH_ARG);

    d_spec.emplace_back("log-progress",
                        "Enables progress logging"
                        "[optional]",
                        TypeInfo(DataType::COMMANDLINE_DT_BOOL),
                        ArgumentSpec::O_OPTIONAL, ArgumentSpec::C_WITHOUT_ARG);

    d_spec.emplace_back(
        "stream-logs",
        "Enables live streaming of the remote command's stdout/stderr to the "
        "local stdout/stderr.",
        TypeInfo(DataType::COMMANDLINE_DT_BOOL), ArgumentSpec::O_OPTIONAL,
        ArgumentSpec::C_WITHOUT_ARG);

    d_spec.emplace_back("use-localcas",
                        "Use the LocalCAS API for CAS requests. This requires "
                        "connecting to buildbox-casd.",
                        TypeInfo(DataType::COMMANDLINE_DT_BOOL),
                        ArgumentSpec::O_OPTIONAL, ArgumentSpec::C_WITHOUT_ARG);

    d_spec.emplace_back(
        "localcas-skip-upload",
        "Skip uploading to the remote CAS when using the LocalCAS API. This "
        "has no effect if `--use-localcas` isn't set.",
        TypeInfo(DataType::COMMANDLINE_DT_BOOL), ArgumentSpec::O_OPTIONAL,
        ArgumentSpec::C_WITHOUT_ARG);
    d_spec.emplace_back("num-digest-threads",
                        "The number of threads to hash files to digests. "
                        "Default=min(hardware-concurrency,16)",
                        TypeInfo(DataType::COMMANDLINE_DT_INT),
                        ArgumentSpec::O_OPTIONAL, ArgumentSpec::C_WITH_ARG);
    d_spec.emplace_back("num-io-threads",
                        "The number of threads for proxy CAS instances to "
                        "upload or download blobs from/to remote CAS. For "
                        "now, only upload is parallelized."
                        "Default=min(hardware-concurrency*2,32)",
                        TypeInfo(DataType::COMMANDLINE_DT_INT),
                        ArgumentSpec::O_OPTIONAL, ArgumentSpec::C_WITH_ARG);

    auto loggingSpec = buildboxcommon::loggingCommandLineSpec();

    d_spec.insert(d_spec.end(), loggingSpec.cbegin(), loggingSpec.cend());

    d_spec.emplace_back("", "Command to remote", TypeInfo(&d_command),
                        ArgumentSpec::O_OPTIONAL,
                        ArgumentSpec::C_WITH_REST_OF_ARGS);

    d_spec.emplace_back(
        "metrics-mode",
        "Metrics Mode: --metrics-mode=MODE - options for MODE are\n"
        "udp://<hostname>:<port>\nfile:///path/to/file\nstderr",
        TypeInfo(DataType::COMMANDLINE_DT_STRING), ArgumentSpec::O_OPTIONAL,
        ArgumentSpec::C_WITH_ARG);
    d_spec.emplace_back("metrics-publish-interval",
                        "Metrics publishing interval",
                        TypeInfo(DataType::COMMANDLINE_DT_INT),
                        ArgumentSpec::O_OPTIONAL, ArgumentSpec::C_WITH_ARG);
    d_spec.emplace_back("metrics-prefix", "Prefix of metric names",
                        TypeInfo(DataType::COMMANDLINE_DT_STRING),
                        ArgumentSpec::O_OPTIONAL, ArgumentSpec::C_WITH_ARG);
}

}; // namespace trexe
