/*
 * Copyright 2019 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_REQUESTMETADATA
#define INCLUDED_BUILDBOXCOMMON_REQUESTMETADATA

#include "build/bazel/remote/execution/v2/remote_execution.pb.h"
#include <buildboxcommon_protos.h>
#include <string>

namespace buildboxcommon {

class RequestMetadataGenerator {

    /** According to the REAPI specification:
     *
     * "An optional Metadata to attach to any RPC request to tell the server
     about
     * an external context of the request."
     *
     * "[...] To use it, the client attaches the header to the call using the
     * canonical proto serialization[.]"

     * name: `build.bazel.remote.execution.v2.requestmetadata-bin`
     * contents: the base64 encoded binary `RequestMetadata` message.
     *
     *
     * This class helps to create that message and attach it to a
     * `grpc::ClientContext` object.
     */

  public:
    RequestMetadataGenerator();
    RequestMetadataGenerator(const std::string &tool_name,
                             const std::string &tool_version);

    void attach_request_metadata(grpc::ClientContext *context) const;

    void set_tool_details(const std::string &tool_name,
                          const std::string &tool_details);

    void set_action_id(const std::string &action_id);
    void set_tool_invocation_id(const std::string &tool_invocation_id);
    void set_correlated_invocations_id(
        const std::string &correlated_invocations_id);
    void set_action_mnemonic(const std::string &action_mnemonic);
    void set_target_id(const std::string &target_id);
    void set_configuration_id(const std::string &configuration_id);

    const std::string &getActionId() const;
    const std::string &getToolInvocationId() const;
    const std::string &getCorrelatedInvocationsId() const;
    const std::string &getActionMnemonic() const;
    const std::string &getTargetId() const;
    const std::string &getConfigurationId() const;
    const ToolDetails &getToolDetails() const;

    /** Extract the RequestMetadata from client metadata as returned by
     * grpc::ServerContext.client_metadata()
     *
     *  If the special HEADER_NAME metadata key is found but it does not parse
     * into a valid RequestMetadata message an std::invalid_argument exception
     * is thrown.
     */
    static RequestMetadata parse_request_metadata(
        const std::multimap<grpc::string_ref, grpc::string_ref> &metadata);

    static const std::string HEADER_NAME;

  private:
    void attach_request_metadata(grpc::ClientContext *context,
                                 const std::string &actionId,
                                 const std::string &toolInvocationId,
                                 const std::string &correlatedInvocationsId,
                                 const std::string &actionMnemonic,
                                 const std::string &targetId,
                                 const std::string &configurationId) const;

    ToolDetails d_tool_details;
    std::string d_action_id;
    std::string d_tool_invocation_id;
    std::string d_correlated_invocations_id;
    std::string d_actionMnemonic;
    std::string d_targetId;
    std::string d_configurationId;

  protected:
    RequestMetadata generate_request_metadata(
        const std::string &actionId, const std::string &toolInvocationId,
        const std::string &correlatedInvocationsId,
        const std::string &actionMnemonic, const std::string &targetId,
        const std::string &configurationId) const;
};

} // namespace buildboxcommon

#endif // INCLUDED_BUILDBOXCOMMON_REQUESTMETADATA
