#include <util/thread/pool.h>

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

#include <wmconsole/version3/wmcutil/args.h>
#include <wmconsole/version3/wmcutil/log.h>
#include <wmconsole/version3/wmcutil/yt/transfer_manager.h>
#include <wmconsole/version3/processors/user_sessions/library/common_parser_opts.h>
#include <wmconsole/version3/processors/user_sessions/library/source_tables.h>
#include <wmconsole/version3/processors/user_sessions/conf/config.h>
#include <wmconsole/version3/processors/user_sessions/protos/user_sessions.pb.h>

#include "monitoring.h"
#include "task_sync.h"

namespace NWebmaster {
namespace NUserSessions {

using namespace NJupiter;

void SyncQueries(const TString &fromCluster, const TString &toCluster, const TString &root, size_t days = 100, TSourceTable::ETableFormat format = TSourceTable::E_FMT_CONVERTED_V4) try {
    NYT::IClientPtr srcClient = NYT::CreateClient(fromCluster);
    NYT::IClientPtr dstClient = NYT::CreateClient(toCluster);

    TDeque<TSourceTable> sourceTables;
    LoadSourceTables(srcClient, root, sourceTables, days, format);

    int lag = 0, holes = 0;
    if (!IsSourceTablesSetComplete(sourceTables, lag, holes)) {
        ythrow yexception() << "source tables set is incomplete: " << root;
    }

    std::reverse(sourceTables.begin(), sourceTables.end());
    if (sourceTables.size() > days) {
        sourceTables.resize(days);
    }
    TSet<TSourceTable> srcTablesSet(sourceTables.begin(), sourceTables.end());

    TDeque<TSourceTable> destTables;
    LoadSourceTables(dstClient, root, destTables, days, format);

    THolder<IThreadPool> processQueue(CreateThreadPool(8));
    for (const TSourceTable &table : sourceTables) {
        if (dstClient->Exists(table.Name)) {
            continue;
        }

        processQueue->SafeAddFunc([=]() {
            try {
                LOG_INFO("sync table %s", table.Name.data());
                TTransferManager(TConfigBase::GetYTToken()).PostTaskAndWait(
                    fromCluster, table.Name,
                    toCluster, table.Name
                );
                LOG_INFO("sync table %s - done", table.Name.data());
            } catch (yexception &e) {
                LOG_ERROR("sync table %s error: %s", table.Name.data(), e.what());
            }
        });
    }
    processQueue->Stop();

    for (const TSourceTable &table : destTables) {
        if (!srcTablesSet.contains(table)) {
            dstClient->Remove(table.Name);
            LOG_INFO("drop table %s", table.Name.data());
        }
    }
} catch (yexception &e) {
    LOG_WARN("unable to sync tables: %s", e.what());
}

void SortQueriesDev() try {
    NYT::IClientPtr client = NYT::CreateClient(TCommonYTConfigSQ::CInstance().MR_SERVER_HOST_USER_SESSIONS);

    TDeque<TSourceTable> sourceTables;
    LoadSourceTables(client, TCommonYTConfigSQ::CInstance().TABLE_PARSED_USER_SESSIONS_DAILY_ROOT, sourceTables, 100, TSourceTable::E_FMT_USER_SESSIONS);

    THolder<IThreadPool> processQueue(CreateThreadPool(4));
    for (const TSourceTable &table : sourceTables) {
        processQueue->SafeAddFunc([=]() {
            try {
                LOG_INFO("sort table %s", table.Name.data());
                TSortCmd<NProto::TQuery>(client)
                    .OperationWeight(TConfig::CInstance().OPERATION_WEIGHT)
                    .Input(TTable<NProto::TQuery>(client, table.Name))
                    .Output(TTable<NProto::TQuery>(client, table.Name)
                        .SetCompressionCodec(ECompressionCodec::BROTLI_6)
                        .SetErasureCodec(EErasureCodec::LRC_12_2_2)
                    )
                    .By({"Host", "CorrectedQuery", "Path", "RegionId", "IsMobile", "IsPad", "Position", "RequestSource", "ResultSource"})
                    .Do()
                ;
                LOG_INFO("sort table %s - done", table.Name.data());
            } catch (yexception &e) {
                LOG_ERROR("sort table %s error: %s", table.Name.data(), e.what());
            }
        });
    }
    processQueue->Stop();

} catch (yexception &e) {
    LOG_WARN("unable to sort tables: %s", e.what());
}

int TaskSync(int argc, const char **argv) {
    auto commonParserOpts = ParseCommonOptions(argc, argv);
    InitCommonSingletones(commonParserOpts);

    SyncQueries(
        TCommonYTConfigSQ::CInstance().MR_SERVER_HOST_USER_SESSIONS,
        TConfig::CInstance().MR_SERVER_HOST_QUERIES,
        TCommonYTConfigSQ::CInstance().TABLE_PARSED_USER_SESSIONS_DAILY_ROOT,
        105,
        TSourceTable::E_FMT_USER_SESSIONS
    );

    SyncQueries(
        TCommonYTConfigSQ::CInstance().MR_SERVER_HOST_USER_SESSIONS,
        TConfig::CInstance().MR_SERVER_HOST_QUERIES,
        TCommonYTConfigSQ::CInstance().TABLE_CONVERTED_QUERIES_V4_ROOT,
        420,
        TSourceTable::E_FMT_CONVERTED_V4
    );

    return 0;
}

} //namespace NUserSessions
} //namespace NWebmaster
