#include <library/cpp/json/json_reader.h>
#include <wmconsole/version3/wmcutil/http_client.h>
#include <wmconsole/legacy/util/logger.h>

#include "host_processor.h"
#include "mirrors_resolver.h"

extern Poco::Logger &logger();

namespace NWebmaster {

TString TMirrorResolver::ToRobotScheme(TString host) {
    if (host.Contains("http://")) {
        host = host.substr(7);
    }

    host.to_lower();
    return host;
}

TMirrorResolver::TMirrorResolver(std::deque<std::string> &hosts) {
#if 0
    std::deque<std::string> hostsWithAlternations(hosts);

    for (const std::string host : hosts) {
        const std::string alt = THostProcessor::alterWWW(host);
        hostsWithAlternations.push_back(alt);
    }
#endif

    log_info << "resolving " << hosts.size() << " mirrors";
    ResolveMirrors(hosts);
    log_info << "resolved " << ResolvedMirrors.size() << " mirrors";
}

void TMirrorResolver::ResolveMirrors(std::deque<std::string> &hosts) {
    const TString geminiProxy = "http://webmaster-canonizer.n.yandex.ru/getMirror?json=true";
    const size_t BATCH_SIZE = 10000;

    size_t offset = 0;
    size_t attemptsLeft = NWebmaster::MIRROR_REQUEST_ATTEMPTS;
    log_info << "Got " << hosts.size() << " hosts";
    do {
        if (offset % 100000 == 0) {
            log_info << "Processed " << offset << " hosts";
        }

        size_t end = std::min(offset + BATCH_SIZE, hosts.size());
        bool error = false;
        try {
            const auto hostsStr = JoinSeq(hosts.begin() + offset, hosts.begin() + end, ";");
            const auto answer = HttpPost<TString>(geminiProxy, hostsStr.c_str(), "application/octet-stream", 5000);

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

            const NJson::TJsonValue::TArray &results = root["results"].GetArray();
            for (size_t i = 0; i < results.size(); i++) {
                const auto host = ToRobotScheme(results[i]["host"].GetStringSafe());
                if (results[i].Has("error")) {
                    log_error << "get mirror: " << host << " " << results[i]["error"].GetStringSafe();
                    continue;
                }

                const auto mirror = ToRobotScheme(results[i]["mirror"].GetStringSafe());
                ResolvedMirrors[host] = mirror;
            }
        } catch (yexception &e) {
            log_error << "yexception in mirrors resolver" << e.what();
            error = true;
        } catch (std::exception &e) {
            log_error << "std::exception in mirrors resolver" << e.what();
            error = true;
        }

        if (error) {
            if (attemptsLeft > 0) {
                log_info << "Retrying this batch, attempt " << attemptsLeft;
                attemptsLeft--;
                continue;
            } else {
                log_error << "All attempts failed, skipping mirrors batch";
            }
        }

        if (attemptsLeft != MIRROR_REQUEST_ATTEMPTS) {
            log_info << "Done retrying batch";
        }

        attemptsLeft = MIRROR_REQUEST_ATTEMPTS;
        offset += BATCH_SIZE;
    } while (offset < hosts.size());
}

std::string TMirrorResolver::GetMirror(const std::string &host) {
    auto it1 = ResolvedMirrors.find(host);
    if (it1 == ResolvedMirrors.end()) {
        cacheMissesCount++;
        if (cacheMissesCount % 10000 == 0) {
            log_info << "Cache misses: " << cacheMissesCount;
        }
        std::deque<std::string> request = { host };
        ResolveMirrors(request);
        auto it2 = ResolvedMirrors.find(host);
        if (it2 == ResolvedMirrors.end()) {
            return host;
        } else {
            return it2->second;
        }
    } else {
        return it1->second;
    }
}

} //namespace NWebmaster
