#include <util/generic/set.h>
#include <util/string/builder.h>
#include <util/string/join.h>
#include <util/string/vector.h>
#include <util/stream/file.h>

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

#include <wmconsole/version3/wmcutil/log.h>

#include "monitor.h"
#include "service.h"

namespace NWebmaster {

TPredictorService::TPredictorService(const TConfig &config)
    : Config(config)
    , Monitor(TMonitor::Instance())
{
    AddAction("/predictSpamHost", this, &TPredictorService::MethodPredictSpamHostRequest);
    AddAction("/getHostnameSpamness", this, &TPredictorService::MethodPredictSpamHostRequest);
    AddAction("/ping", this, &TPredictorService::MethodPing);
}

void *TPredictorService::CreateThreadSpecificResource() {
    return static_cast<void*>(new TClfPredictor);
}

void TPredictorService::DestroyThreadSpecificResource(void *tsr) {
    TClfPredictor *clf = static_cast<TClfPredictor*>(tsr);
    delete clf;
}

bool TPredictorService::MethodPing(THttpServer::TRequest &request) {
    request.Output() << "HTTP/1.1 200 Ok\r\n\r\n" << "<source>webmaster-predictor-daemon</source>";
    return true;
}

bool TPredictorService::MethodPredictSpamHostRequest(THttpServer::TRequest &request) try {
    TClfPredictor *clf = static_cast<TClfPredictor*>(request.ThreadSpecificResource);

    LOG_INFO("requested %s - [%s]", request.Method.data(), request.GetRemoteAddr().data());

    const TString content = request.Input().ReadAll();
    const TVector<TString> hostnames = SplitString(content, ";");
    const TVector<double> probabilites = clf->Predict(hostnames);

    TString output;
    TStringOutput jsonStream(output);
    NJson::TJsonWriter writer(&jsonStream, false, false);

    TDeque<TString> predictionsLog;
    writer.OpenMap();
    writer.Write("results");
    writer.OpenMap();
    for (size_t i = 0; i < hostnames.size(); i++) {
        double probability = probabilites[i];
        int cls = clf->GetClass(probability);
        writer.Write(hostnames[i]);
        writer.OpenMap();
        writer.Write("p", probability);
        writer.Write("c", cls);
        writer.CloseMap();
        predictionsLog.push_back(TStringBuilder() << hostnames[i] << "=" << cls << "@" << probability);
        if (cls) {
            Monitor.PredictedNormalHosts();
        } else {
            Monitor.PredictedSpamHosts();
        }
    }
    writer.CloseMap();
    writer.CloseMap();
    writer.Flush();

    try {
        request.Output() << "HTTP/1.1 200 Ok\r\n\r\n" << output;
        LOG_INFO("sent prediction reply with %lu hosts (%s) in %s", hostnames.size(), JoinSeq(";", predictionsLog).data(), request.GetTimerString().data());
    } catch(const yexception &e) {
        LOG_ERROR("unable to complete prediction answer answer: %s", e.what());
        request.Die(500, e.what());
    }

    return true;
} catch (yexception &e) {
    LOG_ERROR("unable to process request: %s", e.what());
    return true;
}

} //namespace NWebmaster
