#include <library/cpp/testing/gtest/gtest.h>
#include <maps/wikimap/mapspro/services/mrc/eye/lib/generate_wrong_condition/include/generator.h>
#include <maps/wikimap/mapspro/services/mrc/eye/lib/location/include/rotation.h>
#include <maps/wikimap/mapspro/services/mrc/libs/object/include/mock_loader.h>
#include <maps/libs/geolib/include/test_tools/comparison.h>

using namespace maps::geolib3::literals;

namespace maps::mrc::eye {

namespace tests {

enum class Equidistant { No, Yes };

geolib3::Polyline2 polyline(
        const geolib3::PointsVector& points,
        Equidistant equidistant=Equidistant::No)
{
    geolib3::Polyline2 result(points);

    if (equidistant == Equidistant::Yes) {
        result = geolib3::equidistant(
            result,
            geolib3::toMercatorUnits(1, result.points().front()),
            geolib3::Orientation::Clockwise
        );
    }

    return geolib3::convertMercatorToGeodetic(result);
}

std::string print(const geolib3::Polyline2& path)
{
    std::stringstream ss;
    ss << std::fixed;
    for (const auto& p: path.points()) {
        ss << "[" << p.x() << "," << p.y() << "] ";
    }
    ss << std::endl;
    return ss.str();
}

TEST(test_generate_wrong_condition, filter_test)
{
    db::eye::Objects objects{
        {
            db::TId(0u),
            db::eye::SignAttrs{traffic_signs::TrafficSign::ProhibitoryNoUturn, false}
        },
        {
            db::TId(0u),
            db::eye::SignAttrs{traffic_signs::TrafficSign::ProhibitoryNoUturn, true}
        }
    };

    objects = filterObjects<WrongConditionGeneratorImpl>(objects);

    EXPECT_EQ(objects.size(), 1u);
}

TEST(test_generate_wrong_condition, empty)
{
    object::MockLoader loader;

    const db::eye::Object object(
        0u,
        db::eye::SignAttrs{traffic_signs::TrafficSign::ProhibitoryNoUturn, false}
    );

    const db::eye::ObjectLocation location(
        db::TId(0u),
        geolib3::Point2(0, 0),
        toRotation(geolib3::Heading(180.0), common::ImageOrientation(common::Rotation::CW_0))
    );

    const db::eye::Objects slaveObjects;

    const auto hypotheses = WrongConditionGeneratorImpl::validate(object, location, slaveObjects, loader);

    EXPECT_TRUE(hypotheses.empty());
}

TEST(test_generate_wrong_condition, regular_road_without_uturn)
{
    /*
              [201]       [202]
        (101)<ooooo>(102)<ooooo>(103)
                    |--O
    */
    object::MockLoader loader;

    loader.add(
        object::RoadJunctions{
            object::RoadJunction(
                object::RevisionID{101, 1},
                geolib3::Point2{-10, 0}
            )
            .elementIds({201})
            , /////////////////////////////////////////////////
            object::RoadJunction(
                object::RevisionID{102, 1},
                geolib3::Point2{0, 0}
            )
            .elementIds({201, 202})
            , /////////////////////////////////////////////////
            object::RoadJunction(
                object::RevisionID{103, 1},
                geolib3::Point2{10, 0}
            )
            .elementIds({202})
        }
    );

    loader.add(
        object::RoadElements{
            object::RoadElement(
                object::RevisionID{201, 1},
                geolib3::Polyline2{
                    geolib3::PointsVector{{-10, 0},{0, 0}}
                }
            )
            .fc(object::RoadElement::FunctionalClass::MajorLocalRoad)
            .startZLevel(0)
            .startJunctionId(101)
            .endZLevel(0)
            .endJunctionId(102)
            .direction(object::RoadElement::Direction::Both)
            .accessId(object::RoadElement::AccessId::Car)
            , /////////////////////////////////////////////////
            object::RoadElement(
                object::RevisionID{202, 1},
                geolib3::Polyline2{
                    geolib3::PointsVector{{0, 0},{10, 0}}
                }
            )
            .fc(object::RoadElement::FunctionalClass::MajorLocalRoad)
            .startZLevel(0)
            .startJunctionId(102)
            .endZLevel(0)
            .endJunctionId(103)
            .direction(object::RoadElement::Direction::Both)
            .accessId(object::RoadElement::AccessId::Car)
        }
    );

    const db::eye::Object object(
        0u,
        db::eye::SignAttrs{traffic_signs::TrafficSign::ProhibitoryNoUturn, false}
    );

    const db::eye::ObjectLocation location(
        db::TId(0u),
        geolib3::Point2(0, -1),
        toRotation(geolib3::Heading(270.0), common::ImageOrientation(common::Rotation::CW_0))
    );

    const db::eye::Objects slaveObjects;

    const auto hypotheses = WrongConditionGeneratorImpl::validate(object, location, slaveObjects, loader);

    EXPECT_TRUE(hypotheses.empty());
}
TEST(test_generate_wrong_condition, regular_road_with_uturn)
{
    /*
             <--{301}
             -----|
        (101)<ooooo>(102)<ooooo>(103)
              [201]       [202
                    |--O
    */
    object::MockLoader loader;

    loader.add(
        object::RoadJunctions {
            object::RoadJunction(
                object::RevisionID{101, 1},
                geolib3::Point2{-10, 0}
            )
            .elementIds({201})
            , /////////////////////////////////////////////////
            object::RoadJunction(
                object::RevisionID{102, 1},
                geolib3::Point2{0, 0}
            )
            .elementIds({201, 202})
            .conditionIds({301})
            , /////////////////////////////////////////////////
            object::RoadJunction(
                object::RevisionID{103, 1},
                geolib3::Point2{10, 0}
            )
            .elementIds({202})
        }
    );

    loader.add(
        object::RoadElements{
            object::RoadElement(
                object::RevisionID{201, 1},
                geolib3::Polyline2{
                    geolib3::PointsVector{{-10, 0},{0, 0}}
                }
            )
            .fc(object::RoadElement::FunctionalClass::MajorLocalRoad)
            .startZLevel(0)
            .startJunctionId(101)
            .endZLevel(0)
            .endJunctionId(102)
            .direction(object::RoadElement::Direction::Both)
            .accessId(object::RoadElement::AccessId::Car)
            , /////////////////////////////////////////////////
            object::RoadElement(
                object::RevisionID{202, 1},
                geolib3::Polyline2{
                    geolib3::PointsVector{{0, 0},{10, 0}}
                }
            )
            .fc(object::RoadElement::FunctionalClass::MajorLocalRoad)
            .startZLevel(0)
            .startJunctionId(102)
            .endZLevel(0)
            .endJunctionId(103)
            .direction(object::RoadElement::Direction::Both)
            .accessId(object::RoadElement::AccessId::Car)
        }
    );

    loader.add(
        object::Conditions {
            object::Condition{object::RevisionID{301, 1}}
            .type(object::Condition::Type::Uturn)
            .accessId(object::Condition::AccessId::Car)
            .fromElementId(201)
            .viaJunctionId(102)
            .toElementIds({201})
        }
    );

    const db::eye::Object object(
        0u,
        db::eye::SignAttrs{traffic_signs::TrafficSign::ProhibitoryNoUturn, false}
    );

    const db::eye::ObjectLocation location(
        db::TId(0u),
        geolib3::Point2(-1, -1),
        toRotation(geolib3::Heading(270.0), common::ImageOrientation(common::Rotation::CW_0))
    );

    const db::eye::Objects slaveObjects;

    const auto hypotheses = WrongConditionGeneratorImpl::validate(object, location, slaveObjects, loader);

    EXPECT_EQ(hypotheses.size(), 1u);
    const auto& hypothesis = hypotheses.front();
    EXPECT_EQ(hypothesis.type(), db::eye::HypothesisType::ProhibitedPath);
    EXPECT_TRUE(
        geolib3::test_tools::approximateEqual(
            geolib3::convertMercatorToGeodetic(hypothesis.attrs<db::eye::ProhibitedPathAttrs>().mercatorPath),
            polyline({{-10, 0}, {0, 0}, {-10, 0}}, Equidistant::Yes), geolib3::EPS)
    );
    EXPECT_EQ(
        hypothesis.attrs<db::eye::ProhibitedPathAttrs>().movement,
        wiki::social::feedback::Movement::Uturn
    );
}

TEST(test_generate_wrong_condition, two_carriageways_with_uturn)
{
    /*
            (101)       (102)
              o           ^
              o           o
              o           o
        [201] o           o [202]
              o           o
              o           o
              v   [203]   o
            (103)<ooooo>(104)  O
              o           ^    |
              o           o    ┴
              o           o
        [204] o           o [205]
              o           o
              o           o
              v           o
            (105)       (106)
    */
    object::MockLoader loader;

    loader.add(
        object::RoadJunctions {
            object::RoadJunction {
                object::RevisionID{101, 1},
                geolib3::Point2{-2, 10}
            }
            .elementIds({201})
            , /////////////////////////////////////////////////
            object::RoadJunction {
                object::RevisionID{102, 1},
                geolib3::Point2{2, 10}
            }
            .elementIds({202})
            , /////////////////////////////////////////////////
            object::RoadJunction {
                object::RevisionID{103, 1},
                geolib3::Point2{-2, 0}
            }
            .elementIds({201, 203, 204})
            , /////////////////////////////////////////////////
            object::RoadJunction {
                object::RevisionID{104, 1},
                geolib3::Point2{2, 0}
            }
            .elementIds({202, 203, 205})
            , /////////////////////////////////////////////////
            object::RoadJunction {
                object::RevisionID{105, 1},
                geolib3::Point2{-2, -10}
            }
            .elementIds({204})
            , /////////////////////////////////////////////////
            object::RoadJunction {
                object::RevisionID{106, 1},
                geolib3::Point2{2, -10}
            }
            .elementIds({205})
        }
    );

    loader.add(
        object::RoadElements{
            object::RoadElement(
                object::RevisionID{201, 1},
                geolib3::Polyline2{
                    geolib3::PointsVector{{-2, 0},{-2, 10}}
                }
            )
            .fc(object::RoadElement::FunctionalClass::RegionalRoad)
            .startZLevel(0)
            .startJunctionId(103)
            .endZLevel(0)
            .endJunctionId(101)
            .direction(object::RoadElement::Direction::Backward)
            .accessId(object::RoadElement::AccessId::Car)
            , /////////////////////////////////////////////////
            object::RoadElement(
                    object::RevisionID{202, 1},
                    geolib3::Polyline2{
                        geolib3::PointsVector{{2, 0},{2, 10}}
                    }
            )
            .fc(object::RoadElement::FunctionalClass::RegionalRoad)
            .startZLevel(0)
            .startJunctionId(104)
            .endZLevel(0)
            .endJunctionId(102)
            .direction(object::RoadElement::Direction::Forward)
            .accessId(object::RoadElement::AccessId::Car)
            , /////////////////////////////////////////////////
            object::RoadElement(
                object::RevisionID{203, 1},
                geolib3::Polyline2{
                    geolib3::PointsVector{{-2, 0},{2, 0}}
                }
            )
            .fc(object::RoadElement::FunctionalClass::RegionalRoad)
            .startZLevel(0)
            .startJunctionId(103)
            .endZLevel(0)
            .endJunctionId(104)
            .direction(object::RoadElement::Direction::Both)
            .accessId(object::RoadElement::AccessId::Car)
            , /////////////////////////////////////////////////
            object::RoadElement(
                object::RevisionID{204, 1},
                geolib3::Polyline2{
                    geolib3::PointsVector{{-2, -10},{-2, 0}}
                }
            )
            .fc(object::RoadElement::FunctionalClass::RegionalRoad)
            .startZLevel(0)
            .startJunctionId(105)
            .endZLevel(0)
            .endJunctionId(103)
            .direction(object::RoadElement::Direction::Backward)
            .accessId(object::RoadElement::AccessId::Car)
            , /////////////////////////////////////////////////
            object::RoadElement(
                object::RevisionID{205, 1},
                geolib3::Polyline2{
                    geolib3::PointsVector{{2, -10},{2, 0}}
                }
            )
            .fc(object::RoadElement::FunctionalClass::RegionalRoad)
            .startZLevel(0)
            .startJunctionId(106)
            .endZLevel(0)
            .endJunctionId(104)
            .direction(object::RoadElement::Direction::Forward)
            .accessId(object::RoadElement::AccessId::Car)
        }
    );

    const db::eye::Object object(
        0u,
        db::eye::SignAttrs{traffic_signs::TrafficSign::ProhibitoryNoUturn, false}
    );

    const db::eye::ObjectLocation location(
        db::TId(0u),
        geolib3::Point2(3, 0),
        toRotation(geolib3::Heading(180.0), common::ImageOrientation(common::Rotation::CW_0))
    );

    const db::eye::Objects slaveObjects;

    const auto hypotheses = WrongConditionGeneratorImpl::validate(object, location, slaveObjects, loader);

    EXPECT_EQ(hypotheses.size(), 1u);
    const auto& hypothesis = hypotheses.front();
    EXPECT_EQ(hypothesis.type(), db::eye::HypothesisType::ProhibitedPath);
    EXPECT_TRUE(
        geolib3::test_tools::approximateEqual(
            geolib3::convertMercatorToGeodetic(hypothesis.attrs<db::eye::ProhibitedPathAttrs>().mercatorPath),
            polyline({{2, -10}, {2, 0}, {-2, 0}, {-2, -10}}), geolib3::EPS)
    );
    EXPECT_EQ(
        hypothesis.attrs<db::eye::ProhibitedPathAttrs>().movement,
        wiki::social::feedback::Movement::Uturn
    );
}

TEST(test_generate_wrong_condition, two_carriageways_without_uturn)
{
    /*
            (101)       (102)
              o           ^
              o           o
              o           o
        [201] o           o [202]
              o           o
              o           o
              v   [203]   o
            (103)<ooooo>(104)  O
              o x-------x ^    |
              o | {301} | o    ┴
              o |       | o
        [204] o v       | o [205]
              o           o
              o           o
              v           o
            (105)       (106)
    */
    object::MockLoader loader;

    loader.add(
        object::RoadJunctions {
            object::RoadJunction {
                object::RevisionID{101, 1},
                geolib3::Point2{-2, 10}
            }
            .elementIds({201})
            , /////////////////////////////////////////////////
            object::RoadJunction {
                object::RevisionID{102, 1},
                geolib3::Point2{2, 10}
            }
            .elementIds({202})
            , /////////////////////////////////////////////////
            object::RoadJunction {
                object::RevisionID{103, 1},
                geolib3::Point2{-2, 0}
            }
            .elementIds({201, 203, 204})
            , /////////////////////////////////////////////////
            object::RoadJunction {
                object::RevisionID{104, 1},
                geolib3::Point2{2, 0}
            }
            .elementIds({202, 203, 205})
            .conditionIds({301})
            , /////////////////////////////////////////////////
            object::RoadJunction {
                object::RevisionID{105, 1},
                geolib3::Point2{-2, -10}
            }
            .elementIds({204})
            , /////////////////////////////////////////////////
            object::RoadJunction {
                object::RevisionID{106, 1},
                geolib3::Point2{2, -10}
            }
            .elementIds({205})
        }
    );

    loader.add(
        object::RoadElements{
            object::RoadElement(
                object::RevisionID{201, 1},
                geolib3::Polyline2{
                    geolib3::PointsVector{{-2, 0},{-2, 10}}
                }
            )
            .fc(object::RoadElement::FunctionalClass::RegionalRoad)
            .startZLevel(0)
            .startJunctionId(103)
            .endZLevel(0)
            .endJunctionId(101)
            .direction(object::RoadElement::Direction::Backward)
            .accessId(object::RoadElement::AccessId::Car)
            , /////////////////////////////////////////////////
            object::RoadElement(
                    object::RevisionID{202, 1},
                    geolib3::Polyline2{
                        geolib3::PointsVector{{2, 0},{2, 10}}
                    }
            )
            .fc(object::RoadElement::FunctionalClass::RegionalRoad)
            .startZLevel(0)
            .startJunctionId(104)
            .endZLevel(0)
            .endJunctionId(102)
            .direction(object::RoadElement::Direction::Forward)
            .accessId(object::RoadElement::AccessId::Car)
            , /////////////////////////////////////////////////
            object::RoadElement(
                object::RevisionID{203, 1},
                geolib3::Polyline2{
                    geolib3::PointsVector{{-2, 0},{2, 0}}
                }
            )
            .fc(object::RoadElement::FunctionalClass::RegionalRoad)
            .startZLevel(0)
            .startJunctionId(103)
            .endZLevel(0)
            .endJunctionId(104)
            .direction(object::RoadElement::Direction::Both)
            .accessId(object::RoadElement::AccessId::Car)
            , /////////////////////////////////////////////////
            object::RoadElement(
                object::RevisionID{204, 1},
                geolib3::Polyline2{
                    geolib3::PointsVector{{-2, -10},{-2, 0}}
                }
            )
            .fc(object::RoadElement::FunctionalClass::RegionalRoad)
            .startZLevel(0)
            .startJunctionId(105)
            .endZLevel(0)
            .endJunctionId(103)
            .direction(object::RoadElement::Direction::Backward)
            .accessId(object::RoadElement::AccessId::Car)
            , /////////////////////////////////////////////////
            object::RoadElement(
                object::RevisionID{205, 1},
                geolib3::Polyline2{
                    geolib3::PointsVector{{2, -10},{2, 0}}
                }
            )
            .fc(object::RoadElement::FunctionalClass::RegionalRoad)
            .startZLevel(0)
            .startJunctionId(106)
            .endZLevel(0)
            .endJunctionId(104)
            .direction(object::RoadElement::Direction::Forward)
            .accessId(object::RoadElement::AccessId::Car)
        }
    );

    loader.add(
        object::Conditions {
            object::Condition{object::RevisionID{301, 1}}
            .type(object::Condition::Type::Prohibited)
            .accessId(object::Condition::AccessId::Car)
            .fromElementId(205)
            .viaJunctionId(104)
            .toElementIds({203, 204})
        }
    );

    const db::eye::Object object(
        0u,
        db::eye::SignAttrs{traffic_signs::TrafficSign::ProhibitoryNoUturn, false}
    );

    const db::eye::ObjectLocation location(
        db::TId(0u),
        geolib3::Point2(3, 0),
        toRotation(geolib3::Heading(180.0), common::ImageOrientation(common::Rotation::CW_0))
    );

    const db::eye::Objects slaveObjects;

    const auto hypotheses = WrongConditionGeneratorImpl::validate(object, location, slaveObjects, loader);

    EXPECT_TRUE(hypotheses.empty());
}

TEST(test_generate_wrong_condition, prohibited_left_turn)
{
    /*
            (101)       (102)
              o           ^
              o           o
              o           o
        [201] o           o [202]
              o           o
              o           o
              v   [203]   o
            (103)<ooooo>(104)  O
              o <-------x ^    |
              o   {301} | o    ┴
              o         | o
        [204] o         | o [205]
              o           o
              o           o
              v           o
            (105)       (106)
    */
    object::MockLoader loader;

    loader.add(
        object::RoadJunctions {
            object::RoadJunction {
                object::RevisionID{101, 1},
                geolib3::Point2{-2, 10}
            }
            .elementIds({201})
            , /////////////////////////////////////////////////
            object::RoadJunction {
                object::RevisionID{102, 1},
                geolib3::Point2{2, 10}
            }
            .elementIds({202})
            , /////////////////////////////////////////////////
            object::RoadJunction {
                object::RevisionID{103, 1},
                geolib3::Point2{-2, 0}
            }
            .elementIds({201, 203, 204})
            , /////////////////////////////////////////////////
            object::RoadJunction {
                object::RevisionID{104, 1},
                geolib3::Point2{2, 0}
            }
            .elementIds({202, 203, 205})
            .conditionIds({301})
            , /////////////////////////////////////////////////
            object::RoadJunction {
                object::RevisionID{105, 1},
                geolib3::Point2{-2, -10}
            }
            .elementIds({204})
            , /////////////////////////////////////////////////
            object::RoadJunction {
                object::RevisionID{106, 1},
                geolib3::Point2{2, -10}
            }
            .elementIds({205})
        }
    );

    loader.add(
        object::RoadElements{
            object::RoadElement(
                object::RevisionID{201, 1},
                geolib3::Polyline2{
                    geolib3::PointsVector{{-2, 0},{-2, 10}}
                }
            )
            .fc(object::RoadElement::FunctionalClass::RegionalRoad)
            .startZLevel(0)
            .startJunctionId(103)
            .endZLevel(0)
            .endJunctionId(101)
            .direction(object::RoadElement::Direction::Backward)
            .accessId(object::RoadElement::AccessId::Car)
            , /////////////////////////////////////////////////
            object::RoadElement(
                    object::RevisionID{202, 1},
                    geolib3::Polyline2{
                        geolib3::PointsVector{{2, 0},{2, 10}}
                    }
            )
            .fc(object::RoadElement::FunctionalClass::RegionalRoad)
            .startZLevel(0)
            .startJunctionId(104)
            .endZLevel(0)
            .endJunctionId(102)
            .direction(object::RoadElement::Direction::Forward)
            .accessId(object::RoadElement::AccessId::Car)
            , /////////////////////////////////////////////////
            object::RoadElement(
                object::RevisionID{203, 1},
                geolib3::Polyline2{
                    geolib3::PointsVector{{-2, 0},{2, 0}}
                }
            )
            .fc(object::RoadElement::FunctionalClass::RegionalRoad)
            .startZLevel(0)
            .startJunctionId(103)
            .endZLevel(0)
            .endJunctionId(104)
            .direction(object::RoadElement::Direction::Both)
            .accessId(object::RoadElement::AccessId::Car)
            , /////////////////////////////////////////////////
            object::RoadElement(
                object::RevisionID{204, 1},
                geolib3::Polyline2{
                    geolib3::PointsVector{{-2, -10},{-2, 0}}
                }
            )
            .fc(object::RoadElement::FunctionalClass::RegionalRoad)
            .startZLevel(0)
            .startJunctionId(105)
            .endZLevel(0)
            .endJunctionId(103)
            .direction(object::RoadElement::Direction::Backward)
            .accessId(object::RoadElement::AccessId::Car)
            , /////////////////////////////////////////////////
            object::RoadElement(
                object::RevisionID{205, 1},
                geolib3::Polyline2{
                    geolib3::PointsVector{{2, -10},{2, 0}}
                }
            )
            .fc(object::RoadElement::FunctionalClass::RegionalRoad)
            .startZLevel(0)
            .startJunctionId(106)
            .endZLevel(0)
            .endJunctionId(104)
            .direction(object::RoadElement::Direction::Forward)
            .accessId(object::RoadElement::AccessId::Car)
        }
    );

    loader.add(
        object::Conditions {
            object::Condition{object::RevisionID{301, 1}}
            .type(object::Condition::Type::Prohibited)
            .accessId(object::Condition::AccessId::Car)
            .fromElementId(205)
            .viaJunctionId(104)
            .toElementIds({203})
        }
    );

    const db::eye::Object object(
        0u,
        db::eye::SignAttrs{traffic_signs::TrafficSign::ProhibitoryNoLeftTurn, false}
    );

    const db::eye::ObjectLocation location(
        db::TId(0u),
        geolib3::Point2(3, 0),
        toRotation(geolib3::Heading(180.0), common::ImageOrientation(common::Rotation::CW_0))
    );

    const db::eye::Objects slaveObjects;

    const auto hypotheses = WrongConditionGeneratorImpl::validate(object, location, slaveObjects, loader);

    EXPECT_TRUE(hypotheses.empty());
}

TEST(test_generate_wrong_condition, prohibited_left_turn_is_possible)
{
    /*
            (101)       (102)
              o           ^
              o           o
              o           o
        [201] o           o [202]
              o           o
              o           o
              v   [203]   o
            (103)<ooooo>(104)  O
              o           ^    |
              o           o    ┴
              o           o
        [204] o           o [205]
              o           o
              o           o
              v           o
            (105)       (106)
    */
    object::MockLoader loader;

    loader.add(
        object::RoadJunctions {
            object::RoadJunction {
                object::RevisionID{101, 1},
                geolib3::Point2{-2, 10}
            }
            .elementIds({201})
            , /////////////////////////////////////////////////
            object::RoadJunction {
                object::RevisionID{102, 1},
                geolib3::Point2{2, 10}
            }
            .elementIds({202})
            , /////////////////////////////////////////////////
            object::RoadJunction {
                object::RevisionID{103, 1},
                geolib3::Point2{-2, 0}
            }
            .elementIds({201, 203, 204})
            , /////////////////////////////////////////////////
            object::RoadJunction {
                object::RevisionID{104, 1},
                geolib3::Point2{2, 0}
            }
            .elementIds({202, 203, 205})
            .conditionIds({301})
            , /////////////////////////////////////////////////
            object::RoadJunction {
                object::RevisionID{105, 1},
                geolib3::Point2{-2, -10}
            }
            .elementIds({204})
            , /////////////////////////////////////////////////
            object::RoadJunction {
                object::RevisionID{106, 1},
                geolib3::Point2{2, -10}
            }
            .elementIds({205})
        }
    );

    loader.add(
        object::RoadElements{
            object::RoadElement(
                object::RevisionID{201, 1},
                geolib3::Polyline2{
                    geolib3::PointsVector{{-2, 0},{-2, 10}}
                }
            )
            .fc(object::RoadElement::FunctionalClass::RegionalRoad)
            .startZLevel(0)
            .startJunctionId(103)
            .endZLevel(0)
            .endJunctionId(101)
            .direction(object::RoadElement::Direction::Backward)
            .accessId(object::RoadElement::AccessId::Car)
            , /////////////////////////////////////////////////
            object::RoadElement(
                    object::RevisionID{202, 1},
                    geolib3::Polyline2{
                        geolib3::PointsVector{{2, 0},{2, 10}}
                    }
            )
            .fc(object::RoadElement::FunctionalClass::RegionalRoad)
            .startZLevel(0)
            .startJunctionId(104)
            .endZLevel(0)
            .endJunctionId(102)
            .direction(object::RoadElement::Direction::Forward)
            .accessId(object::RoadElement::AccessId::Car)
            , /////////////////////////////////////////////////
            object::RoadElement(
                object::RevisionID{203, 1},
                geolib3::Polyline2{
                    geolib3::PointsVector{{-2, 0},{2, 0}}
                }
            )
            .fc(object::RoadElement::FunctionalClass::RegionalRoad)
            .startZLevel(0)
            .startJunctionId(103)
            .endZLevel(0)
            .endJunctionId(104)
            .direction(object::RoadElement::Direction::Both)
            .accessId(object::RoadElement::AccessId::Car)
            , /////////////////////////////////////////////////
            object::RoadElement(
                object::RevisionID{204, 1},
                geolib3::Polyline2{
                    geolib3::PointsVector{{-2, -10},{-2, 0}}
                }
            )
            .fc(object::RoadElement::FunctionalClass::RegionalRoad)
            .startZLevel(0)
            .startJunctionId(105)
            .endZLevel(0)
            .endJunctionId(103)
            .direction(object::RoadElement::Direction::Backward)
            .accessId(object::RoadElement::AccessId::Car)
            , /////////////////////////////////////////////////
            object::RoadElement(
                object::RevisionID{205, 1},
                geolib3::Polyline2{
                    geolib3::PointsVector{{2, -10},{2, 0}}
                }
            )
            .fc(object::RoadElement::FunctionalClass::RegionalRoad)
            .startZLevel(0)
            .startJunctionId(106)
            .endZLevel(0)
            .endJunctionId(104)
            .direction(object::RoadElement::Direction::Forward)
            .accessId(object::RoadElement::AccessId::Car)
        }
    );

    const db::eye::Object object(
        0u,
        db::eye::SignAttrs{traffic_signs::TrafficSign::ProhibitoryNoLeftTurn, false}
    );

    const db::eye::ObjectLocation location(
        db::TId(0u),
        geolib3::Point2(3, 0),
        toRotation(geolib3::Heading(180.0), common::ImageOrientation(common::Rotation::CW_0))
    );

    const db::eye::Objects slaveObjects;

    const auto hypotheses = WrongConditionGeneratorImpl::validate(object, location, slaveObjects, loader);

    EXPECT_EQ(hypotheses.size(), 1u);
    const auto& hypothesis = hypotheses.front();
    EXPECT_EQ(hypothesis.type(), db::eye::HypothesisType::ProhibitedPath);
    EXPECT_TRUE(
        geolib3::test_tools::approximateEqual(
            geolib3::convertMercatorToGeodetic(hypothesis.attrs<db::eye::ProhibitedPathAttrs>().mercatorPath),
            polyline({{2, -10}, {2, 0}, {-2, 0}}), geolib3::EPS)
    );
    EXPECT_EQ(
        hypothesis.attrs<db::eye::ProhibitedPathAttrs>().movement,
        wiki::social::feedback::Movement::LeftTurn
    );
}

TEST(test_generate_wrong_condition, prohibited_right_turn_is_possible)
{
    /*
        (101)<ooooo>(102)<ooooo>(103)
              [201]   ^   [202]
                      o
                |--O  o
                      o   [203]
                      o
                      o
                      v
                    (104)
    */
    object::MockLoader loader;

    loader.add(
        object::RoadJunctions{
            object::RoadJunction(
                object::RevisionID{101, 1},
                geolib3::Point2{-10, 0}
            )
            .elementIds({201})
            , /////////////////////////////////////////////////
            object::RoadJunction(
                object::RevisionID{102, 1},
                geolib3::Point2{0, 0}
            )
            .elementIds({201, 202, 203})
            , /////////////////////////////////////////////////
            object::RoadJunction(
                object::RevisionID{103, 1},
                geolib3::Point2{10, 0}
            )
            .elementIds({202})
            , /////////////////////////////////////////////////
            object::RoadJunction(
                object::RevisionID{104, 1},
                geolib3::Point2{0, -10}
            )
            .elementIds({203})
        }
    );

    loader.add(
        object::RoadElements{
            object::RoadElement(
                object::RevisionID{201, 1},
                geolib3::Polyline2{
                    geolib3::PointsVector{{-10, 0},{0, 0}}
                }
            )
            .fc(object::RoadElement::FunctionalClass::MajorLocalRoad)
            .startZLevel(0)
            .startJunctionId(101)
            .endZLevel(0)
            .endJunctionId(102)
            .direction(object::RoadElement::Direction::Both)
            .accessId(object::RoadElement::AccessId::Car)
            , /////////////////////////////////////////////////
            object::RoadElement(
                object::RevisionID{202, 1},
                geolib3::Polyline2{
                    geolib3::PointsVector{{0, 0},{10, 0}}
                }
            )
            .fc(object::RoadElement::FunctionalClass::MajorLocalRoad)
            .startZLevel(0)
            .startJunctionId(102)
            .endZLevel(0)
            .endJunctionId(103)
            .direction(object::RoadElement::Direction::Both)
            .accessId(object::RoadElement::AccessId::Car)
            , /////////////////////////////////////////////////
            object::RoadElement(
                object::RevisionID{203, 1},
                geolib3::Polyline2{
                    geolib3::PointsVector{{0, 0},{0, -10}}
                }
            )
            .fc(object::RoadElement::FunctionalClass::MajorLocalRoad)
            .startZLevel(0)
            .startJunctionId(102)
            .endZLevel(0)
            .endJunctionId(104)
            .direction(object::RoadElement::Direction::Both)
            .accessId(object::RoadElement::AccessId::Car)
        }
    );


    const db::eye::Object object(
        0u,
        db::eye::SignAttrs{traffic_signs::TrafficSign::ProhibitoryNoRightTurn, false}
    );

    const db::eye::ObjectLocation location(
        db::TId(0u),
        geolib3::Point2(-1, -1),
        toRotation(geolib3::Heading(270.0), common::ImageOrientation(common::Rotation::CW_0))
    );

    const db::eye::Objects slaveObjects;

    const auto hypotheses = WrongConditionGeneratorImpl::validate(object, location, slaveObjects, loader);

    EXPECT_EQ(hypotheses.size(), 1u);
    const auto& hypothesis = hypotheses.front();
    EXPECT_EQ(hypothesis.type(), db::eye::HypothesisType::ProhibitedPath);
    EXPECT_TRUE(
        geolib3::test_tools::approximateEqual(
            geolib3::convertMercatorToGeodetic(hypothesis.attrs<db::eye::ProhibitedPathAttrs>().mercatorPath),
            polyline({{-10, 0}, {0, 0}, {0, -10}}), geolib3::EPS)
    );
    EXPECT_EQ(
        hypothesis.attrs<db::eye::ProhibitedPathAttrs>().movement,
        wiki::social::feedback::Movement::RightTurn
    );
}

TEST(test_generate_wrong_condition, prohibited_right_turn_is_possible_with_trucks_table)
{
    /*
        (101)<ooooo>(102)<ooooo>(103)
              [201]   ^   [202]
                      o
                |--O  o
                      o   [203]
                      o
                      o
                      v
                    (104)
    */
    object::MockLoader loader;

    loader.add(
        object::RoadJunctions{
            object::RoadJunction(
                object::RevisionID{101, 1},
                geolib3::Point2{-10, 0}
            )
            .elementIds({201})
            , /////////////////////////////////////////////////
            object::RoadJunction(
                object::RevisionID{102, 1},
                geolib3::Point2{0, 0}
            )
            .elementIds({201, 202, 203})
            , /////////////////////////////////////////////////
            object::RoadJunction(
                object::RevisionID{103, 1},
                geolib3::Point2{10, 0}
            )
            .elementIds({202})
            , /////////////////////////////////////////////////
            object::RoadJunction(
                object::RevisionID{104, 1},
                geolib3::Point2{0, -10}
            )
            .elementIds({203})
        }
    );

    loader.add(
        object::RoadElements{
            object::RoadElement(
                object::RevisionID{201, 1},
                geolib3::Polyline2{
                    geolib3::PointsVector{{-10, 0},{0, 0}}
                }
            )
            .fc(object::RoadElement::FunctionalClass::MajorLocalRoad)
            .startZLevel(0)
            .startJunctionId(101)
            .endZLevel(0)
            .endJunctionId(102)
            .direction(object::RoadElement::Direction::Both)
            .accessId(object::RoadElement::AccessId::Car)
            , /////////////////////////////////////////////////
            object::RoadElement(
                object::RevisionID{202, 1},
                geolib3::Polyline2{
                    geolib3::PointsVector{{0, 0},{10, 0}}
                }
            )
            .fc(object::RoadElement::FunctionalClass::MajorLocalRoad)
            .startZLevel(0)
            .startJunctionId(102)
            .endZLevel(0)
            .endJunctionId(103)
            .direction(object::RoadElement::Direction::Both)
            .accessId(object::RoadElement::AccessId::Car)
            , /////////////////////////////////////////////////
            object::RoadElement(
                object::RevisionID{203, 1},
                geolib3::Polyline2{
                    geolib3::PointsVector{{0, 0},{0, -10}}
                }
            )
            .fc(object::RoadElement::FunctionalClass::MajorLocalRoad)
            .startZLevel(0)
            .startJunctionId(102)
            .endZLevel(0)
            .endJunctionId(104)
            .direction(object::RoadElement::Direction::Both)
            .accessId(object::RoadElement::AccessId::Car)
        }
    );


    const db::eye::Object object(
        0u,
        db::eye::SignAttrs{traffic_signs::TrafficSign::ProhibitoryNoRightTurn, false}
    );

    const db::eye::ObjectLocation location(
        db::TId(0u),
        geolib3::Point2(-1, -1),
        toRotation(geolib3::Heading(270.0), common::ImageOrientation(common::Rotation::CW_0))
    );

    const db::eye::Objects slaveObjects{
        {
            db::TId(1u),
            db::eye::SignAttrs{traffic_signs::TrafficSign::InformationHeavyVehicle, false}
        },
    };

    const auto hypotheses = WrongConditionGeneratorImpl::validate(object, location, slaveObjects, loader);

    EXPECT_EQ(hypotheses.size(), 0u);
}

} //namespace tests

} //namespace maps::mrc::eye
