#include "user_edits_processing_times.h"

#include "stat_util.h"

#include <maps/libs/common/include/exception.h>

namespace maps::wiki::user_edits_metrics {

UserEditsProcessingTimesDimensions::UserEditsProcessingTimesDimensions(
    chrono::TimePoint fielddate,
    Days days,
    std::string region,
    size_t userType,
    unsigned char percentile,
    bool inTrunk
)
    : fielddate_(std::chrono::round<std::chrono::seconds>(fielddate))
    , days_(days)
    , region_(std::move(region))
    , userType_(userType)
    , percentile_(percentile)
    , inTrunk_(inTrunk)
{}


void UserEditsProcessingTimesDimensions::printHeader(csv::OutputStream& os)
{
    os << "fielddate" << "days" << "region" << "user_type" << "percentile" << "in_trunk";
}


void UserEditsProcessingTimesDimensions::print(csv::OutputStream& os) const
{
    os << chrono::formatIntegralDateTime(fielddate_, "%Y-%m-%d %H:%M:%S")
       << daysToStatDictValue(days_)
       << region_
       << userType_
       << static_cast<unsigned int>(percentile_)
       << inTrunk_;
}

std::map<std::string, std::string> UserEditsProcessingTimesDimensions::keyValues(
    pqxx::transaction_base& txn) const
{
    std::map<std::string, std::string> result;
    result.emplace(
        "fielddate",
        txn.quote(chrono::formatIntegralDateTime(fielddate_, "%Y-%m-%d  %H:%M:%S")));

    result.emplace("days__str", txn.quote(std::string(toString(days_))));
    result.emplace("days", std::to_string(daysToStatDictValue(days_)));
    result.emplace("region", txn.quote(region_));
    result.emplace("user_type__str", txn.quote(statDictValueToUserType(userType_)));
    result.emplace("user_type", std::to_string(userType_));
    result.emplace("percentile", std::to_string(static_cast<unsigned int>(percentile_)));
    result.emplace("in_trunk", inTrunk_ ? "1" : "0");
    result.emplace("in_trunk__str", inTrunk_ ? "'True'" : "'False'");

    return result;
}


void UserEditsProcessingTimesMeasures::printHeader(csv::OutputStream& os)
{
    os << "cameras" << "carparks" << "geocoder" << "graph"
       << "mtr_export" << "renderer"
       << "bicycle_graph" << "pedestrian_graph"
       << "approved" << "exported" << "in_stable" << "resolved";
}


void UserEditsProcessingTimesMeasures::print(csv::OutputStream& os) const
{
    os << toString(cameras) << toString(carparks) << toString(geocoder) << toString(graph)
       << toString(mtrExport) << toString(renderer)
       << toString(bicycleGraph) << toString(pedestrianGraph)
       << toString(approved) << toString(exported) << toString(inStable) << toString(resolved);
}

std::map<std::string, std::string> UserEditsProcessingTimesMeasures::keyValues() const
{
    std::map<std::string, std::string> result;

    result.emplace("cameras", toString(cameras));
    result.emplace("carparks", toString(carparks));
    result.emplace("geocoder", toString(geocoder));
    result.emplace("graph", toString(graph));
    result.emplace("mtr_export", toString(mtrExport));
    result.emplace("renderer", toString(renderer));
    result.emplace("bicycle_graph", toString(bicycleGraph));
    result.emplace("pedestrian_graph", toString(pedestrianGraph));
    result.emplace("approved", toString(approved));
    result.emplace("exported", toString(exported));
    result.emplace("in_stable", toString(inStable));
    result.emplace("resolved", toString(resolved));

    return result;
}


UserEditsProcessingTimesReport::UserEditsProcessingTimesReport()
    : Report("mapspro.user_edits_processing_times")
    , ReportDataUploader(Report::name)
{}

void UserEditsProcessingTimesReport::upload(pqxx::transaction_base& txn) const
{
    ReportDataUploader::upload(txn, dimensionsToMeasures_);
}

void
UserEditsProcessingTimesReport::add(const MetricVec& metrics)
{
    for (const auto& metric: metrics) {
        const unsigned char percentile = metric.quantile * 100;
        const auto userType = userTypeToStatDictValue(metric.userType);

        auto& alldaysMeasures = dimensionsToMeasures_[
            UserEditsProcessingTimesDimensions(metric.time, Days::AllDays, metric.region, userType, percentile, metric.inTrunk)
        ];
        auto& workdaysMeasures = dimensionsToMeasures_[
            UserEditsProcessingTimesDimensions(metric.time, Days::WorkDays, metric.region, userType, percentile, metric.inTrunk)
        ];

        switch (metric.event) {
            case Event::DeployedCams:
                alldaysMeasures.cameras = metric.value;
                workdaysMeasures.cameras = metric.workdaysValue;
                break;
            case Event::DeployedCarparks:
                alldaysMeasures.carparks = metric.value;
                workdaysMeasures.carparks = metric.workdaysValue;
                break;
            case Event::DeployedGeocoder:
                alldaysMeasures.geocoder = metric.value;
                workdaysMeasures.geocoder = metric.workdaysValue;
                break;
            case Event::DeployedGraph:
                alldaysMeasures.graph = metric.value;
                workdaysMeasures.graph = metric.workdaysValue;
                break;
            case Event::DeployedMtrExport:
                alldaysMeasures.mtrExport = metric.value;
                workdaysMeasures.mtrExport = metric.workdaysValue;
                break;
            case Event::DeployedRenderer:
                alldaysMeasures.renderer = metric.value;
                workdaysMeasures.renderer = metric.workdaysValue;
                break;
            case Event::DeployedBicycleGraph:
                alldaysMeasures.bicycleGraph = metric.value;
                workdaysMeasures.bicycleGraph = metric.workdaysValue;
                break;
            case Event::DeployedPedestrianGraph:
                alldaysMeasures.pedestrianGraph = metric.value;
                workdaysMeasures.pedestrianGraph = metric.workdaysValue;
                break;
            case Event::Approved:
                alldaysMeasures.approved = metric.value;
                workdaysMeasures.approved = metric.workdaysValue;
                break;
            case Event::Exported:
                alldaysMeasures.exported = metric.value;
                workdaysMeasures.exported = metric.workdaysValue;
                break;
            case Event::InStable:
                alldaysMeasures.inStable = metric.value;
                workdaysMeasures.inStable = metric.workdaysValue;
                break;
            case Event::Resolved:
                alldaysMeasures.resolved = metric.value;
                workdaysMeasures.resolved = metric.workdaysValue;
                break;
            default:
                throw RuntimeError() << "Unsupported event type: '" << metric.event << "'.";
        }
    }
}

} // namespace maps::wiki::user_edits_metrics
