/*
 * 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.
 */

#ifndef INCLUDED_BUILDBOXCOMMON_CONNECTIONOPTIONS
#define INCLUDED_BUILDBOXCOMMON_CONNECTIONOPTIONS

#include <grpcpp/channel.h>
#include <memory>
#include <optional>
#include <set>
#include <string>
#include <string_view>
#include <vector>

namespace buildboxcommon {

// coverity[UNINIT_CTOR]
struct ConnectionOptions {
    std::string d_clientCert;
    std::string d_clientCertPath;
    std::string d_clientKey;
    std::string d_clientKeyPath;
    std::string d_accessTokenPath;
    std::string d_instanceName;
    std::string d_serverCert;
    std::string d_serverCertPath;
    std::string d_url;
    bool d_useGoogleApiAuth = false;
    std::string d_tokenReloadInterval;
    std::string d_loadBalancingPolicy;
    std::string d_credentialHelperPath;

    /*
     * These are strings to allow for easier
     * propagation from the worker to the runner.
     */
    std::string d_retryLimit = "4";    /* Number of times to retry */
    std::string d_retryDelay = "1000"; /* Initial delay in milliseconds */
    std::set<grpc::StatusCode> d_retryOnCodes; /* GRPC status codes to retry*/
    std::string d_requestTimeout = "0"; /* How long to wait for a response */
    int64_t d_minThroughput = 0; /* Minimum throughput in bytes per second */
    std::string d_keepaliveTime = "0"; /* The period for keepalive pings */

    /**
     * If the given argument is a server option, update this struct with
     * it and return true. Otherwise, return false.
     *
     * Valid server options are "--remote=URL", "--instance=NAME",
     * "--server-cert=PATH", "--client-key=PATH", "--client-cert=PATH", and,
     * "--access-token=PATH"
     *
     * If a prefix is passed, it's added to the name of each option.
     * (For example, passing a prefix of "cas-" would cause this method to
     * recognize "--cas-remote=URL" and so on.)
     */
    bool parseArg(std::string_view arg,
                  std::optional<std::string_view> prefix = std::nullopt);

    void setClientCert(const std::string &value);
    void setClientCertPath(const std::string &value);
    void setClientKey(const std::string &value);
    void setClientKeyPath(const std::string &value);
    void setAccessTokenPath(const std::string &value);
    void setTokenReloadInterval(const std::string &value);
    void setInstanceName(const std::string &value);
    void setRetryDelay(const std::string &value);
    void setRetryLimit(const std::string &value);
    void setRetryOnCodes(const std::set<grpc::StatusCode> &value);
    void setRequestTimeout(const std::string &value);
    void setMinThroughput(const std::string &value);
    void setKeepaliveTime(const std::string &value);
    void setServerCert(const std::string &value);
    void setServerCertPath(const std::string &value);
    void setUrl(const std::string &value);
    void setUseGoogleApiAuth(const bool value);
    void setLoadBalancingPolicy(const std::string &value);

    /**
     * Add arguments corresponding to this struct's settings to the given
     * vector. If a prefix is passed, it's added to the name of each
     * option as in parseArg.
     */
    void putArgs(std::vector<std::string> *out,
                 const char *prefix = nullptr) const;

    /**
     * Create a gRPC Channel from the options in this struct.
     */
    std::shared_ptr<grpc::Channel> createChannel() const;

    /**
     * Print usage-style help messages for each of the arguments parsed
     * by ConnectionOptions.
     *
     * padWidth is the column to align the argument descriptions to,
     * serviceName is a human-readable name to be used in the "URL for
     * [serviceName] service" help message, and the prefix is added to
     * the name of each option as in parseArg.
     */
    static void printArgHelp(int padWidth, const char *serviceName = "CAS",
                             const char *prefix = nullptr);

    /**
     * Reset all options to default values
     */
    void reset();
};

std::ostream &operator<<(std::ostream &out, const ConnectionOptions &obj);

} // namespace buildboxcommon
#endif
