//
// Created by Alexander D. Kravchenko on 2/16/22.
//

#include <google/protobuf/util/time_util.h>
#include <library/cpp/protobuf/yql/descriptor.h>

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

#include <robot/library/yt/static/command.h>

#include <util/digest/city.h>
#include <util/generic/size_literals.h>

#include <wmconsole/version3/library/jupiter/jupiter.h>
#include <wmconsole/version3/wmcutil/compress.h>
#include <wmconsole/version3/wmcutil/log.h>

#include <wmconsole/version3/protos/feeds.pb.h>
#include <market/idx/datacamp/proto/offer/OfferMeta.pb.h>

#include "process_offer_bans.h"
#include "config.h"


using namespace google::protobuf::util;

namespace NWebmaster {

using namespace proto::feeds;

namespace NFeeds {

using namespace NJupiter;

static const char *F_DOMAIN_FIELD = "domain";
static const char *F_FEED_URL_FIELD = "feed_url";
static const char *F_PARTNER_ID_FIELD = "partner_id";



struct TOfferBansJoinMapper : public NYT::IMapper<NYT::TTableReader<NYT::TNode>, NYT::TTableWriter<OfferBansRow>> {
public:
    void Do(TReader *input, TWriter *output) override {

        for (const auto &cursor: *input) {
            const NYT::TNode &row = cursor.GetRow();
            OfferBansRow out;
            TString type = row.HasKey("feed_type") ? row["feed_type"].AsString() : "goods";
            TString aboReason = row.HasKey("abo_reason") ? row["abo_reason"].AsString() : "MANUALLY_HIDDEN";
            out.Setdomain(row["domain"].AsString());
            out.Setfeed_url(row["feed_url"].AsString());
            out.Setoffer_id(row["offer_id"].AsString());
            out.Settimestamp(row["ts"].AsInt64());

            out.Setreason(aboReason);
            if (row["comment"].IsString()) {
                out.Setcomment(row["comment"].AsString());
            }
            out.Settype(type);
            output->AddRow(out);
        }
    }
};
REGISTER_MAPPER(TOfferBansJoinMapper)


struct TDatacampOfferBansMapper : public NYT::IMapper<NYT::TTableReader<ServiceOffersTableInputRow>,
        NYT::TTableWriter<ServiceOffersTableOutputRow>> {
    Y_SAVELOAD_JOB(VerticalShareHosts)
public:
    TDatacampOfferBansMapper() = default;

    TDatacampOfferBansMapper(const THashMap<i64, std::pair<TString, TString>> &verticalShareHosts) : VerticalShareHosts(verticalShareHosts) {
    }

    void Do(TReader *input, TWriter *output) override {
        for (const auto &cursor: *input) {
            const ServiceOffersTableInputRow &row = cursor.GetRow();
            i64 partnerId = row.shop_id();
            if (!VerticalShareHosts.contains(partnerId)) {
                continue;
            }
            TString domain;
            TString feedUrl;

            std::tie(domain, feedUrl) = VerticalShareHosts[partnerId];
            ServiceOffersTableOutputRow out;
            out.Setbusiness_id(row.business_id());
            out.Setshop_id(row.shop_id());
            out.Setoffer_id(row.shop_sku());
            out.Setdomain(domain);
            out.Setfeed_url(feedUrl);
            *out.mutable_resolution() = row.resolution();

            for (const auto &sourceCursor: row.resolution().by_source()) {
                Market::DataCamp::DataSource source = sourceCursor.meta().source();
                if (source != Market::DataCamp::MARKET_ABO_SHOP_SKU) {
                    continue;
                }
                out.Settimestamp(TimeUtil::TimestampToMilliseconds(sourceCursor.meta().timestamp()));
                for (const auto &verdictCursor: sourceCursor.verdict()) {
                    for (const auto &resultsCursor: verdictCursor.results()) {
                        if (!resultsCursor.is_banned()) {
                            continue;
                        }
                        for (const auto &messagesCursor: resultsCursor.messages()) {
                            out.Setabo_reason(messagesCursor.code());
                            for (const auto &paramsCursor: messagesCursor.params()) {
                                if (paramsCursor.name() == "public_comment") {
                                    out.Setcomment(paramsCursor.value());
                                }

                            }
                        }
                        output->AddRow(out);
                    }
                }
            }
        }
    }

private:
    THashMap<i64, std::pair<TString, TString>> VerticalShareHosts;
};

REGISTER_MAPPER(TDatacampOfferBansMapper)

void LoadWmPartners(NYT::IClientBasePtr client, const TString &tableName, THashMap<i64, std::pair<TString, TString>> &verticalSharePartners) {
    auto reader = client->CreateTableReader<NYT::TNode>(tableName);
    for (const auto &cursor: *reader) {
        const NYT::TNode &row = cursor.GetRow();
        verticalSharePartners[row[F_PARTNER_ID_FIELD].AsInt64()] = std::make_pair(row[F_DOMAIN_FIELD].AsString(), row[F_FEED_URL_FIELD].AsString());
    }
}

int ProcessOfferBans(int, const char **) {
    const TConfig &config = TConfig::CInstance();
    NYT::IClientPtr client = NYT::CreateClient(config.MR_TURBO_SERVER_HOST);
    NYT::ITransactionPtr tx = client->StartTransaction();

    THashMap<i64, std::pair<TString, TString>> verticalSharePartners(100000);

    LOG_INFO("Start load webmaster partners");
    LoadWmPartners(client, config.TABLE_SOURCE_VERTICAL_SHARE_HOSTS, verticalSharePartners);
    LOG_INFO("Finish load webmaster partners");

    const TTable<ServiceOffersTableInputRow> input =
            TTable<ServiceOffersTableInputRow>(tx, config.TABLE_DATACAMP_SERVICE_OFFERS);
    const TTable<ServiceOffersTableOutputRow> output =
            TTable<ServiceOffersTableOutputRow>(tx, config.TABLE_SOURCE_VERTICAL_SHARE_OFFER_BANS);
    const TTable<OfferBansRow> outputJoined =
            TTable<OfferBansRow>(tx, config.TABLE_SOURCE_OFFER_BANS_JOINED);


    LOG_INFO("Start processing offer bans");
    TMapCmd<TDatacampOfferBansMapper>(tx, new TDatacampOfferBansMapper(verticalSharePartners))
            .Input(input)
            .Output(output)
            .Do();
    LOG_INFO("Finish processing offer bans");

    LOG_INFO("Start join offer bans");
    TMapCmd<TOfferBansJoinMapper>(tx)
            .Input<NYT::TNode>(config.TABLE_SOURCE_VERTICAL_SHARE_OFFER_BANS)
            .Input<NYT::TNode>(config.TABLE_ABO_OFFER_BANS)
            .Output<OfferBansRow>(outputJoined)
            .Do();
    LOG_INFO("Finish join offer bans");

    NYTUtils::SetAttr(tx, output, "_yql_proto_field_resolution", GenerateProtobufTypeConfig<Market::DataCamp::Resolution>());

    LOG_INFO("Start sorting offer bans");
    TSortCmd<ServiceOffersTableOutputRow>(tx, output).By({"business_id", "shop_id", "offer_id"}).Do();
    TSortCmd<OfferBansRow>(tx, outputJoined).By({"domain", "type"}).Do();
    LOG_INFO("Finish sorting offer bans");

    tx->Commit();
    return 0;
}

}
}
