#include <util/draft/datetime.h>
#include <util/generic/set.h>
#include <util/string/join.h>

#include <library/cpp/json/json_reader.h>

#include <wmconsole/version3/wmcutil/datetime.h>
#include <wmconsole/version3/wmcutil/http_client.h>
#include <wmconsole/version3/wmcutil/yt/misc.h>

#include "beautyurl.h"
#include "config.h"
#include "monitor.h"

namespace NWebmaster {

namespace {
const char *F_HOST          = "Host";
const char *F_BEAUTY_URL    = "BeautyUrl";
}

void GetBeautyUrls(const TString &handler, const TDeque<TString> &hosts, TMap<TString, TString> &response) {
    const char *F_ERROR = "error";
    const char *F_HOST = "host";
    const char *F_MIRROR = "mirror";
    const char *F_RESULTS = "results";

    const int MAX_RETRIES = 5;
    const int TIMEOUT = 20000; //20s
    //TVector<TString> HttpHeaders;
    //HttpHeaders.push_back("Content-Type: application/json");
    //HttpHeaders.push_back(TString("Authorization: OAuth ") + Token);
    const TString data = JoinSeq(";", hosts);

    for (int retry = 0; retry < MAX_RETRIES; retry++) {
        try {
            NWebmaster::TPostRequest<TString> post(handler);
            post.SetTimeout(TIMEOUT);
            //post.SetHeaders(HttpHeaders);
            post.SetData(data);
            TAtomicSharedPtr<TString> res = post.Perform();

            NJson::TJsonValue root;
            TStringStream json(*res);
            NJson::ReadJsonTree(&json, &root);

            for (const auto &result : root[F_RESULTS].GetArray()) {
                const auto &obj = result.GetMap();
                if (obj.contains(F_ERROR)) {
                    const TString &error = obj.at(F_ERROR).GetString();
                    if (error == "BAD_URL" || error == "URL_NOT_FOUND" || error == "TIMEOUT_EXCEEDED") {
                        //...
                    } else {
                        ythrow yexception() << "response contains bad answer " << error;
                    }
                }

                response[obj.at(F_HOST).GetString()] = obj.at(F_MIRROR).GetString();
            }

            return;
        } catch (yexception &e) {
            LOG_WARN("source beautyurl regions, %s, retry %d", e.what(), retry);
        } catch (...) {
            LOG_WARN("source beautyurl regions, unknown error, retry %d", retry);
        }
        Sleep(TDuration::Seconds(5));
    }

    ythrow yexception() << "source beautyurl regions, unable to complete http request after " << MAX_RETRIES << " retries";
}

void UploadBeautyUrls(NYT::IClientBasePtr clientSearch, const THashSet<TString> &webmasterHosts) {
    const auto &config = TConfig::CInstance();

    TSet<TString> sortedWebmasterHosts(webmasterHosts.begin(), webmasterHosts.end());

    const TString beautyUrlSourceTable = NYTUtils::JoinPath(config.TABLE_DIGEST_SOURCE_BEAUTYURL, NUtils::Date2StrTZ(Now().TimeT()));
    NYT::ITransactionPtr tx = clientSearch->StartTransaction();

    tx->Create(beautyUrlSourceTable,
        NYT::NT_TABLE,
        NYT::TCreateOptions()
            .Force(true)
            .Recursive(true)
    );

    NYT::TTableSchema beautyUrlSourceSchema;
    beautyUrlSourceSchema.Strict(true);
    beautyUrlSourceSchema.AddColumn(NYT::TColumnSchema().Name(F_HOST).Type(NYT::VT_STRING).SortOrder(NYT::SO_ASCENDING));
    beautyUrlSourceSchema.AddColumn(NYT::TColumnSchema().Name(F_BEAUTY_URL).Type(NYT::VT_STRING));

    LOG_INFO("source beauty url, uploading %lu hosts to %s", sortedWebmasterHosts.size(), beautyUrlSourceTable.data());

    auto writer = tx->CreateTableWriter<NYT::TNode>(NYT::TRichYPath(beautyUrlSourceTable).Schema(beautyUrlSourceSchema));
    size_t counter = 0;
    for (auto it = sortedWebmasterHosts.begin(); it != sortedWebmasterHosts.end();) {
        TDeque<TString> hostsToRequest;
        TMap<TString, TString> response;
        for (size_t i = 0; it != sortedWebmasterHosts.end() && i < 500; ++i, ++it) {
            hostsToRequest.push_back(*it);
            if ((counter % 100000) == 0) {
                LOG_INFO("source beauty url, uploaded %lu hosts", counter);
            }
            counter++;
        }

        GetBeautyUrls(config.BEAUTYURL_HANDLER, hostsToRequest, response);
        for (const auto &obj : response)  {
            writer->AddRow(NYT::TNode()
                (F_HOST, obj.first)
                (F_BEAUTY_URL, obj.second)
            );
        }
        //break;
    }
    writer->Finish();
    tx->Commit();
}

} //namespace NWebmaster
