#include "new_feedback_onfoot_counter.h"

#include <maps/wikimap/mapspro/services/mrc/libs/db/include/walk_object_gateway.h>
#include <maps/wikimap/mapspro/libs/acl/include/aclgateway.h>
#include <maps/libs/log8/include/log8.h>

namespace acl = maps::wiki::acl;

namespace maps::wiki::tasks::involvement {

namespace {
const std::set<std::string> IGNORE_USERS_ROLES {
    "partner-pedestrian-onfoot",
    "sprav-pedestrian-onfoot",
    "lavka-pedestrian-onfoot"
};

const std::chrono::minutes ACL_REFRESH_INTERVAL(5);

class ByUserFilter
{
public:
    void refresh(pqxx::transaction_base& coreTxn)
    {
        const auto now = chrono::TimePoint::clock::now();
        if (refreshedAt_ &&
            std::chrono::duration_cast<std::chrono::minutes>(*refreshedAt_ - now) <
                ACL_REFRESH_INTERVAL)
        {
            return;
        }
        refreshedAt_ = now;

        acl::ACLGateway aclGw(coreTxn);
        std::set<acl::ID> roleIds;
        for (const auto& roleName : IGNORE_USERS_ROLES) {
            try {
                auto role = aclGw.role(roleName);
                roleIds.insert(role.id());
            } catch (const std::exception& ex) {
                WARN() << "NewOnfootFeedbackCounter: exception while getting role: "
                    << roleName << " " << ex.what();
            }
        }
        const auto usersIds = aclGw.userIdsByRoles(roleIds);
        const auto users = aclGw.usersByIds({usersIds.begin(), usersIds.end()});
        uidsStrings_.clear();
        for (const auto& user : users) {
            uidsStrings_.insert(std::to_string(user.uid()));
        }
    }

    bool operator ()(const mrc::db::WalkObjectGateway::Entity& record) const
    {
        return  !record.hasUserId() || uidsStrings_.contains(record.userId());
    }

private:
    std::set<std::string> uidsStrings_;
    std::optional<chrono::TimePoint> refreshedAt_;
};

} // namespace

int64_t NewOnfootFeedbackCounter::count(
    const geolib3::MultiPolygon2& multiPolygon)
{
    static ByUserFilter byUserFilter;
    mrc::db::WalkObjectGateway gw(mrcReadTxn_);

    sql_chemistry::FiltersCollection filter{sql_chemistry::op::Logical::And};
    filter.add(mrc::db::table::WalkObject::geometry.intersects(multiPolygon));
    filter.add(mrc::db::table::WalkObject::dataset == mrc::db::Dataset::Walks);
    if (start_) {
        filter.add(mrc::db::table::WalkObject::createdAt >= *start_);
    }
    if (finish_) {
        filter.add(mrc::db::table::WalkObject::createdAt <= *finish_);
    }
    auto records = gw.load(filter);
    byUserFilter.refresh(coreTxn_);
    std::erase_if(records, byUserFilter);
    INFO() << " NEW_FEEDBACK_ONFOOT_COUNTER: " << records.size();
    return records.size() - previousValue_;
}
} // namespace maps::wiki::tasks::involvement
