#include <maps/wikimap/mapspro/services/autocart/pipeline/libs/wiki/include/table.h>

#include <maps/libs/log8/include/log8.h>

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

#include <maps/libs/json/include/value.h>

#include <chrono>
#include <unordered_map>

namespace maps::wiki::autocart::pipeline {

namespace {

static const std::string WIKI_API_URL = "https://wiki-api.yandex-team.ru/_api/";
static const std::string AUTH_HEADER_NAME = "Authorization";
static const std::string CONTENT_TYPE_HEADER_NAME = "Content-Type";
static const std::string JSON_CONTENT_TYPE = "application/json";

static const std::string ASSESSORS_WIKI_TABLE
    = "users/dolotov-e/Tablica-asessorov-v-Toloke";

json::Value loadWikiTableJson(
    const std::string& table, const std::string& oauthToken)
{
    http::URL url(WIKI_API_URL + "frontend/" + table + "/.grid");
    http::Client client;
    client.setTimeout(std::chrono::seconds(5));
    return common::retry(
        [&]() {
            http::Request request(client, http::GET, url);
            request.addHeader(AUTH_HEADER_NAME, "OAuth " + oauthToken);
            request.addHeader(CONTENT_TYPE_HEADER_NAME, JSON_CONTENT_TYPE);
            http::Response response = request.perform();
            if (response.status() == 200) {
                return json::Value::fromString(response.readBody());
            } else {
                throw maps::RuntimeError("Failed to download wiki table: " + table);
            }
        },
        common::RetryPolicy()
            .setTryNumber(6)
            .setInitialCooldown(std::chrono::seconds(1))
            .setCooldownBackoff(2.)
    );
}

std::unordered_map<std::string, std::string>
parseWikiTableStructure(const json::Value& wikiTableJson) {
    static const std::string FIELD_DATA = "data";
    static const std::string FIELD_STRUCTURE = "structure";
    static const std::string FIELD_FIELDS = "fields";
    static const std::string FIELD_TITLE = "title";
    static const std::string FIELD_NAME = "name";
    std::unordered_map<std::string, std::string> nameToTitle;
    const json::Value& structure = wikiTableJson[FIELD_DATA][FIELD_STRUCTURE];
    for (const json::Value& field : structure[FIELD_FIELDS]) {
        nameToTitle[field[FIELD_NAME].as<std::string>()]
            = field[FIELD_TITLE].as<std::string>();
    }
    return nameToTitle;
}

} // namespace

WikiTable loadWikiTable(const std::string& name, const std::string& oauthToken) {
    static const std::string FIELD_DATA = "data";
    static const std::string FIELD_ROWS = "rows";
    static const std::string FIELD_KEY = "__key__";
    static const std::string FIELD_RAW = "raw";

    json::Value wikiTableJson = loadWikiTableJson(name, oauthToken);
    std::unordered_map<std::string, std::string> keyToName
        = parseWikiTableStructure(wikiTableJson);
    WikiTable wikiTable;
    for (const json::Value& row : wikiTableJson[FIELD_DATA][FIELD_ROWS]) {
        WikiTableRow tableRow;
        for (const json::Value& col : row) {
            std::string name = keyToName.at(col[FIELD_KEY].as<std::string>());
            std::string value = col[FIELD_RAW].as<std::string>();
            tableRow[name] = value;
        }
        wikiTable.push_back(tableRow);
    }
    return wikiTable;
}

} // namespace maps::wiki::autocart::pipeline
