/*
 * Copyright 2021 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 <buildboxcasd_localacinstance.h>
#include <buildboxcasd_metricnames.h>

#include <buildboxcommon_logging.h>
#include <buildboxcommon_protos.h>

#include <buildboxcommonmetrics_countingmetricutil.h>
#include <buildboxcommonmetrics_countingmetricvalue.h>
#include <buildboxcommonmetrics_durationmetrictimer.h>
#include <buildboxcommonmetrics_metricguard.h>
#include <string>

using namespace buildboxcasd;
using namespace buildboxcommon;

std::shared_ptr<AcInstance>
LocalAcInstance::clone(std::shared_ptr<CasInstance> cas, bool readOnly)
{
    return std::make_shared<LocalAcInstance>(*this, cas, readOnly);
}

grpc::Status
LocalAcInstance::GetActionResult(const GetActionResultRequest &request,
                                 ActionResult *result)
{
    buildboxcommon::buildboxcommonmetrics::MetricGuard<
        buildboxcommon::buildboxcommonmetrics::DurationMetricTimer>
        mt(MetricNames::TIMER_NAME_AC_GET_ACTION_RESULT);

    BUILDBOX_LOG_DEBUG("Checking ActionCache for result of ActionDigest='" +
                       buildboxcommon::toString(request.action_digest()) +
                       "'");
    bool read = d_storage->readAction(request.action_digest(), result);
    if (!read) {
        buildboxcommonmetrics::CountingMetricUtil::recordCounterMetric(
            MetricNames::COUNTER_NAME_AC_GET_ACTION_RESULT_MISSES, 1);

        BUILDBOX_LOG_INFO("ActionResult not found for ActionDigest='" +
                          buildboxcommon::toString(request.action_digest()) +
                          "'");

        return grpc::Status(grpc::StatusCode::NOT_FOUND,
                            "Not found in ActionCache");
    }
    else {
        if (AcInstance::hasAllDigests(result)) {
            BUILDBOX_LOG_INFO(
                "ActionResult found for ActionDigest='" +
                buildboxcommon::toString(request.action_digest()) + "'");

            buildboxcommonmetrics::CountingMetricUtil::recordCounterMetric(
                MetricNames::COUNTER_NAME_AC_GET_ACTION_RESULT_HITS, 1);

            return grpc::Status::OK;
        }
        else {
            buildboxcommonmetrics::CountingMetricUtil::recordCounterMetric(
                MetricNames::COUNTER_NAME_AC_GET_ACTION_RESULT_OUTPUT_MISSES,
                1);

            BUILDBOX_LOG_INFO(
                "Some output blob(s) missing from cache for ActionDigest='" +
                buildboxcommon::toString(request.action_digest()) + "'");
            return grpc::Status(
                grpc::StatusCode::NOT_FOUND,
                "ActionResult's output files not found in CAS");
        }
    }
}

grpc::Status
LocalAcInstance::UpdateActionResult(const UpdateActionResultRequest &request,
                                    ActionResult *result)
{
    if (isReadOnly()) {
        return grpc::Status(grpc::StatusCode::PERMISSION_DENIED,
                            "Write access to action cache denied");
    }

    buildboxcommon::buildboxcommonmetrics::MetricGuard<
        buildboxcommon::buildboxcommonmetrics::DurationMetricTimer>
        mt(MetricNames::TIMER_NAME_AC_UPDATE_ACTION_RESULT);

    BUILDBOX_LOG_INFO("Updating ActionCache with result of ActionDigest='" +
                      buildboxcommon::toString(request.action_digest()) + "'");
    try {
        d_storage->storeAction(request.action_digest(),
                               request.action_result());
    }
    catch (const std::system_error &e) {
        return grpc::Status(
            grpc::StatusCode::PERMISSION_DENIED,
            "Unable to write cache file for ActionDigest='" +
                buildboxcommon::toString(request.action_digest()) + "'");
    }
    result->CopyFrom(request.action_result());

    return grpc::Status::OK;
}
