#include "export_files.h"
#include "helpers.h"

#include <maps/libs/log8/include/log8.h>
#include <maps/libs/common/include/exception.h>
#include <maps/libs/common/include/file_utils.h>

#include <type_traits>

namespace fs = std::filesystem;

namespace maps::wiki::exporter {

const std::string WORK_DIR_PREFIX = "export-";

const std::string GZ_SUFFIX = ".gz";

namespace {

const std::string TAR = ".tar";
const std::string GZ = ".gz";

const std::string JSON2YMAPSDF_LOG = "json2ymapsdf_errors.txt";
const std::string PRINTED_CONFIG = "printed-config.json";
const std::string OBJECT_IDS = "oids.txt";
const std::string JSON_LIST = "json-list.fifo";

std::string makeWorkDirPrefix(Subset subset) {
    return WORK_DIR_PREFIX + std::string(toString(subset));
}

std::string makeWorkDirSuffix(const TaskParams& taskParams) {
    return (
        std::to_string(taskParams.taskId) + "-" +
        taskParams.branch + "-" +
        std::to_string(taskParams.commitId)
    );
}

} // namespace

ExportFiles::ExportFiles(
    const std::string& rootDir,
    const ExportConfig& exportCfg
)
    : subset_(exportCfg.subset())
    , workDir_(maps::common::createUniqueDir(
        rootDir,
        /* prefix = */ makeWorkDirPrefix(exportCfg.subset()),
        /* suffix = */ makeWorkDirSuffix(exportCfg.taskParams())
    ))
    , resultDir_(workDir_ / "result")
    , tempDir_(workDir_)
    , dumpDir_(tempDir_ / "dump")
    , jsonDir_(tempDir_ / "json")
    , serviceShapeDir_(tempDir_ / "service-shape")
    , serviceGeojsonDir_(tempDir_ / "service-geojson")
{
    fs::create_directory(serviceShapeDir_);
    fs::create_directory(serviceGeojsonDir_);
    fs::create_directory(jsonDir_);
    fs::create_directory(dumpDir_);

    fs::create_directory(resultDir_);
    for (const auto& region: exportCfg.regions()) {
        fs::create_directory(resultDirectoryFor(region));
    }
    fs::create_directory(resultDirectoryFor(NO_REGION));

    INFO() << *this;
}

void ExportFiles::cleanupOldDirs(const std::string& rootDir, Subset subset) {
    constexpr size_t ITERATIONS_TO_KEEP = 4;
    exporter::cleanupOldDirs(rootDir, makeWorkDirPrefix(subset), ITERATIONS_TO_KEEP);
}

std::string ExportFiles::operator()(TmpFile file) const
{
    switch (file) {
        case TmpFile::DUMP_DIR:
            return dumpDir_.string();
        case TmpFile::JSON2YMAPSDF_LOG:
            return (tempDir_ / JSON2YMAPSDF_LOG).string();
        case TmpFile::JSON_DIR:
            return jsonDir_.string();
        case TmpFile::JSON_LIST_FILE:
            return (tempDir_ / JSON_LIST).string();
        case TmpFile::OBJECT_IDS_DUMP_FILE:
            return (tempDir_ / OBJECT_IDS).string();
        case TmpFile::PRINTED_CFG:
            return (tempDir_ / PRINTED_CONFIG).string();
        case TmpFile::SERVICE_SHAPE_DIR:
            return serviceShapeDir_.string();
        case TmpFile::SERVICE_GEOJSON_DIR:
            return serviceGeojsonDir_.string();
        case TmpFile::SERVICE_JSON_FILE:
            return (tempDir_ / "service-json.json").string();
    }
}

std::string ExportFiles::operator()(ResultFile file, const Region& region) const
{
    const auto& regionResultDir = resultDirectoryFor(region);
    std::string dumpBaseName = ((subset_ == Subset::Ymapsdf) ? "ymapsdf2.dump" : "export_mtr");
    switch (file) {
        case ResultFile::DUMP_TAR_GZ:
            return (regionResultDir / (dumpBaseName + TAR + GZ)).string();
        case ResultFile::DUMP_GZ_TAR:
            return (regionResultDir / (dumpBaseName + GZ + TAR)).string();
        case ResultFile::JSON2YMAPSDF_LOG:
            return (regionResultDir / JSON2YMAPSDF_LOG).string();
        case ResultFile::OBJECT_IDS_ARCHIVE:
            return (regionResultDir / (OBJECT_IDS + GZ)).string();

        case ResultFile::SERVICE_SHAPE_ARCHIVE:
            return (resultDir_ / ("shape" + TAR + GZ)).string();
        case ResultFile::SERVICE_GEOJSON_ARCHIVE:
            return (resultDir_ / ("geojson" + TAR + GZ)).string();
    }
}

std::string ExportFiles::ymapsdfSchemasArchive() const {
    return (resultDir_ / ("ymapsdf2.schema" + TAR)).string();
}

std::string ExportFiles::jsonBackupFile() const {
    return (resultDir_ / ("json-backup" + TAR + GZ)).string();
}

std::ostream& operator<<(std::ostream& out, const ExportFiles& exportFiles)
{
    out <<
        "ExportFiles:\n" <<
        "    workDir: " << exportFiles.workDir_ << "\n" <<
        "    tempDir: " << exportFiles.tempDir_.path() << "\n" <<
        "    resultDir: " << exportFiles.resultDir_ << "\n"
    ;
    return out;
}

std::filesystem::path ExportFiles::resultDirectoryFor(const Region& region) const {
    if (region == NO_REGION) {
        return resultDir_;
    } else {
        return resultDir_ / region;
    }
}

std::vector<std::string> resultFiles(
    const ExportFiles& exportFiles,
    Subset subset,
    Region region)
{
    switch(subset) {
        case Subset::Service:
            return {
                exportFiles(ResultFile::SERVICE_SHAPE_ARCHIVE, region),
            };
            break;
        case Subset::Mrc:
        case Subset::MrcPedestrian:
            return {
                exportFiles(ResultFile::SERVICE_SHAPE_ARCHIVE, region),
                exportFiles(ResultFile::SERVICE_GEOJSON_ARCHIVE, region),
            };
            break;
        case Subset::Ymapsdf:
            return {
                exportFiles(ResultFile::DUMP_GZ_TAR, region),
                exportFiles(ResultFile::OBJECT_IDS_ARCHIVE, region),
                exportFiles(ResultFile::JSON2YMAPSDF_LOG, region),
                exportFiles.ymapsdfSchemasArchive(),
            };
            break;
        case Subset::Masstransit:
            return {
                exportFiles(ResultFile::DUMP_TAR_GZ, region),
            };
            break;
        case Subset::Poi:
            throw LogicError() << "You should not be here!";
    }
    throw LogicError() << "Invalid export subset value: " << static_cast<int>(subset);
}

} // namespace maps::wiki::exporter
