#include <maps/wikimap/mapspro/libs/social_serv_serialize/include/jsonize_aggregated_counter.h>

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

#include <map>

namespace maps::wiki::socialsrv::serialize {

using Source = std::string;
using namespace social::feedback;


namespace {

const std::string WORKFLOW = "workflow";
const std::string WORKFLOWS = "workflows";

const std::string SOURCE = "source";
const std::string SOURCES = "sources";

const std::string TYPE = "type";
const std::string TYPES = "types";

const std::string TASKS_COUNT = "tasksCount";

class TypeCounter {
public:
    uint64_t tasksCount() const { return tasksCount_; }
    void addData(const AggregatedCounter& counter) {
        tasksCount_ += counter.count;
    }

private:
    uint64_t tasksCount_{};
};

class SourceCounter {
public:
    uint64_t tasksCount() const { return tasksCount_; }
    const std::map<Type, TypeCounter>& typeCounters() const { return typeCounters_; }

    void addData(const AggregatedCounter& counter)
    {
        REQUIRE(counter.type.has_value(), "missing aggregation over '" << Column::Type << "'");
        typeCounters_[counter.type.value()].addData(counter);
        tasksCount_ += counter.count;
    }
private:
    uint64_t tasksCount_{};
    std::map<Type, TypeCounter> typeCounters_;
};

class WorkflowCounter {
public:
    uint64_t tasksCount() const { return tasksCount_; }
    const std::map<Source, SourceCounter>& sourceCounters() const { return sourceCounters_; }

    void addData(const AggregatedCounter& counter) {
        REQUIRE(counter.source.has_value(), "missing aggregation over '" << Column::Source << "'");
        sourceCounters_[counter.source.value()].addData(counter);
        tasksCount_ += counter.count;
    }
private:
    uint64_t tasksCount_{};
    std::map<Source, SourceCounter> sourceCounters_;
};

class WorkflowCounters {
public:
    uint64_t tasksCount() const { return tasksCount_; }
    const std::map<Workflow, WorkflowCounter>& workflowCounters() const { return workflowCounters_; }

    void addData(const AggregatedCounter& counter)
    {
        REQUIRE(counter.workflow.has_value(), "missing aggregation over '" << Column::Workflow << "'");
        workflowCounters_[counter.workflow.value()].addData(counter);
        tasksCount_ += counter.count;
    }
private:
    uint64_t tasksCount_{};
    std::map<Workflow, WorkflowCounter> workflowCounters_;
};

WorkflowCounters
getWorkflowCounters(const std::vector<AggregatedCounter>& counters)
{
    WorkflowCounters retVal;
    for (const auto& counter: counters) {
        retVal.addData(counter);
    }
    return retVal;
}

void json(
    const std::pair<Type, TypeCounter>& typeCounterPair,
    json::ObjectBuilder builder)
{
    const auto type = typeCounterPair.first;
    const auto& typeCounter = typeCounterPair.second;
    builder[TYPE] = std::string(toString(type));
    builder[TASKS_COUNT] = typeCounter.tasksCount();
}

void json(
    const std::pair<Source, SourceCounter>& sourceCounterPair,
    json::ObjectBuilder builder)
{
    const auto& source = sourceCounterPair.first;
    const auto& sourceCounter = sourceCounterPair.second;
    builder[SOURCE] = source;
    builder[TASKS_COUNT] = sourceCounter.tasksCount();
    builder[TYPES] << [&](json::ArrayBuilder typesBuilder) {
        for (const auto& typeCounterPair : sourceCounter.typeCounters()) {
            typesBuilder << typeCounterPair;
        }
    };
}

void json(
    const std::pair<Workflow, WorkflowCounter>& workflowCounterPair,
    json::ObjectBuilder builder)
{
    const auto workflow = workflowCounterPair.first;
    const auto& workflowCounter = workflowCounterPair.second;
    builder[WORKFLOW] = std::string(toString(workflow));
    builder[TASKS_COUNT] = workflowCounter.tasksCount();
    builder[SOURCES] << [&](json::ArrayBuilder builder) {
        for (const auto& sourceCounterPair : workflowCounter.sourceCounters()) {
            builder << sourceCounterPair;
        }
    };
}

} // namespace

void jsonize(
    json::ObjectBuilder& builder,
    const std::vector<AggregatedCounter>& counters)
{
    const auto workflowCounters = getWorkflowCounters(counters);

    builder[TASKS_COUNT] = workflowCounters.tasksCount();

    builder[WORKFLOWS] << [&](json::ArrayBuilder builder) {
        for (const auto& workflowCounterPair: workflowCounters.workflowCounters()) {
            builder << workflowCounterPair;
        }
    };
}

} // namespace maps::wiki::socialsrv::serialize
