#include "metrics.h"

#include "time_computer.h"

#include <algorithm>
#include <iostream>

namespace maps::wiki::user_edits_metrics {

namespace {

bool areRegionsIntersects(
    const std::set<std::string>& regions1,
    const std::set<std::string>& regions2)
{
    std::vector<std::string> intersection;
    std::set_intersection(
        regions1.begin(), regions1.end(),
        regions2.begin(), regions2.end(),
        std::back_inserter(intersection));
    return !intersection.empty();
}

} // namespace

std::string Metric::name() const
{
    const auto quantileStr = std::to_string(int(quantile * 100)) + "_prc";
    return
        region + "." + userType + "." + std::string(toString(event)) + "." +
        (inTrunk ? "in_trunk." : "") +
        quantileStr +
        (estimated == Estimated::Yes ? ".estimated" : "");
}

std::optional<Metric> quantileMetric(
    const std::vector<CommitData>& commitData,
    const std::string& regionName,
    const std::set<std::string>& eventRegions,
    CommitFilter commitFilter,
    Event event,
    double quantile,
    bool inTrunk,
    const std::string& userType)
{
    ASSERT(0 < quantile && quantile <= 1);

    std::vector<std::optional<Metric>> metrics;
    metrics.reserve(commitData.size());

    for (const auto& commit: commitData) {
        if (commit.inTrunk() != inTrunk ||
            !commitFilter(commit))
        {
            continue;
        }

        if (!commit.insideRegion(regionName)) {
            continue;
        }
        if (!areRegionsIntersects(commit.regionsFromCommit(), eventRegions)) {
            // Do not process commit for all_regions if event was never deployed
            // in any of these regions
            continue;
        }

        auto eventTime = commit.findEvent(regionName, event);
        if (eventTime) {
            auto value = std::chrono::duration_cast<std::chrono::seconds>(
                *eventTime - commit.created());
            auto workdaysValue = computeDurationMinusWeekends(commit.created(), *eventTime);
            metrics.emplace_back(
                Metric{commit.created(), value, workdaysValue, quantile, regionName, userType, event, inTrunk}
            );
        } else {
            metrics.emplace_back(std::nullopt);
        }
    }

    if (metrics.empty()) {
        return std::nullopt;
    }

    const auto percentileIt = metrics.begin() + std::llround(quantile * (metrics.size() - 1));
    std::nth_element(
        metrics.begin(), percentileIt, metrics.end(),
        [](const std::optional<Metric>& left, const std::optional<Metric>& right) {
            if (!left) {
                return false;
            }
            if (!right) {
                return true;
            }
            return left->value < right->value;
        });
    return *percentileIt;
}

} // namespace maps::wiki::user_edits_metrics
