#include "yt_reader.h"

#include <yandex/maps/wiki/common/yt.h>
#include <maps/libs/chrono/include/time_point.h>

#include <chrono>
#include <iostream>
#include <map>
#include <string>

namespace maps::wiki::easyview_walkers_photos_tool {

namespace {

const TString YT_REALM = "hahn";
const std::string YT_YANG_RAW_DESKTOP = "//home/sprav/assay/tasker/walkers/v3/yang_raw_desktop";

const auto MAX_PHOTO_AGE = std::chrono::years(1);
const size_t BULK_ROWS_TO_INFO = 10000;

const std::string TAG_ENTRANCE = "entrance";
const std::string TAG_FACADE = "facade";
const std::string TAG_MAIN = "main";

const std::string TAG_TAKING_POSTFIX = "_taking";

const std::set<std::string> RAW_TABLE_ACCEPTED_OBJECT_TAGS {
    TAG_ENTRANCE,
    TAG_FACADE,
    TAG_MAIN
};

std::string normalizeRawTableTag(std::string tag)
{
    std::transform(
        tag.begin(),
        tag.end(),
        tag.begin(),
        [](unsigned char c) { return std::tolower(c); });
    if (tag == TAG_MAIN) {
        return TAG_ENTRANCE;
    }
    return tag;
}

} // namespace

PhotosData readPhotoDataFromYt(
    size_t limit,
    bool ignoreOldPhotos)
{
    NYT::JoblessInitialize();
    auto client = common::yt::createYtClient(YT_REALM);
    auto rowCount = client->Get(TString(YT_YANG_RAW_DESKTOP + "/@row_count")).AsInt64();
    auto reader = client->CreateTableReader<NYT::TNode>(TString(YT_YANG_RAW_DESKTOP));

    std::cout << "Reading photos data from YT started" << std::endl;

    PhotosData result;
    result.reserve(rowCount);

    const auto now = maps::chrono::TimePoint::clock::now();
    size_t rowIndex = 0;

    for (; (!limit || result.size() < limit) && reader->IsValid(); reader->Next()) {
        ++rowIndex;
        if (rowIndex % BULK_ROWS_TO_INFO == 0) {
            std::cout << rowIndex << " / " << rowCount << " rows processed, "
                      << result.size() << " photos found" << std::endl;
        }

        const NYT::TNode& row = reader->GetRow();
        const auto& json = row["json"].AsMap();

        auto submitTs =
            chrono::sinceEpochToTimePoint<std::chrono::milliseconds>(
                row["submitTs"].IntCast<uint64_t>());

        if (ignoreOldPhotos && (now - submitTs > MAX_PHOTO_AGE)) {
            continue;
        }

        if (!json.contains("photos") || !json.contains("entrances")) {
            continue;
        }
        const auto& photos = json.at("photos").AsList();
        const auto& entrances = json.at("entrances").AsList();

        std::map<std::string, std::string> urlToTag;
        std::map<std::string, geolib3::Point2> tagToCoord;

        for (const auto& photo : photos) {
            const auto& jsonPhoto = photo.AsMap();
            const auto tag = normalizeRawTableTag(jsonPhoto.at("tag").AsString());
            urlToTag[jsonPhoto.at("url").AsString()] = tag;
        }
        for (const auto& entrance : entrances) {
            const auto& jsonEntrance = entrance.AsMap();
            const auto tag = normalizeRawTableTag(jsonEntrance.at("type").AsString());
            tagToCoord[tag] =
                geolib3::Point2{
                    jsonEntrance.at("lon").ConvertTo<double>(),
                    jsonEntrance.at("lat").ConvertTo<double>(),
                };
        }

        for (const auto& [url, tagObject] : urlToTag) {
            if (!RAW_TABLE_ACCEPTED_OBJECT_TAGS.contains(tagObject)) {
                continue;
            }
            std::string finalTagObject = tagObject;
            if (finalTagObject == TAG_FACADE && !tagToCoord.contains(finalTagObject)) {
                finalTagObject = TAG_ENTRANCE;
            }
            auto tagTaking = finalTagObject + TAG_TAKING_POSTFIX;

            if (tagToCoord.contains(finalTagObject) || tagToCoord.contains(tagTaking))
            {
                PhotoData photoData;

                photoData.tag = finalTagObject;
                photoData.urlPhoto = url;
                photoData.permalink = row["permalink"].IsNull()
                    ? ""
                    : std::to_string(row["permalink"].AsInt64());
                photoData.urlPermalink = photoData.permalink.empty()
                    ? ""
                    : "https://altay.yandex-team.ru/cards/perm/" + photoData.permalink;
                if (tagToCoord.contains(finalTagObject)) {
                    photoData.coordObject = tagToCoord.at(finalTagObject);
                }
                if (tagToCoord.contains(tagTaking)) {
                    photoData.coordTaking = tagToCoord.at(tagTaking);
                }
                photoData.submitTs = submitTs;

                result.emplace_back(std::move(photoData));
            }
        }
    }

    std::cout << "Reading photos data from YT finished" << std::endl;
    std::cout << "Total photo count: " << result.size() << std::endl;

    return result;
}

} // maps::wiki::easyview_walkers_photos_tool
