#include <saas/api/search_client/client.h>
#include <library/cpp/logger/global/global.h>
#include <mapreduce/yt/interface/client.h>
#include <mapreduce/yt/interface/node.h>
#include <mapreduce/yt/interface/io-inl.h>
#include <library/cpp/getopt/last_getopt.h>
#include <library/cpp/http/misc/httpcodes.h>
#include <library/cpp/json/json_reader.h>

#include <util/thread/pool.h>
#include <util/generic/map.h>
#include <library/cpp/string_utils/url/url.h>
#include <library/cpp/cgiparam/cgiparam.h>
#include <library/cpp/string_utils/quote/quote.h>


struct TContext {
    TString OutputTable;
    TString SearchHost;
    TString LogFile;
    TString SearchService;
    ui16 SearchPort;
    ui32 Threads = 16;
    ui32 RecordsCount = 1;
};

TSet<TString> RequestAttrs;

class TSearchThread : public IObjectInQueue {
public:
    TSearchThread(const TString& req, NSaas::TSearchClient& client, NYT::TTableWriterPtr<NYT::TNode> ytClient, ui32 qId)
        : Request(req)
        , Client(client)
        , YTWriter(ytClient)
        , QId(qId)
    {}

    void Process(void*) override {
        NSaas::TQuery cacheQuery;
        TStringBuf query = GetPathAndQuery(Request);
        TString queryStr(query.data(), query.size());
        cacheQuery.SetExtraParams("&ms=proto&meta_search=first_found&sp_meta_search=proxy");

        TCgiParameters cgi;
        cgi.Scan(query);
        cacheQuery.SetText(CGIEscapeRet(cgi.Get("text")));
        INFO_LOG << Client.GetFullUrl(cacheQuery, TDuration::MilliSeconds(2000)) << Endl;

        NSaas::TSearchReply reply = Client.SendAsyncQuery(cacheQuery, TDuration::MilliSeconds(2000));

        if (reply.GetCode() == HTTP_OK) {
            auto func = [this, &queryStr](const NMetaProtocol::TDocument& doc) {
                NYT::TNode node;
                node("query", queryStr);
                node("query_id", QId);

                auto& archiveInfo = doc.GetArchiveInfo();
                TMap<TString, TString> attrs;

                for (ui32 i = 0; i < archiveInfo.GtaRelatedAttributeSize(); ++i) {
                    auto& attr = archiveInfo.GetGtaRelatedAttribute(i);
                    if (RequestAttrs.contains(attr.GetKey())) {
                        attrs[attr.GetKey()] = attr.GetValue();
                        node(attr.GetKey(), attr.GetValue());
                    }
                }
                YTWriter->AddRow(node);
            };
            reply.ScanDocs(func);
        } else {
            ERROR_LOG << "No data for " << reply.GetReport().Utf8DebugString() << Endl;
        }
    }

private:
    TString Request;
    NSaas::TSearchClient& Client;
    NYT::TTableWriterPtr<NYT::TNode> YTWriter;
    ui32 QId;
};

class TSaasSearcher {
public:
    TSaasSearcher(const TContext& context)
        : SearchClient(context.SearchService, context.SearchHost, context.SearchPort)
        , Context(context)
    {
        Queue.Start(context.Threads);
        YTClient = NYT::CreateClient("freud");
        YTWriter = YTClient->CreateTableWriter<NYT::TNode>(Context.OutputTable);
    }

    void Run() {
        TFileInput input(Context.LogFile);
        TString line;

        ui32 counter = 0;
        while(input.ReadLine(line)) {
            Queue.SafeAddAndOwn(THolder(new TSearchThread(line, SearchClient, YTWriter, counter)));
            ++counter;
            if (counter >= Context.RecordsCount) {
                break;
            }
        }
    }

private:
    NSaas::TSearchClient SearchClient;
    TContext Context;
    TThreadPool Queue;
    NYT::IClientPtr YTClient;
    NYT::TTableWriterPtr<NYT::TNode> YTWriter;
};

int main(int argc, const char** argv) {
    DoInitGlobalLog("console", 7, false, false);
    NYT::Initialize(argc, argv);

    RequestAttrs.insert("path");
    RequestAttrs.insert("coord");
    RequestAttrs.insert("walk_time");
    RequestAttrs.insert("walk_distance");
    RequestAttrs.insert("cars_info");
    RequestAttrs.insert("dests_info");

    NLastGetopt::TOpts opts;
    opts.AddHelpOption();

    TContext context;

    opts.AddCharOption('h', "host").StoreResult(&context.SearchHost).DefaultValue("saas-searchproxy-maps-prestable.yandex.net");
    opts.AddCharOption('p', "port").StoreResult(&context.SearchPort).DefaultValue("17000");
    opts.AddCharOption('s', "service").StoreResult(&context.SearchService).Required();
    opts.AddCharOption('i', "filename").StoreResult(&context.LogFile).Required();
//    opts.AddCharOption('l', "log level").StoreResult(&ctx.LogLevel).DefaultValue("6");
    opts.AddCharOption('t', "threads").StoreResult(&context.Threads).DefaultValue("16");
    opts.AddCharOption('c', "rec-count").StoreResult(&context.RecordsCount).DefaultValue("1000");
    opts.AddCharOption('o', "output-tabe").StoreResult(&context.OutputTable).DefaultValue("//home/extdata/nsofya/cars_selection");

    NLastGetopt::TOptsParseResult parseOpt(&opts, argc, argv);

    TSaasSearcher(context).Run();

    return 0;
}
