#include "json2ymapsdf.h"

#include "export_helpers.h"
#include <maps/libs/enum_io/include/enum_io.h>

#include <maps/libs/log8/include/log8.h>
#include <yandex/maps/shell_cmd.h>
#include <yandex/maps/shellcmd/logging_ostream.h>
#include <maps/libs/stringutils/include/join.h>

#include <boost/regex.hpp>

namespace maps::wiki::exporter {

namespace {

const boost::regex MSG_PATTERN("\\[\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}\\] (<[^ <>]+ \\d+>) (\\w+): (.*)");

void logLoggedMessage(const std::string& msg)
{
    try {
        boost::smatch matchResult;
        boost::regex_match(msg, matchResult, MSG_PATTERN);
        const auto proc = matchResult[1];
        const auto logLevel = enum_io::fromString<log8::Level>(matchResult[2].str());
        const auto trimmedMsg = matchResult[3];

        MAPS_LOG(logLevel) << proc << " " << trimmedMsg;
    } catch(...) {
        INFO() << msg;
    }
}


enum class Json2YMapsDfMode { Upload, PrintConfig };


void json2ymapsdf(const ExportConfig& exportCfg, const ExportFiles& exportFiles, Json2YMapsDfMode mode)
{
    INFO() << "load json from " << exportFiles(TmpFile::JSON_DIR);

    std::ostringstream loadCmd;
    loadCmd
        << exportCfg.json2ymapsdfPath()
        << " --conn=\"" << exportCfg.ymapsdfConnStr() << "\""
        << " --schema=" << exportCfg.schema(NO_REGION)
        << " --tmp-dir=" << exportFiles(TmpFile::JSON_DIR)
        << " --json-list=" << exportFiles(TmpFile::JSON_LIST_FILE)
        << " --transform-cfg=" << exportCfg.json2ymapsdfTransformXml()
        << " --data-error-log=" << exportFiles(TmpFile::JSON2YMAPSDF_LOG)
        << " --log-level=info"
        << " --print-progress"
        << (exportCfg.keepJson()
            ? ""
            : " --delete-json")
        << (exportCfg.taskParams().experiment.empty()
            ? ""
            : " --experiment=" + exportCfg.taskParams().experiment)
        << (mode == Json2YMapsDfMode::PrintConfig
            ? " --print-configuration=" + exportFiles(TmpFile::PRINTED_CFG)
            : "")
        << (exportCfg.subset() != Subset::Ymapsdf
            ? ""
            : " --regions " + stringutils::join(exportCfg.regions(), " "));

    shell::stream::LoggingOutputStream loggedOut(
        [&](const std::string& s){
            exportCfg.logger().logInfo() << s;
        });
    shell::stream::LoggingOutputStream loggedErr(
        [](const std::string& s){
            logLoggedMessage(s);
        });

    shell::ShellCmd cmd(loadCmd.str(), loggedOut, loggedErr);
    auto exitCode = cmd.run();

    if (exitCode) {
        INFO() << "json2ymapsdf failed: " << loadCmd.str();
        const fs::path logPath{exportFiles(TmpFile::JSON2YMAPSDF_LOG)};
        if (!fs::exists(logPath) || !fs::file_size(logPath)) {
            throw RuntimeError() << "json2ymapsdf failed";
        }

        WARN() << "json2ymapsdf failed. Publish errors log to MDS";
        auto dataset = publishMdsErrorDataset(exportCfg, {logPath.string()});

        throw RuntimeError() << "json2ymapsdf failed, log files: " << files(dataset);
    }
}

} // namespace


void json2ymapsdf(const ExportConfig& exportCfg, const ExportFiles& exportFiles)
{
    json2ymapsdf(exportCfg, exportFiles, Json2YMapsDfMode::Upload);
}


void json2ymapsdfLoadConfig(const ExportConfig& exportCfg, const ExportFiles& exportFiles)
{
    json2ymapsdf(exportCfg, exportFiles, Json2YMapsDfMode::PrintConfig);
}

} // namespace maps::wiki::exporter
