#include <library/cpp/testing/common/env.h>
#include <library/cpp/testing/gtest/gtest.h>
#include <maps/wikimap/mapspro/services/mrc/libs/db/include/feature_gateway.h>
#include <maps/wikimap/mapspro/services/mrc/libs/db/include/hypothesis_gateway.h>
#include <maps/wikimap/mapspro/services/mrc/libs/db/include/sign_gateway.h>
#include <maps/wikimap/mapspro/services/mrc/long_tasks/feedback_stat/lib/counter.h>
#include <maps/wikimap/mapspro/services/mrc/long_tasks/feedback_stat/lib/db.h>
#include <yandex/maps/mrc/unittest/database_fixture.h>

#include <boost/lexical_cast.hpp>

namespace maps::mrc::feedback_stat::tests {

using namespace testing;

struct Fixture : testing::Test,
                 unittest::WithUnittestConfig<unittest::DatabaseFixture> {
};

    TEST(suite_counter, test_aggregation)
    {
        social::FeedbackTasks feedbackTasks;
        feedbackTasks.push_back(social::FeedbackTask{
            .id = 1,
            .createdAt = chrono::parseSqlDateTime("2020-09-23 12:07:00"),
            .type = wiki::social::feedback::Type::Address,
            .verdict = wiki::social::feedback::Verdict::Accepted});
        feedbackTasks.push_back(social::FeedbackTask{
            .id = 2,
            .createdAt = chrono::parseSqlDateTime("2020-09-23 12:28:00"),
            .type = wiki::social::feedback::Type::Address,
            .verdict = wiki::social::feedback::Verdict::Rejected});
        feedbackTasks.push_back(social::FeedbackTask{
            .id = 3,
            .createdAt = chrono::parseSqlDateTime("2020-09-23 12:30:00"),
            .type = wiki::social::feedback::Type::Address,
            .verdict = wiki::social::feedback::Verdict::Accepted});
        feedbackTasks.push_back(social::FeedbackTask{
            .id = 4,
            .createdAt = chrono::parseSqlDateTime("2020-09-23 15:17:00"),
            .type = wiki::social::feedback::Type::Address,
            .verdict = wiki::social::feedback::Verdict::Accepted});
        feedbackTasks.push_back(social::FeedbackTask{
            .id = 5,
            .createdAt = chrono::parseSqlDateTime("2020-09-24 12:44:00"),
            .type = wiki::social::feedback::Type::RoadSpeedLimit,
            .verdict = wiki::social::feedback::Verdict::Accepted});

        TIdSet committedFeedbackIds{
            1,
            3,
            4,
        };

        TIdToSignalMap feedbackIdToSignalMap{
            {1,
             {.createdAt = chrono::parseSqlDateTime("2020-09-23 11:07:00"),
              .source = Source::Agents}},
            {2,
             {.createdAt = chrono::parseSqlDateTime("2020-09-23 11:28:00"),
              .source = Source::Agents}},
            {3,
             {.createdAt = chrono::parseSqlDateTime("2020-09-23 11:30:00"),
              .source = Source::Rides}},
            {4,
             {.createdAt = chrono::parseSqlDateTime("2020-09-23 14:17:00"),
              .source = Source::Rides}},
            {5,
             {.createdAt = chrono::parseSqlDateTime("2020-09-24 11:44:00"),
              .source = Source::Agents}},
        };

        TTimeline expectAgents{
            {chrono::parseSqlDateTime("2020-09-23 00:00:00"),
             {
                 {wiki::social::feedback::Type::Address,
                  Counter{.waiting = 0,
                          .rejected = 1,
                          .accepted = 0,
                          .committed = 1}},
             }},
            {chrono::parseSqlDateTime("2020-09-24 00:00:00"),
             {
                 {wiki::social::feedback::Type::RoadSpeedLimit,
                  Counter{.waiting = 0,
                          .rejected = 0,
                          .accepted = 1,
                          .committed = 0}},
             }},
        };

        TTimeline expectRides{
            {chrono::parseSqlDateTime("2020-09-23 00:00:00"),
             {
                 {wiki::social::feedback::Type::Address,
                  Counter{.waiting = 0,
                          .rejected = 0,
                          .accepted = 0,
                          .committed = 2}},
             }},
        };

        auto timelineBySource =
            count(feedbackTasks, committedFeedbackIds, feedbackIdToSignalMap);

        EXPECT_EQ(timelineBySource.at(Source::Agents), expectAgents);
        EXPECT_EQ(timelineBySource.at(Source::Rides), expectRides);
    }

    TEST_F(Fixture, test_min_signal)
    {
        struct TestDatum {
            chrono::TimePoint createdAt;
            Source source;
            db::Dataset dataset;
        };

        auto testData = std::vector<TestDatum>{
            {.createdAt = chrono::parseSqlDateTime("2020-09-28 15:44:00"),
             .source = Source::Rides,
             .dataset = db::Dataset::Rides},
            {.createdAt = chrono::parseSqlDateTime("2020-09-28 15:43:00"),
             .source = Source::Agents,
             .dataset = db::Dataset::Agents},
        };

        std::vector<Signal> expectedSignals;

        auto features = db::Features{};
        for (const auto& testData : testData) {
            expectedSignals.push_back({
                .createdAt = testData.createdAt,
                .source = testData.source});
            features.push_back(
                sql_chemistry::GatewayAccess<db::Feature>::construct()
                    .setSourceId("sourceId")
                    .setGeodeticPos(geolib3::Point2{0, 0})
                    .setHeading(geolib3::Heading{0})
                    .setTimestamp(testData.createdAt)
                    .setMdsKey({"1234", "abc"})
                    .setDataset(testData.dataset));
        }
        {
            auto txn = pool().masterWriteableTransaction();
            db::FeatureGateway{*txn}.insert(features);
            txn->commit();
        }

        auto sign = sql_chemistry::GatewayAccess<db::Sign>::construct()
                        .setType(db::Sign::Type::PriorityGiveWay)
                        .setGeodeticPos(geolib3::Point2{0, 0}, 0)
                        .setHeading(geolib3::Heading{0}, geolib3::Degrees{0});
        {
            auto txn = pool().masterWriteableTransaction();
            db::SignGateway{*txn}.insert(sign);
            txn->commit();
        }

        auto signFeatures = db::SignFeatures{};
        for (const auto& feature : features) {
            signFeatures.emplace_back(feature.id(), 0, 0, 0, 0)
                .setSignId(sign.id());
        }
        {
            auto txn = pool().masterWriteableTransaction();
            db::SignFeatureGateway{*txn}.insert(signFeatures);
            txn->commit();
        }

        auto hypothesis = db::Hypothesis(db::HypothesisType::TrafficSign,
                                         geolib3::Point2{0, 0},
                                         0,
                                         0,
                                         sign.id())
                              .setFeedbackTaskId(1);
        {
            auto txn = pool().masterWriteableTransaction();
            db::HypothesisGateway{*txn}.insert(hypothesis);
            txn->commit();
        }

        auto feedbackIdToSignalMap = loadFeedbackIdToSignalMap(pool());
        EXPECT_EQ(feedbackIdToSignalMap.at(hypothesis.feedbackTaskId()),
                  *std::min_element(expectedSignals.begin(), expectedSignals.end()));
    }

}  // namespace maps::mrc::feedback_stat::tests
