#include "fixture.h"

#include <maps/wikimap/mapspro/services/mrc/libs/db/include/feature_gateway.h>
#include <maps/wikimap/mapspro/services/mrc/libs/db/include/walk_object_gateway.h>

#include <library/cpp/testing/gtest/gtest.h>
#include <maps/libs/introspection/include/comparison.h>
#include <maps/libs/local_postgres/include/instance.h>
#include <yandex/maps/mrc/unittest/database_fixture.h>
#include <yandex/maps/mrc/unittest/unittest_config.h>

namespace maps::mrc::db {
using introspection::operator==;
} // namespace maps::mrc::db

namespace maps::mrc::db::tests {
using namespace ::testing;

namespace {

const std::string TEST_UID = "123456";

} // namespace

TEST_F(Fixture, test_walks_test_objects) {
    WalkObjects objects{{
        Dataset::Walks,
        "SOURCE_1",
        chrono::parseIsoDateTime("2018-07-01T18:00:00+03"),
        WalkFeedbackType::None,
        geolib3::Point2{40.0, 40.0},
        "comment"
    }, {
        Dataset::Walks,
        "SOURCE_1",
        chrono::parseIsoDateTime("2018-07-02T18:00:00+03"),
        WalkFeedbackType::Other,
        geolib3::Point2{40.0, 40.0},
        "comment"
    }, {
        Dataset::Walks,
        "SOURCE_3",
        chrono::parseIsoDateTime("2021-06-09T11:10:00+03"),
        WalkFeedbackType::Barrier,
        geolib3::Point2{40.0, 40.0},
        "comment"
    }};

    objects[0].setIndoorLevelId("1");
    objects[0].setActionType(ObjectActionType::Add);
    objects[0].setTaskId("task-id");
    objects[0].setNmapsObjectId("nmaps-object-id");

    Features photos;
    // Save objects
    {
        auto txn = txnHandle();

        WalkObjectGateway objectGateway{*txn};
        objectGateway.insertx(objects);

        photos = Features{
            Feature{
                "SOURCE_1",
                geolib3::Point2{40.0, 40.0},
                geolib3::Heading(20),
                "2018-07-01 18:00:00+03",
                {"GROUP_ID_1", "PATH_1"},
                Dataset::Agents}
            .setUserId(TEST_UID)
            .setWalkObjectId(objects[0].id())
        ,
            Feature{
                "SOURCE_2",
                geolib3::Point2{50.0, 50.0},
                geolib3::Heading(30),
                "2018-07-02 18:00:00+03",
                {"GROUP_ID_2", "PATH_2"},
                Dataset::Agents}
            .setUserId(TEST_UID)
            .setWalkObjectId(objects[1].id())
        ,
            Feature{
                "SOURCE_3",
                geolib3::Point2{50.0, 50.0},
                geolib3::Heading(30),
                "2021-06-09 11:10:00+03",
                {"GROUP_ID_3", "PATH_3"},
                Dataset::BackofficeObject}
            .setUserId(TEST_UID)
            .setWalkObjectId(objects[1].id())
        };
        photos[0].setGdprDeleted(true);

        FeatureGateway photoGateway{*txn};
        photoGateway.insert(photos);

        txn->commit();
    }

    // Check loading photo by filter
    {
        auto txn = txnHandle();
        FeatureGateway photoGateway(*txn);

        auto ids = photoGateway.loadIds();
        std::sort(ids.begin(), ids.end());
        TIds expected = {photos[0].id(), photos[1].id(), photos[2].id()};
        std::sort(expected.begin(), expected.end());
        EXPECT_EQ(ids, expected);
        EXPECT_EQ(photos[0], photoGateway.loadById(photos[0].id()));

        auto photosOfObject = photoGateway.load(
            table::Feature::walkObjectId.equals(objects[0].id()));
        ASSERT_EQ(photosOfObject.size(), 1u);
        EXPECT_EQ(photosOfObject[0], photos[0]);
    }

    // Check loading object by filter
    {
        auto txn = txnHandle();
        WalkObjectGateway objectGateway(*txn);

        auto ids = objectGateway.loadIds();
        std::sort(ids.begin(), ids.end());
        TIds expected = {objects[0].id(), objects[1].id(), objects[2].id()};
        std::sort(expected.begin(), expected.end());
        EXPECT_EQ(ids, expected);

        auto loadedObject = objectGateway.loadById(objects[0].id());
        EXPECT_EQ(objects[0], loadedObject);
        EXPECT_EQ(photos[0].walkObjectId(), loadedObject.id());
        EXPECT_EQ(objects[0].taskId(), loadedObject.taskId());
        EXPECT_EQ(objects[0].nmapsObjectId(), loadedObject.nmapsObjectId());
    }
}

TEST_F(Fixture, test_walk_object_geometry) {
    WalkObject object{
        Dataset::Walks,
        "SOURCE_1",
        chrono::parseIsoDateTime("2018-07-01T18:00:00+03"),
        WalkFeedbackType::None,
        geolib3::Point2{40.0, 40.0}
    };

    auto point = geolib3::Point2{40.0, 40.0};
    object.setUserId(TEST_UID)
          .setGeodeticGeometry(point);

    // Save object
    {
        auto txn = txnHandle();

        WalkObjectGateway objectGateway{*txn};
        objectGateway.insertx(object);
        txn->commit();
    }

    // Check loading object
    {
        auto txn = txnHandle();
        WalkObjectGateway objectGateway(*txn);
        auto loadedObject = objectGateway.loadById(object.id());
        EXPECT_EQ(object, loadedObject);

        auto loadedPoint = std::get<geolib3::Point2>(object.geodeticGeometry());
        EXPECT_EQ(point, loadedPoint);
    }

    // Update geometry from point to polyline
    auto polyline = geolib3::Polyline2(
            geolib3::PointsVector{{10.0, 10.0}, {20.0, 20.0}});
    {
        auto txn = txnHandle();
        WalkObjectGateway objectGateway(*txn);

        object.setGeodeticGeometry(polyline);

        objectGateway.updatex(object);
        txn->commit();
    }

    // Check updated geometry
    {
        auto txn = txnHandle();
        WalkObjectGateway objectGateway(*txn);
        auto loadedObject = objectGateway.loadById(object.id());
        EXPECT_EQ(object, loadedObject);

        auto loadedPolyline = std::get<geolib3::Polyline2>(object.geodeticGeometry());
        EXPECT_EQ(polyline.points(), loadedPolyline.points());
    }
}

TEST_F(Fixture, test_walks_no_unique_time_with_empty_device_id)
{
    auto objects =
        WalkObjects{{Dataset::BackofficeObject,
                     feature::NO_SOURCE_ID,
                     chrono::parseIsoDateTime("2021-06-10T16:34:12+03"),
                     WalkFeedbackType::None,
                     geolib3::Point2{20.0, 20.0},
                     "comment"},
                    {Dataset::BackofficeObject,
                     feature::NO_SOURCE_ID,
                     chrono::parseIsoDateTime("2021-06-10T16:34:12+03"),
                     WalkFeedbackType::None,
                     geolib3::Point2{30.0, 30.0},
                     "comment"}};
    auto txn = txnHandle();
    WalkObjectGateway objectGateway{*txn};
    objectGateway.insertx(objects);
    txn->commit();
}

} // namespace maps::mrc::db::tests
