#include <util/string/builder.h>
#include <util/string/subst.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 "service.h"

namespace NWebmaster {

THost2VecViewerService::THost2VecViewerService(TSimpleSharedPtr<NWord2Vec::TModel> model)
    : Model(model)
{
    OwnerCanonizer.LoadTrueOwners();
    OwnerCanonizer.LoadSerpCategOwners();

    Searcher.Reset(new TBruteforceSearcher("1"));
    Searcher->SetModels(Model.Get(), Model.Get(), false /*normalized*/);

    AddAction("/analogy", this, &THost2VecViewerService::MethodAnalogyRequest);
    AddAction("/ping", this, &THost2VecViewerService::MethodPing);
}

void *THost2VecViewerService::CreateThreadSpecificResource() {
    return nullptr;
}

void THost2VecViewerService::DestroyThreadSpecificResource(void */*tsr*/) {
    //delete obj;
}

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

TString RenderPage(const TString &content) {
    TFileInput pageTemplateFile("page.html");
    TString pageTemplate = pageTemplateFile.ReadAll();
    SubstGlobal(pageTemplate, "$body", content);
    return pageTemplate;
}

TString RenderPanel(const TString &content) {
    const TString panel = "<pre>" + content + "</pre>";
    return RenderPage(panel);
}

TString RenderTable(const TString &content) {
    const TString table =
        "<table class='table table-hover'>"
        "<thead>"
        "<tr><th>Host</th><th>Analogy</th><th>Cosine</th><th>InvCosine</th></tr>"
        "</thead>"
        "<tbody>"
        + content +
        "</tbody>"
        "</table>";

    return RenderPage(table);
}

bool THost2VecViewerService::MethodAnalogyRequest(THttpServer::TRequest &request) try {
    LOG_INFO("requested %s - [%s]", request.Method.data(), request.GetRemoteAddr().data());

    TCgiParameters::const_iterator hostIt = request.Params.Find("host");
    TCgiParameters::const_iterator samplesIt = request.Params.Find("samples");

    if (hostIt == request.Params.end()) {
        request.Output() << "HTTP/1.1 200 Ok\r\n\r\n" << RenderPanel("No host param");
        return true;
    }

    const TString host = hostIt->second;
    int samples = 300;

    if (samplesIt != request.Params.end() && samplesIt->second.size() > 0) {
        samples = FromString(samplesIt->second);
    }

    TUtf16String wHost = TUtf16String::FromAscii(host);
    TString content;

    if (Model->Has(wHost)) {
        TVector<TUtf16String> words = { wHost };
        TVector<TWordQuality> results = Searcher->FindBestMatches(words, samples, false/*debug*/, 1);
        Sort(results.begin(), results.end());

        for (size_t i = 0; i < results.size(); ++i) {
            const TString analogy = WideToUTF8(results[i].Word);
            content += TStringBuilder() << "<tr><td>" << host << "</td><td>" << analogy << "</td><td>" << results[i].Quality << "</td><td>" << -results[i].Quality << "</td>" << Endl;
            LOG_INFO("Host=%s Analogy=%s Cosine=%f InvCosine=%f", host.data(), analogy.data(), results[i].Quality, -results[i].Quality);
        }
    } else {
        LOG_INFO("Host=%s Analogy=%s Cosine=%f InvCosine=%f", host.data(), host.data(), 1.0, -1.0);
        content += TStringBuilder() << "<tr><td>" << host << "</td><td>" << host << "</td><td>" << 1.0 << "</td><td>" << -1.0 << "</td>" << Endl;
    }

    request.Output() << "HTTP/1.1 200 Ok\r\n\r\n" << RenderTable(content);
    LOG_INFO("requested %s - [%s] processed in %s", request.Method.data(), request.GetRemoteAddr().data(), request.GetTimerString().data());

    return true;
} catch (yexception &e) {
    LOG_ERROR("unable to complete answer: %s", e.what());
    request.Output() << "HTTP/1.1 200 Ok\r\n\r\n" << RenderPanel(e.what());
    return true;
}

} //namespace NWebmaster
