#include <maps/wikimap/mapspro/services/tasks_feedback/src/sync_fbapi_feedback_worker/lib/fbapi_issue_parser.h>
#include <maps/wikimap/mapspro/services/tasks_feedback/src/sync_fbapi_feedback_worker/lib/worker.h>
#include <maps/wikimap/mapspro/services/tasks_feedback/src/sync_fbapi_feedback_worker/lib/magic_strings.h>
#include <maps/wikimap/mapspro/services/tasks_feedback/src/sync_fbapi_feedback_worker/lib/feedback_subsource.h>
#include <maps/wikimap/mapspro/services/tasks_feedback/src/sync_fbapi_feedback_worker/lib/decode_coordinates.h>

#include <library/cpp/testing/common/env.h>
#include <maps/libs/log8/include/log8.h>
#include <yandex/maps/wiki/social/fbapi_issue.h>
#include <yandex/maps/wiki/social/feedback/attribute_names.h>
#include <yandex/maps/wiki/social/feedback/description.h>
#include <yandex/maps/wiki/social/feedback/description_keys.h>
#include <yandex/maps/wiki/social/feedback/enums.h>

#include <boost/test/unit_test.hpp>

#include <map>

namespace maps::wiki::sync_fbapi_feedback::tests {

namespace sf = social::feedback;
namespace sfa = social::feedback::attrs;
namespace sft = social::feedback::tanker;

namespace {

struct SetLogLevelFixture
{
    SetLogLevelFixture() {
        maps::log8::setLevel(maps::log8::Level::FATAL);
    }
};

struct TestFixture : public SetLogLevelFixture
{
};

FbapiIssueParser issueParserFromFileName(
    const std::string& fileName,
    IUriResolver& uriResolver)
{
    return FbapiIssueParser(
        uriResolver,
        fbapi::Task(
            json::Value::fromFile(SRC_("data/" + fileName))
        )
    );
}

class StubUriResolver : public IUriResolver
{
public:
    StubUriResolver() {}
    explicit StubUriResolver(std::map<std::string, std::string> uriMap)
        : uriMap_(std::move(uriMap)) {}

    std::optional<std::string> resolveObjectUri(const std::string& uri) override {
        auto it = uriMap_.find(uri);
        if (uriMap_.end() != it) {
            return it->second;
        }
        return std::nullopt;
    }
    std::optional<Route> resolveDrivingRouteUri(const std::string&) override {
        return std::nullopt;
    }
    std::optional<Route> resolveMtRouteUri(const std::string&) override {
        return std::nullopt;
    }
    std::optional<Route> resolvePedestrianRouteUri(const std::string&) override {
        return std::nullopt;
    }
    std::optional<Route> resolveBicycleRouteUri(const std::string&) override {
        return std::nullopt;
    }
    std::optional<Route> resolveScooterRouteUri(const std::string&) override {
        return std::nullopt;
    }

private:
    const std::map<std::string, std::string> uriMap_;
};

FbapiIssueParser issueParserFromFileName(const std::string& fileName)
{
    StubUriResolver uriResolver;
    return issueParserFromFileName(fileName, uriResolver);
}

} // anonymous namespace

BOOST_FIXTURE_TEST_CASE(remove_object_toponym_stop_parsing, TestFixture)
{
    auto issue = issueParserFromFileName("remove_object.toponym.stop.json");
    BOOST_CHECK_EQUAL(issue.type(), sf::Type::PublicTransportStop);
    BOOST_CHECK(issue.description() ==
        sf::DescriptionI18n(sft::fb_desc::REMOVE_STOP_KEY)
    );
    BOOST_CHECK(issue.hidden() == true);

    const auto& attrs = issue.attrs();

    BOOST_REQUIRE(attrs.existCustom(sfa::SUBSOURCE));
    BOOST_REQUIRE_EQUAL(
        attrs.getCustom(sfa::SUBSOURCE),
        generateSubsource(question_id::REMOVE_OBJECT, answer_id::TOPONYM)
    );
    BOOST_REQUIRE_EQUAL(
        attrs.getCustom(sfa::USER_DATA_SEARCH_REQUEST),
        "User search request"
    );
    BOOST_REQUIRE_EQUAL(
        attrs.getCustom(sfa::USER_DATA_COMMENT),
        "Test remove stop"
    );
    BOOST_REQUIRE_EQUAL(
        attrs.getCustom(sfa::UID),
        "946771392"
    );
    BOOST_REQUIRE_EQUAL(
        attrs.getCustom(sfa::YANDEXUID),
        "7951179461569331160"
    );
}

BOOST_FIXTURE_TEST_CASE(wrong_name_report_name_stop_parsing, TestFixture)
{
    auto issue = issueParserFromFileName("wrong_name.report_name.stop.json");
    BOOST_CHECK_EQUAL(issue.type(), sf::Type::PublicTransportStop);

    BOOST_CHECK(issue.description() ==
        sf::DescriptionI18n(
            sft::fb_desc::WRONG_NAME_KEY,
            {
                {sft::CURRENT_NAME, sf::Description("Проспект Героев")},
                {sft::CORRECT_NAME, sf::Description("Проспект Героев 1")}
            }
        ));
    BOOST_CHECK(issue.hidden() == true);

    const auto& attrs = issue.attrs();

    BOOST_REQUIRE(attrs.existCustom(sfa::SUBSOURCE));
    BOOST_REQUIRE_EQUAL(
        attrs.getCustom(sfa::SUBSOURCE),
        generateSubsource(question_id::WRONG_NAME, answer_id::REPORT_NAME)
    );
    BOOST_REQUIRE_EQUAL(
        attrs.getCustom(sfa::USER_DATA_COMMENT),
        "Test rename stop"
    );
}

BOOST_FIXTURE_TEST_CASE(wrong_location_report_location_stop_parsing, TestFixture)
{
    const auto issue = issueParserFromFileName("wrong_location.report_location.stop.json");
    BOOST_CHECK_EQUAL(issue.type(), sf::Type::PublicTransportStop);
    BOOST_CHECK(issue.description() ==
        sf::DescriptionI18n(sft::fb_desc::LOCATION_WRONG_POS_KEY)
    );
    BOOST_CHECK(issue.hidden() == true);

    const auto& attrs = issue.attrs();

    BOOST_REQUIRE(attrs.existCustom(sfa::SUBSOURCE));
    BOOST_REQUIRE_EQUAL(
        attrs.getCustom(sfa::SUBSOURCE),
        generateSubsource(question_id::WRONG_LOCATION, answer_id::REPORT_LOCATION)
    );
    BOOST_REQUIRE_EQUAL(
        attrs.getCustom(sfa::USER_DATA_COMMENT),
        "Test stop move"
    );

    BOOST_REQUIRE(attrs.exist(sf::AttrType::ObjectDiff));
}

BOOST_FIXTURE_TEST_CASE(wrong_lines_report_lines_parsing, TestFixture)
{
    const auto issue = issueParserFromFileName("wrong_lines.report_lines.json");
    BOOST_CHECK_EQUAL(issue.type(), sf::Type::PublicTransportStop);
    BOOST_CHECK(issue.description() ==
        sf::DescriptionI18n(sft::fb_desc::WRONG_LINES_SET)
    );
    BOOST_REQUIRE(!issue.objectId());
    BOOST_CHECK(issue.hidden() == true);

    const auto& attrs = issue.attrs();
    BOOST_REQUIRE(attrs.existCustom(sfa::SUBSOURCE));
    BOOST_REQUIRE_EQUAL(
        attrs.getCustom(sfa::SUBSOURCE),
        generateSubsource(question_id::WRONG_LINES, answer_id::REPORT_LINES)
    );
    BOOST_REQUIRE_EQUAL(
        attrs.getCustom(sfa::USER_DATA_COMMENT),
        "Test stop lines edit"
    );
}

BOOST_FIXTURE_TEST_CASE(add_object_other_parsing, TestFixture)
{
    auto issue = issueParserFromFileName("add_object.other.json");
    BOOST_CHECK_EQUAL(issue.type(), sf::Type::Other);
    BOOST_CHECK(issue.description() ==
        sf::DescriptionI18n(sft::fb_desc::ABSENT_OBJECT_KEY)
    );
    BOOST_REQUIRE(!issue.objectId());
    BOOST_CHECK(issue.hidden() == true);

    const auto& attrs = issue.attrs();
    BOOST_REQUIRE(attrs.existCustom(sfa::SUBSOURCE));
    BOOST_REQUIRE_EQUAL(
        attrs.getCustom(sfa::SUBSOURCE),
        generateSubsource(question_id::ADD_OBJECT, answer_id::OTHER)
    );
    BOOST_REQUIRE_EQUAL(
        attrs.getCustom(sfa::USER_DATA_COMMENT),
        "Test"
    );
    BOOST_REQUIRE(attrs.getFlag(sfa::FROM_PUSH));
    BOOST_REQUIRE_EQUAL(
        attrs.getCustom(sfa::USER_EMAIL),
        "yndx-kensora@yandex.ru"
    );
}

BOOST_FIXTURE_TEST_CASE(wrong_address_dont_know_parsing, TestFixture)
{
    auto issue = issueParserFromFileName("wrong_address.dont_know.json");
    BOOST_CHECK_EQUAL(issue.type(), sf::Type::Address);
    BOOST_CHECK(issue.description() ==
        sf::DescriptionI18n(sft::fb_desc::ADDRESS_UNKNOWN_KEY, {}));
    BOOST_REQUIRE(issue.objectId());
    BOOST_CHECK_EQUAL(*issue.objectId(), 110419761);
    BOOST_CHECK(issue.hidden() == true);

    const auto& attrs = issue.attrs();
    BOOST_REQUIRE(attrs.existCustom(sfa::SUBSOURCE));
    BOOST_REQUIRE_EQUAL(
        attrs.getCustom(sfa::SUBSOURCE),
        generateSubsource(question_id::WRONG_ADDRESS, answer_id::DONT_KNOW)
    );
}

BOOST_FIXTURE_TEST_CASE(wrong_address_report_address_parsing, TestFixture)
{
    auto issue = issueParserFromFileName("wrong_address.report_address.json");
    BOOST_CHECK_EQUAL(issue.type(), sf::Type::Address);

    BOOST_CHECK(issue.description() ==
        sf::DescriptionI18n(
            sft::fb_desc::ADDR_CORRECTION_KEY,
            {
                {sft::CORRECT_STREET, sf::Description("Ну какой-то проезд")},
                {sft::CORRECT_HOUSE, sf::Description("1000к3")}
            }
        ));

    BOOST_REQUIRE(issue.objectId());
    BOOST_CHECK_EQUAL(*issue.objectId(), 56800591);
    BOOST_CHECK(issue.hidden() == std::nullopt);

    const auto& attrs = issue.attrs();

    BOOST_REQUIRE(attrs.existCustom(sfa::SUBSOURCE));
    BOOST_REQUIRE_EQUAL(
        attrs.getCustom(sfa::SUBSOURCE),
        generateSubsource(question_id::WRONG_ADDRESS, answer_id::REPORT_ADDRESS)
    );

    BOOST_REQUIRE(attrs.existCustom(sfa::STREET));
    BOOST_REQUIRE(attrs.getCustom(sfa::STREET) == "Ну какой-то проезд");

    BOOST_REQUIRE(attrs.existCustom(sfa::HOUSE));
    BOOST_REQUIRE(attrs.getCustom(sfa::HOUSE) == "1000к3");
}

BOOST_FIXTURE_TEST_CASE(wrong_address_report_address_string_parsing, TestFixture)
{
    auto issue = issueParserFromFileName("wrong_address.report_address_string.json");
    BOOST_CHECK_EQUAL(issue.type(), sf::Type::Address);

    BOOST_CHECK(issue.description() ==
        sf::DescriptionI18n(
            sft::fb_desc::ADDR_CORRECTION_KEY,
            {
                {sft::CORRECT_ADDRESS, sf::Description("улица Мира 5")}
            }
        ));
}

BOOST_FIXTURE_TEST_CASE(wrong_address_report_location_parsing, TestFixture)
{
    auto issue = issueParserFromFileName("wrong_address.report_location.json");
    BOOST_CHECK_EQUAL(issue.type(), sf::Type::Address);

    BOOST_CHECK(issue.description() ==
        sf::DescriptionI18n(sft::fb_desc::ADDR_WRONG_POS_KEY, {})
    );

    const auto& attrs = issue.attrs();

    BOOST_REQUIRE(attrs.existCustom(sfa::SUBSOURCE));
    BOOST_REQUIRE_EQUAL(
        attrs.getCustom(sfa::SUBSOURCE),
        generateSubsource(question_id::WRONG_ADDRESS, answer_id::REPORT_LOCATION)
    );

    BOOST_REQUIRE(issue.objectId());
    BOOST_CHECK_EQUAL(*issue.objectId(), 56697621);
    BOOST_CHECK(issue.hidden() == std::nullopt);
}

BOOST_FIXTURE_TEST_CASE(decode_coordinates, TestFixture)
{
    {
        auto points = decodeCoordinates("wNinAICxTwFAQg8AQEIPALgLAAAKAAAA");
        BOOST_CHECK_EQUAL(points.size() , 3);
        BOOST_CHECK_CLOSE(points[2].x(), 12.003, 0.001);
        BOOST_CHECK_CLOSE(points[2].y(), 23, 0.001);
    }
    {
        auto points = decodeCoordinates("oTz0_aqrdAM=");
        BOOST_CHECK_EQUAL(points.size() , 1);
        BOOST_CHECK_CLOSE(points[0].x(), -34.3253, 0.001);
        BOOST_CHECK_CLOSE(points[0].y(), 57.9778, 0.001);
    }
}

BOOST_FIXTURE_TEST_CASE(wrong_route_report_route_parsing, TestFixture)
{
    auto issue = issueParserFromFileName("wrong_route.report_route.json");
    BOOST_CHECK_EQUAL(issue.type(), sf::Type::Barrier);

    BOOST_CHECK(issue.description() ==
        sf::DescriptionI18n(sft::fb_desc::ROUTE_NO_DRIVEWAY_KEY)
    );

    const auto& attrs = issue.attrs();

    BOOST_REQUIRE(attrs.existCustom(sfa::SUBSOURCE));
    BOOST_REQUIRE_EQUAL(
        attrs.getCustom(sfa::SUBSOURCE),
        generateSubsource(question_id::WRONG_ROUTE, answer_id::REPORT_ROUTE)
    );
    BOOST_REQUIRE_EQUAL(
        attrs.getCustom(sfa::USER_DATA_COMMENT),
        "Test"
    );

    BOOST_REQUIRE(attrs.exist(sf::AttrType::SourceContext));
    const auto& sourceContext = attrs.get(sf::AttrType::SourceContext);
    BOOST_REQUIRE(sourceContext.hasField("type"));
    BOOST_REQUIRE_EQUAL(sourceContext["type"].type(), json::Type::String);
    BOOST_REQUIRE_EQUAL(sourceContext["type"].as<std::string>(), "route");
    BOOST_CHECK_EQUAL(
        sourceContext["content"]["departureAt"].as<std::string>(),
        "2020-10-08T17:20:39Z");
    {
        auto vehicleRestrictions =
            sourceContext["content"]["userErrors"][1]["context"]["vehicleRestrictions"];
        BOOST_CHECK_CLOSE(vehicleRestrictions["maxWeight"].as<float>(), 12, 0.0001);
        BOOST_CHECK_CLOSE(vehicleRestrictions["height"].as<float>(), 4.1235, 0.00001);
    }
    {
        auto vehicleRestrictions =
            sourceContext["content"]["vehicleRestrictions"];
        BOOST_CHECK_CLOSE(vehicleRestrictions["weight"].as<float>(), 2.2323, 0.0001);
        BOOST_CHECK_CLOSE(vehicleRestrictions["maxWeight"].as<float>(), 5, 0.0001);
        BOOST_CHECK_CLOSE(vehicleRestrictions["payload"].as<float>(), 2, 0.0001);
        BOOST_CHECK_CLOSE(vehicleRestrictions["axleWeight"].as<float>(), 3.5, 0.0001);
        BOOST_CHECK_CLOSE(vehicleRestrictions["height"].as<float>(), 4.1, 0.0001);
        BOOST_CHECK_CLOSE(vehicleRestrictions["length"].as<float>(), 10, 0.0001);
        BOOST_CHECK_CLOSE(vehicleRestrictions["width"].as<float>(), 23, 0.0001);
        BOOST_CHECK_EQUAL(vehicleRestrictions["ecoClass"].as<int>(), 4);
        BOOST_CHECK_EQUAL(vehicleRestrictions["hasTrailer"].as<bool>(), false);
    }
    BOOST_CHECK(issue.hidden() == true);
}

BOOST_FIXTURE_TEST_CASE(wrong_entrance_report_location_parsing, TestFixture)
{
    auto issue = issueParserFromFileName("wrong_entrance.report_location.json");
    BOOST_CHECK_EQUAL(issue.type(), sf::Type::Entrance);

    BOOST_REQUIRE(issue.objectId());
    BOOST_CHECK_EQUAL(*issue.objectId(), 1561531504);
    BOOST_CHECK(issue.hidden() == std::nullopt);

    BOOST_CHECK(issue.description() ==
        sf::DescriptionI18n(
            sft::fb_desc::ENTRANCE_WRONG_POS_KEY,
            {{sft::ENTRANCE_NAME, sf::Description("2")}}
        )
    );

    const auto& attrs = issue.attrs();

    BOOST_REQUIRE(attrs.existCustom(sfa::SUBSOURCE));
    BOOST_REQUIRE_EQUAL(
        attrs.getCustom(sfa::SUBSOURCE),
        generateSubsource(question_id::WRONG_ENTRANCE, answer_id::REPORT_LOCATION)
    );

    BOOST_REQUIRE(attrs.existCustom(sfa::ENTRANCE_NAME));
    BOOST_REQUIRE(attrs.getCustom(sfa::ENTRANCE_NAME) == "2");
}

BOOST_FIXTURE_TEST_CASE(wrong_location_report_location_parsing, TestFixture)
{
    StubUriResolver uriResolver({{"ymapsbm1://geo?...", "1561531504"}});
    auto issue = issueParserFromFileName(
        "wrong_location.report_location.json",
        uriResolver);
    BOOST_CHECK_EQUAL(issue.type(), sf::Type::Poi);

    BOOST_REQUIRE(issue.objectId());
    BOOST_CHECK_EQUAL(*issue.objectId(), 1561531504);
    BOOST_CHECK(issue.hidden() == true);

    BOOST_CHECK(issue.description() ==
        sf::DescriptionI18n(
            sft::fb_desc::LOCATION_WRONG_POS_KEY,
            {}
        )
    );

    const auto& attrs = issue.attrs();

    BOOST_REQUIRE(attrs.existCustom(sfa::SUBSOURCE));
    BOOST_REQUIRE_EQUAL(
        attrs.getCustom(sfa::SUBSOURCE),
        generateSubsource(question_id::WRONG_LOCATION, answer_id::REPORT_LOCATION)
    );
    BOOST_REQUIRE_EQUAL(
        attrs.getCustom(sfa::OBJECT_URI),
        "ymapsbm1://geo?..."
    );

    BOOST_REQUIRE(attrs.exist(sf::AttrType::ObjectDiff));
}

BOOST_FIXTURE_TEST_CASE(wrong_entrance_not_found_parsing, TestFixture)
{
    auto issue = issueParserFromFileName("wrong_entrance.not_found.json");
    BOOST_CHECK_EQUAL(issue.type(), sf::Type::Entrance);

    BOOST_REQUIRE(issue.objectId());
    BOOST_CHECK_EQUAL(*issue.objectId(), 1561531504);
    BOOST_CHECK(issue.hidden() == std::nullopt);

    BOOST_CHECK(issue.description() ==
        sf::DescriptionI18n(
            sft::fb_desc::DELETE_ENTRANCE_KEY,
            {{sft::ENTRANCE_NAME, sf::Description("34")}}
        )
    );

    const auto& attrs = issue.attrs();

    BOOST_REQUIRE(attrs.existCustom(sfa::SUBSOURCE));
    BOOST_REQUIRE_EQUAL(
        attrs.getCustom(sfa::SUBSOURCE),
        generateSubsource(question_id::WRONG_ENTRANCE, answer_id::NOT_FOUND)
    );

    BOOST_REQUIRE(attrs.existCustom(sfa::ENTRANCE_NAME));
    BOOST_REQUIRE(attrs.getCustom(sfa::ENTRANCE_NAME) == "34");
}

BOOST_FIXTURE_TEST_CASE(wrong_position_demolished_parsing, TestFixture)
{
    auto issue = issueParserFromFileName("wrong_position.demolished.json");
    BOOST_CHECK_EQUAL(issue.type(), sf::Type::Building);
    BOOST_CHECK(issue.description() ==
        sf::DescriptionI18n(sft::fb_desc::BUILDING_DEMOLISHED_KEY, {}));
    BOOST_REQUIRE(issue.objectId());
    BOOST_CHECK_EQUAL(*issue.objectId(), 56703519);
    BOOST_CHECK(issue.hidden() == true);
}

BOOST_FIXTURE_TEST_CASE(wrong_position_never_been_here_parsing, TestFixture)
{
    auto issue = issueParserFromFileName("wrong_position.never_been_here.json");
    BOOST_CHECK_EQUAL(issue.type(), sf::Type::Building);
    BOOST_CHECK(issue.description() ==
        sf::DescriptionI18n(sft::fb_desc::NO_BUILDING_HERE_KEY, {}));
    BOOST_REQUIRE(issue.objectId());
    BOOST_CHECK_EQUAL(*issue.objectId(), 1682356487);
    BOOST_CHECK(issue.hidden() == true);
}

BOOST_FIXTURE_TEST_CASE(wrong_position_not_found_parsing, TestFixture)
{
    auto issue = issueParserFromFileName("wrong_position.not_found.json");
    BOOST_CHECK_EQUAL(issue.type(), sf::Type::Building);
    BOOST_CHECK(issue.description() ==
        sf::DescriptionI18n(sft::fb_desc::NO_BUILDING_HERE_KEY, {}));
    BOOST_REQUIRE(issue.objectId());
    BOOST_CHECK_EQUAL(*issue.objectId(), 110372958);
    BOOST_CHECK(issue.hidden() == std::nullopt);
}

BOOST_FIXTURE_TEST_CASE(wrong_position_other_parsing, TestFixture)
{
    auto issue = issueParserFromFileName("wrong_position.other.json");
    BOOST_CHECK_EQUAL(issue.type(), sf::Type::Building);
    BOOST_CHECK(issue.description() ==
        sf::DescriptionI18n(sft::fb_desc::NO_BUILDING_HERE_KEY)
    );
    BOOST_REQUIRE(issue.objectId());
    BOOST_CHECK_EQUAL(*issue.objectId(), 110419761);
    BOOST_CHECK(issue.hidden() == true);

    const auto& attrs = issue.attrs();
    BOOST_REQUIRE_EQUAL(
        attrs.getCustom(sfa::USER_DATA_COMMENT),
        "снесли"
    );
}

BOOST_FIXTURE_TEST_CASE(add_object_entrance_parsing, TestFixture)
{
    auto issue = issueParserFromFileName("add_object.entrance.json");
    BOOST_CHECK_EQUAL(issue.type(), sf::Type::Entrance);
    BOOST_CHECK(issue.description() ==
        sf::DescriptionI18n(
            sft::fb_desc::ABSENT_ENTRANCE_KEY,
            {{sft::ENTRANCE_NAME, sf::Description("3")}}
        )
    );
    BOOST_REQUIRE(issue.objectId());
    BOOST_CHECK_EQUAL(*issue.objectId(), 1571881602);
    BOOST_CHECK(issue.hidden() == std::nullopt);

    const auto& attrs = issue.attrs();

    BOOST_REQUIRE(attrs.existCustom(sfa::SUBSOURCE));
    BOOST_REQUIRE_EQUAL(
        attrs.getCustom(sfa::SUBSOURCE),
        generateSubsource(question_id::ADD_OBJECT, answer_id::ENTRANCE)
    );

    BOOST_REQUIRE(attrs.existCustom(sfa::ENTRANCE_NAME));
    BOOST_REQUIRE(attrs.getCustom(sfa::ENTRANCE_NAME) == "3");
}

BOOST_FIXTURE_TEST_CASE(add_object_parking_parsing, TestFixture)
{
    auto issue = issueParserFromFileName("add_object.parking.json");
    BOOST_CHECK_EQUAL(issue.type(), sf::Type::Parking);
    BOOST_CHECK(issue.description() ==
        sf::DescriptionI18n(sft::fb_desc::ABSENT_PARKING_ENTRANCES_KEY)
    );
    BOOST_REQUIRE(issue.objectId());
    BOOST_CHECK_EQUAL(*issue.objectId(), 1571881602);
    BOOST_CHECK(issue.hidden() == true);

    const auto& attrs = issue.attrs();

    BOOST_REQUIRE(attrs.existCustom(sfa::SUBSOURCE));
    BOOST_REQUIRE_EQUAL(
        attrs.getCustom(sfa::SUBSOURCE),
        generateSubsource(question_id::ADD_OBJECT, answer_id::PARKING)
    );
    BOOST_REQUIRE_EQUAL(
        attrs.getCustom(sfa::USER_DATA_COMMENT),
        "Test add parking"
    );
    BOOST_REQUIRE(attrs.exist(sf::AttrType::ObjectDiff));
}

BOOST_FIXTURE_TEST_CASE(add_object_parking_parsing_no_entrances, TestFixture)
{
    auto issue = issueParserFromFileName("add_object.parking-no-entrances.json");
    BOOST_CHECK_EQUAL(issue.type(), sf::Type::Parking);
    BOOST_CHECK(issue.description() ==
        sf::DescriptionI18n(
            sft::fb_desc::ABSENT_PARKING_KEY,
            {}
        )
    );
    BOOST_REQUIRE(issue.objectId());
    BOOST_CHECK_EQUAL(*issue.objectId(), 1571881602);
    BOOST_CHECK(issue.hidden() == std::nullopt);

    const auto& attrs = issue.attrs();

    BOOST_REQUIRE(attrs.existCustom(sfa::SUBSOURCE));
    BOOST_REQUIRE_EQUAL(
        attrs.getCustom(sfa::SUBSOURCE),
        generateSubsource(question_id::ADD_OBJECT, answer_id::PARKING)
    );
    BOOST_REQUIRE(!attrs.exist(sf::AttrType::ObjectDiff));
}

BOOST_FIXTURE_TEST_CASE(add_object_toponym_parsing, TestFixture)
{
    auto issue = issueParserFromFileName("add_object.toponym.json");
    BOOST_CHECK_EQUAL(issue.type(), sf::Type::Address);
    BOOST_CHECK(issue.description() ==
        sf::DescriptionI18n(
            sft::fb_desc::NEW_ADDRESS_KEY,
            {{sft::CORRECT_STREET, sf::Description("Кастанаевская улица")},
             {sft::CORRECT_HOUSE, sf::Description("30к1")}}
        )
    );
    BOOST_REQUIRE(issue.objectId());
    BOOST_CHECK_EQUAL(*issue.objectId(), 1561531506);
    BOOST_CHECK(issue.hidden() == std::nullopt);

    const auto& attrs = issue.attrs();

    BOOST_REQUIRE(attrs.existCustom(sfa::SUBSOURCE));
    BOOST_REQUIRE_EQUAL(
        attrs.getCustom(sfa::SUBSOURCE),
        generateSubsource(question_id::ADD_OBJECT, answer_id::TOPONYM)
    );
    BOOST_REQUIRE(!attrs.existCustom(sfa::USER_DATA_COMMENT));
    BOOST_REQUIRE(!attrs.exist(sf::AttrType::UserDataPhotoUrls));

    BOOST_REQUIRE(attrs.existCustom(sfa::STREET));
    BOOST_REQUIRE(attrs.getCustom(sfa::STREET) == "Кастанаевская улица");

    BOOST_REQUIRE(attrs.existCustom(sfa::STREET));
    BOOST_REQUIRE(attrs.getCustom(sfa::HOUSE) == "30к1");
}

BOOST_FIXTURE_TEST_CASE(add_object_stop_parsing, TestFixture)
{
    auto issue = issueParserFromFileName("add_object.stop.json");
    BOOST_CHECK_EQUAL(issue.type(), sf::Type::PublicTransportStop);
    BOOST_CHECK(issue.description() ==
        sf::DescriptionI18n(
            sft::fb_desc::NO_PUBLIC_TRANSPORT_STOP_HERE_KEY,
            {{sft::NAME, sf::Description("L'va Tolstogo")}}
        )
    );
    BOOST_CHECK(issue.hidden() == std::nullopt);

    const auto& attrs = issue.attrs();

    BOOST_REQUIRE(attrs.existCustom(sfa::SUBSOURCE));
    BOOST_REQUIRE_EQUAL(
        attrs.getCustom(sfa::SUBSOURCE),
        generateSubsource(question_id::ADD_OBJECT, answer_id::STOP)
    );
    BOOST_REQUIRE(!attrs.existCustom(sfa::USER_DATA_COMMENT));
}

BOOST_FIXTURE_TEST_CASE(add_object_fence_parsing, TestFixture)
{
    auto issue = issueParserFromFileName("add_object.fence.json");
    BOOST_CHECK_EQUAL(issue.type(), sf::Type::Fence);
    BOOST_CHECK(issue.description() ==
        sf::DescriptionI18n(sft::fb_desc::ABSENT_FENCE_KEY)
    );
    BOOST_CHECK(issue.hidden() == true);

    const auto& attrs = issue.attrs();

    BOOST_REQUIRE(attrs.existCustom(sfa::SUBSOURCE));
    BOOST_REQUIRE_EQUAL(
        attrs.getCustom(sfa::SUBSOURCE),
        generateSubsource(question_id::ADD_OBJECT, answer_id::FENCE)
    );
    BOOST_REQUIRE_EQUAL(
        attrs.getCustom(sfa::USER_DATA_COMMENT),
        "Test add fence"
    );

    BOOST_REQUIRE(attrs.exist(sf::AttrType::ObjectDiff));
}

BOOST_FIXTURE_TEST_CASE(add_object_road_parsing, TestFixture)
{
    auto issue = issueParserFromFileName("add_object.road.json");
    BOOST_CHECK_EQUAL(issue.type(), sf::Type::Road);
    BOOST_CHECK(issue.description() ==
        sf::DescriptionI18n(
            sft::fb_desc::ABSENT_ROAD_KEY,
            sf::ParamToDescription{{"roadName", sf::Description{"road name"}}})
    );
    BOOST_CHECK(issue.hidden() == true); // has message

    const auto& attrs = issue.attrs();

    BOOST_REQUIRE(attrs.existCustom(sfa::SUBSOURCE));
    BOOST_REQUIRE_EQUAL(
        attrs.getCustom(sfa::SUBSOURCE),
        generateSubsource(question_id::ADD_OBJECT, answer_id::ROAD)
    );
    BOOST_REQUIRE_EQUAL(
        attrs.getCustom(sfa::USER_DATA_COMMENT),
        "Test add road"
    );

    BOOST_REQUIRE(attrs.exist(sf::AttrType::ObjectDiff));
}

BOOST_FIXTURE_TEST_CASE(other_comment_parsing, TestFixture)
{
    auto issue = issueParserFromFileName("other.comment.json");
    BOOST_CHECK_EQUAL(issue.type(), sf::Type::Other);
    BOOST_CHECK(issue.description() ==
        sf::DescriptionI18n(sft::fb_desc::USER_COMMENT_KEY)
    );
    BOOST_REQUIRE(issue.objectId());
    BOOST_CHECK_EQUAL(*issue.objectId(), 1682356487);
    BOOST_REQUIRE(issue.hidden());
    BOOST_CHECK_EQUAL(*issue.hidden(), true);

    const auto& attrs = issue.attrs();
    BOOST_REQUIRE_EQUAL(
        attrs.getCustom(sfa::USER_DATA_COMMENT),
        "Дом, по адрему ул.Ленина, 15, снесли"
    );
}

BOOST_FIXTURE_TEST_CASE(other_photos_parsing, TestFixture)
{
    auto issue = issueParserFromFileName("other.photos.json");
    BOOST_CHECK_EQUAL(issue.type(), sf::Type::Other);
    BOOST_CHECK(issue.description() ==
        sf::DescriptionI18n(sft::fb_desc::USER_COMMENT_KEY)
    );
    BOOST_REQUIRE(issue.objectId());
    BOOST_CHECK_EQUAL(*issue.objectId(), 1682356487);
    BOOST_REQUIRE(issue.hidden());
    BOOST_CHECK_EQUAL(*issue.hidden(), true);

    const auto& attrs = issue.attrs();
    BOOST_REQUIRE(!attrs.existCustom(sfa::USER_DATA_COMMENT));
    BOOST_REQUIRE(attrs.exist(sf::AttrType::UserDataPhotoUrls));
    auto photoUrls = attrs.get(sf::AttrType::UserDataPhotoUrls);
    BOOST_CHECK_EQUAL(photoUrls, json::Value::fromString(
        R"--(["https://avatars.mds.yandex.net/get-maps-feedback/123/456/orig")--"
        R"--(,"https://avatars.mds.yandex.net/get-maps-feedback/789/012/orig"])--"
    ));
}

BOOST_FIXTURE_TEST_CASE(remove_object_toponym_parsing, TestFixture)
{
    // TODO: check other fields and fix test data
    auto issue = issueParserFromFileName("remove_object.toponym.json");
    BOOST_CHECK_EQUAL(issue.type(), sf::Type::Other);
    BOOST_REQUIRE(issue.objectId());
    BOOST_CHECK_EQUAL(*issue.objectId(), 56686285);
}

} // namespace maps::wiki::sync_fbapi_feedback::tests
