#include "save_filter.h"
#include "magic_strings.h"

#include <maps/wikimap/mapspro/services/editor/src/check_permissions.h>
#include <maps/wikimap/mapspro/services/editor/src/exception.h>
#include <maps/wikimap/mapspro/services/editor/src/configs/config.h>

#include <yandex/maps/wiki/filters/stored_filter.h>
#include <yandex/maps/wiki/filters/exception.h>

namespace maps {
namespace wiki {

namespace {

template<typename T>
void reset(std::unique_ptr<T>& ptr, T value)
{
    ptr.reset(new T(std::move(value)));
}

} // namespace

SaveFilter::SaveFilter(
        const Request& request)
    : controller::BaseController<SaveFilter>(BOOST_CURRENT_FUNCTION)
    , request_(request)
{ }

std::string SaveFilter::printRequest() const
{
    std::stringstream ss;
    ss << " uid: " << request_.uid
       << " filterId: " << request_.filterId
       << " filterName: " << request_.filterName
       << " isPublic: " << request_.isPublic
       << " expression: '" << request_.expression << "'";
    return ss.str();
}

void SaveFilter::control()
{
    auto work = cfg()->poolCore().masterWriteableTransaction();
    CheckPermissions checkPermissions(request_.uid, *work);
    checkPermissions.checkPermissionsToEditFilters();
    bool editPublicFiltersAccess = checkPermissions.isUserHasAccessToEditPublicFilters();
    try {
        if (request_.filterId == 0) {
            reset(result_->filter, filters::StoredFilter::create(
                    *work, request_.uid,
                    request_.filterName, request_.isPublic, request_.expression));
        } else {
            reset(result_->filter, filters::StoredFilter::load(*work, request_.filterId));

            WIKI_REQUIRE(
                result_->filter->isPublic() ||
                    request_.uid == result_->filter->createdBy() ||
                    editPublicFiltersAccess,
                ERR_FORBIDDEN,
                "Edit access forbidden to filter id: " << request_.filterId)
                << " user uid: " << request_.uid;

            WIKI_REQUIRE(
                editPublicFiltersAccess ||
                    (request_.isPublic == result_->filter->isPublic() &&
                        request_.filterName == result_->filter->name()) ||
                    (request_.uid == result_->filter->createdBy()),
                ERR_FORBIDDEN,
                "Edit access forbidden to filter id: " << request_.filterId)
                << " user uid: " << request_.uid;

            result_->filter->setPublic(*work, request_.isPublic);
            result_->filter->setName(*work, request_.filterName);
            result_->filter->setExpression(*work, request_.uid, request_.expression);
        }
    } catch (const filters::ParseError& ex) {
        THROW_WIKI_LOGIC_ERROR(
                ERR_PARSE_ERROR, "could not parse expression: " << ex.what());
    } catch (const filters::FilterNotFound&) {
        THROW_WIKI_LOGIC_ERROR(
                ERR_FILTER_NOT_FOUND,
                "filter id: " << request_.filterId << " does not exist");
    }

    result_->token = pgpool3::generateToken(*work);
    work->commit();
}

} // namespace wiki
} // namespace maps
