#include "indoor_candidates.h"

#include "altay_reader.h"
#include "config.h"
#include "feedback.h"
#include "indoor_level.h"

#include <maps/libs/log8/include/log8.h>
#include <yandex/maps/wiki/common/string_utils.h>

namespace maps::wiki::merge_poi {
namespace {
const auto MERGE_POI_INDOOR_CANDIDATES_PERMALINKS = "sprav.merge_poi_indoor_candidates_permalinks"s;

std::set<poi_feed::PermalinkId>
loadCreatedTasksPermalinks(pgpool3::Pool& pool)
{
    auto txn = pool.slaveTransaction();
    auto rows = txn->exec("SELECT permalink_id FROM " + MERGE_POI_INDOOR_CANDIDATES_PERMALINKS);
    std::set<poi_feed::PermalinkId> ids;
    for (const auto& row : rows) {
        ids.insert(row["permalink_id"].as<poi_feed::PermalinkId>());
    }
    return ids;
}

void
writeCreatedTasksPermalinks(pgpool3::Pool& pool, const std::set<poi_feed::PermalinkId>& ids)
{
    if (ids.empty()) {
        return;
    }
    auto txn = pool.masterWriteableTransaction();
    std::string query = "INSERT INTO " + MERGE_POI_INDOOR_CANDIDATES_PERMALINKS + " (permalink_id) "
        " VALUES " +
        common::join(ids, [](const auto id) {
            return "(" + std::to_string(id) + ")";
        },
        ',');
    txn->exec(query);
    txn->commit();
}
} // namespace

void createFeedbackForIndoorCandidates(
        const std::string& nyakIndoorCandidatesYTURL,
        const NmapsExportResult& nmapsData,
        const Config& cfg)
{
    const auto alreadyCreated = loadCreatedTasksPermalinks(cfg.socialPool());
    const auto indoorPlansData = loadIndoorPlansData(cfg.corePool());
    const auto& indoorLevels = indoorPlansData.activeLevels;
    INFO() << " Indoor levels loaded " << indoorLevels.size();
    if (indoorLevels.empty()) {
        return;
    }
    const IndoorCandidatesData candidatesData(
        nyakIndoorCandidatesYTURL,
        cfg.editorCfg(),
        cfg.supportedNmapsLangs());
    const auto& candidates = candidatesData.candidates;
    INFO() << " Indoor candidates read " << candidates.size();
    if (candidates.empty()) {
        return;
    }
    geolib3::StaticGeometrySearcher<poi_feed::FeedObjectData::Position, size_t>
        candidatesSearcher;
    INFO() << " Build StaticGeometrySearcher for candidates started.";
    std::set<size_t> toFeedback;
    for (size_t i = 0 ; i < candidates.size(); ++i) {
        const auto& candidate = candidates[i];

        if (alreadyCreated.count(candidate.permalink()) ||
            !nmapsData.findByPermalink(candidate.permalink()).empty())
        {
            continue;
        }
        const auto planPois = nmapsData.findByPermalink(
            candidatesData.candidatePermalinkToLocatedAt.at(candidate.permalink()));
        if (std::any_of(
                planPois.begin(), planPois.end(),
                [&indoorPlansData](const auto planPoi)
                {
                    return planPoi->indoorPlanId().has_value() &&
                        !indoorPlansData.inactivePlans.contains(*planPoi->indoorPlanId());
                }))
        {
            toFeedback.insert(i);
        } else {
            candidatesSearcher.insert(&*candidate.position(), i);
        }
    }
    candidatesSearcher.build();
    INFO() << " Build StaticGeometrySearcher for candidates finished.";
    INFO() << " Build set of candidates inside levels";
    for (const auto& indoorLevel : indoorLevels) {
        auto insideBounds = candidatesSearcher.find(indoorLevel.boundingBox());
        for (auto it = insideBounds.first; it != insideBounds.second; ++it) {
            if (toFeedback.count(it->value())) {
                continue;
            }
            const auto& candidate = candidates[it->value()];
            if (indoorLevel.contains(*candidate.position())) {
                toFeedback.insert(it->value());
                INFO() << "createFeedbackForIndoorCandidates level id: " << indoorLevel.oid << " permalink: " << it->value();
            }
        }
    }
    INFO() << " Found " << toFeedback.size() << " candidates to create feedback for.";
    FeedbackClient feedbackClient(cfg.socialBackofficeUrl());
    feedbackClient.setTvmTicketProvider(cfg.socialBackofficeTvmTicketProvider());
    std::set<poi_feed::PermalinkId> permalinkIds;
    for (const auto idx : toFeedback) {
        const auto& candidate = candidates[idx];
        const auto result = feedbackClient.createMissingIndoorPoiTask(candidate);
        if (result.resolution == Resolution::CreatedFeedback) {
            permalinkIds.insert(candidate.permalink());
        }
    }
    INFO() << " Tasks created.";
    writeCreatedTasksPermalinks(cfg.socialPool(), permalinkIds);
}

} // namespace maps::wiki::merge_poi
