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

#ifndef INCLUDED_TREXE_EXECUTIONCONTEXT
#define INCLUDED_TREXE_EXECUTIONCONTEXT

#include <memory>
#include <ostream>
#include <ThreadPool.h>

#include <buildboxcommon_casclient.h>
#include <buildboxcommon_connectionoptions.h>
#include <buildboxcommon_executionclient.h>
#include <buildboxcommon_protos.h>

#include <trexe_actionbuilder.h>
#include <trexe_actiondata.h>
#include <trexe_executionoptions.h>

#include <trexe/result_metadata.pb.h>

using namespace buildboxcommon;

namespace trexe {

class ExecutionContext {
  public:
    ExecutionContext(const std::shared_ptr<const ExecutionOptions> &options,
                     const ConnectionOptions &casConnectionOptions,
                     const ConnectionOptions &executionConnectionOptions,
                     const ConnectionOptions &actioncacheConnectionOptions,
                     const std::shared_ptr<const ConnectionOptions>
                         &logstreamConnectionOptions);

    ExecutionContext(const std::shared_ptr<const ExecutionOptions> &options,
                     const std::shared_ptr<CASClient> &casClient,
                     const std::shared_ptr<ExecutionClient> &execClient);
    ~ExecutionContext();

    bool skipsCacheLookup() const;
    bool isResultCached(bool allowRemoteQuery = false);
    bool cancelOperation();
    bool execute(bool async = false);

    void setActionData(const ActionData &actionData);

    std::shared_ptr<ActionResult>
    getActionResult(bool allowRemoteQuery = false);
    bool downloadResults(const std::string where = "./out/");
    bool downloadResults(const ActionResult &ar,
                         const std::string &where = "./out/");
    bool downloadCompletedOperation(const std::string &where, bool wait);

    template <typename T>
    void writeProtoInJSONToFile(const std::string &filepath, const T &proto,
                                bool throwExc = true);
    bool outputStdoutStderr();

    bool checkLogStreamStatus();

    const TrexeResultMetadata &resultMetadata() const;
    std::shared_ptr<const ActionResult> actionResult() const;

  private:
    std::shared_ptr<const ExecutionOptions> d_executionOptions;
    ActionData d_actionData;
    std::shared_ptr<ActionResult> d_actionResult;
    bool d_getActionResultRemoteQueryCalled = false;
    std::shared_ptr<CASClient> d_casClient;
    std::shared_ptr<ExecutionClient> d_executionClient;
    TrexeResultMetadata d_resultMetadata;
    std::unique_ptr<ThreadPool> d_ioThreadPool = nullptr;

    bool outputStreamContent(std::ostream &stream,
                             const std::string &inlineStream,
                             const Digest &streamDigest);
    void getActionResult(const Operation &operation, ActionResult *ar);
    void updateActionResultMetadata(const ActionResult &actionResult,
                                    bool cached_result);
    void updateOperationMetadata(const Operation &operation);
    void logExecutionMetadata(const ExecutedActionMetadata &metadata);
    void updateGrpcError(const grpc::Status &error);
    void enableLogStream(const Digest &actionDigest);
};

} // namespace trexe

#endif
