#include "clickhouse.h"

#include <maps/libs/log8/include/log8.h>
#include <maps/wikimap/mapspro/services/mrc/libs/clickhouse/include/column.h>
#include <maps/wikimap/mapspro/services/mrc/libs/clickhouse/include/table.h>

namespace maps::mrc::feedback_stat::clickhouse {

namespace {

const TString FEEDBACK_TBL = "feedback";
const TString SOURCE_COL = "source";
const TString DATE_COL = "date";
const TString TYPE_COL = "type";
const TString WAITING_COL = "waiting";
const TString REJECTED_COL = "rejected";
const TString ACCEPTED_COL = "accepted";
const TString COMMITTED_COL = "committed";

struct FeedbackTable : maps::mrc::clickhouse::Table<int8_t,
                                                    uint64_t,
                                                    int8_t,
                                                    uint64_t,
                                                    uint64_t,
                                                    uint64_t,
                                                    uint64_t>
{
    FeedbackTable(TString tableName)
        : Table(std::move(tableName),
                std::make_tuple(SOURCE_COL,
                                DATE_COL,
                                TYPE_COL,
                                WAITING_COL,
                                REJECTED_COL,
                                ACCEPTED_COL,
                                COMMITTED_COL))
    {
    }
};

auto forEachWriteHost = [](const common::ClickHouseConfig& config, auto fun) {
    NClickHouse::TClientOptions options;
    options.SetDefaultDatabase(TString(config.database()))
        .SetUser(TString(config.user()))
        .SetPassword(TString(config.password()));
    for (const auto& host : config.writeHosts()) {
        NClickHouse::TClient client(options.SetHost(TString(host)));
        fun(client);
    }
};

template <class ClickHouseTable>
void flush(ClickHouseTable& table, const common::ClickHouseConfig& config)
{
    NClickHouse::TBlock block;
    table.addToBlock(block);
    forEachWriteHost(config,
                     [&](auto& client) { client.Insert(table.name(), block); });
    table.clear();
}

}  // namespace

void upload(const TTimelineBySource& timelineBySource,
            const common::ClickHouseConfig& config)
{
    FeedbackTable table{FEEDBACK_TBL};
    forEachWriteHost(config, [&](auto& client) { table.create(client); });
    size_t rows = 0;

    for (const auto& [source, timeline] : timelineBySource) {
        auto src = static_cast<int8_t>(source);

        for (const auto& [day, counterByType] : timeline) {
            auto ms = static_cast<uint64_t>(
                std::chrono::duration_cast<std::chrono::milliseconds>(
                    day.time_since_epoch())
                    .count());

            for (const auto& [feedbackType, counter] : counterByType) {
                table.addRow(std::make_tuple(src,
                                             ms,
                                             static_cast<int8_t>(feedbackType),
                                             counter.waiting,
                                             counter.rejected,
                                             counter.accepted,
                                             counter.committed));
                if ((++rows % BATCH_SIZE) == 0) {
                    flush(table, config);
                    INFO() << "clickhouse rows: " << rows;
                }
            }
        }
    }
    if ((rows % BATCH_SIZE) != 0) {
        flush(table, config);
        INFO() << "clickhouse rows: " << rows;
    }
}

}  // namespace maps::mrc::feedback_stat::clickhouse
