#include <maps/wikimap/mapspro/services/mrc/toloka/tools/detection_result_handler/tool.h>

#define BOOST_TEST_DYN_LINK
#define BOOST_AUTO_TEST_MAIN
#include <boost/test/auto_unit_test.hpp>

namespace maps {
namespace geolib3 {

bool operator==(const BoundingBox& box1, const BoundingBox& box2)
{
    constexpr double EPS = 1e-6;
    return abs(box1.minX() - box2.minX()) < EPS &&
            abs(box1.minY() - box2.minY()) < EPS &&
            abs(box1.maxX() - box2.maxX()) < EPS &&
            abs(box1.maxY() - box2.maxY()) < EPS;
}

} // geolib3

namespace mrc {
namespace toloka {
namespace test {

namespace {

const std::string TS_1 = "task-suite-id-1";
const std::string TS_2 = "task-suite-id-2";

const std::string POOL_ID = "1";

const std::string USER_1 = "user-id-1";
const std::string USER_2 = "user-id-2";
const std::string USER_3 = "user-id-3";

const std::string SOURCE_1 = "source-1";
const std::string SOURCE_2 = "source-2";
const std::string SOURCE_3 = "source-3";
const std::string SOURCE_4 = "source-4";

const auto ACCEPTED = io::AssignmentStatus::Accepted;

constexpr size_t OVERLAP = 3;

Rectangle rect(double x1, double y1, double x2, double y2)
{
    return Rectangle{geolib3::Point2(x1, y1), geolib3::Point2(x2, y2)};
}

} // anonymous namespace

BOOST_AUTO_TEST_CASE(merge_task_suites)
{
    IdToTaskSuite idToTaskSuite {
        {TS_1, {TS_1, POOL_ID, OVERLAP, {{SOURCE_1}, {SOURCE_2}}}},
        {TS_2, {TS_2, POOL_ID, OVERLAP, {{SOURCE_3}, {SOURCE_4}}}}
    };

    TaskSuiteIdToResults taskSuiteIdToResults {
        // 3 results for task suite 1:
        {TS_1, {
            {
                // Result from user 1
                TS_1, "unused", ACCEPTED, USER_1,
                {
                    TaskInput{SOURCE_1},
                    TaskInput{SOURCE_2}
                },
                {
                    TaskOutput{{rect(10, 10, 20, 20), rect(30, 30, 50, 50)}},
                    TaskOutput{{rect(100, 200, 150, 260), rect(300, 300, 400, 400)}}
                }
            },
            {
                // Result from user 2
                TS_1, "unused", ACCEPTED, USER_2,
                {
                    TaskInput{SOURCE_1},
                    TaskInput{SOURCE_2}
                },
                {
                    TaskOutput{{rect(10.5, 11.5, 20.5, 21), rect(29, 29, 52, 52)}},
                    TaskOutput{{rect(105, 205, 145, 255), rect(350, 350, 450, 450)}}
                }
            },
            {
                // Result from user 3
                TS_1, "unused", ACCEPTED, USER_3,
                {
                    TaskInput{SOURCE_1},
                    TaskInput{SOURCE_2}
                },
                {
                    TaskOutput{{}},
                    TaskOutput{{rect(95, 195, 145, 261)}}
                }
            }
        }},
        // Results for task suite 2 (only 2 users accomplished)
        {TS_2, {
            {
                TS_2, "unused", ACCEPTED, USER_1,
                {
                    TaskInput{SOURCE_3},
                    TaskInput{SOURCE_4}
                },
                {
                    TaskOutput{{rect(50, 50, 150, 150)}},
                    TaskOutput{{rect(100, 100, 300, 300)}}
                }
            },
            {
                TS_2, "unused", ACCEPTED, USER_2,
                {
                    TaskInput{SOURCE_3},
                    TaskInput{SOURCE_4}
                },
                {
                    TaskOutput{{rect(50, 50, 150, 150)}},
                    TaskOutput{{rect(100, 100, 300, 300)}}
                }
            }
        }}
    };

    auto tsResults = mergeTasksResults(idToTaskSuite, taskSuiteIdToResults);

    // Validate results.
    // Aggregated results are available only for task suite 1, but not for
    // task suite 2, where not enough users have accomplished the assignments

    BOOST_CHECK_EQUAL(tsResults.size(), 1);
    const auto& taskResults = tsResults[0].taskResults;

    auto findTaskResult = [&](const std::string& src) {
        return std::find_if(taskResults.begin(), taskResults.end(),
            [&](const TaskResult& tr) { return tr.input.source == src; }
        );
    };

    auto task1Itr = findTaskResult(SOURCE_1);
    auto task2Itr = findTaskResult(SOURCE_2);

    BOOST_ASSERT(task1Itr != taskResults.end());
    BOOST_ASSERT(task1Itr->output.rectangles.size() == 2);
    BOOST_CHECK_EQUAL(task1Itr->output.rectangles[0], rect(10.5, 11.5, 20.5, 21));
    BOOST_CHECK_EQUAL(task1Itr->output.rectangles[1], rect(30, 30, 52, 52));

    BOOST_ASSERT(task2Itr != taskResults.end());
    BOOST_ASSERT(task2Itr->output.rectangles.size() == 1);
    BOOST_CHECK_EQUAL(task2Itr->output.rectangles[0], rect(100, 200, 145, 260));
}

BOOST_AUTO_TEST_CASE(merge_task_suites_2)
{
    IdToTaskSuite idToTaskSuite {
        {TS_1, {TS_1, POOL_ID, OVERLAP, {{SOURCE_1}, {SOURCE_2}}}}
    };

    TaskSuiteIdToResults taskSuiteIdToResults {
        {TS_1, {
            {
                // Result from user 1
                TS_1, "unused", ACCEPTED, USER_1,
                { TaskInput{SOURCE_1} },
                { TaskOutput{{rect(100, 200, 150, 260), rect(100, 200, 150, 260)}} }
            },
            {
                // Result from user 2
                TS_1, "unused", ACCEPTED, USER_2,
                { TaskInput{SOURCE_1} },
                { TaskOutput{{rect(500, 500, 600, 600)}} }
            },
            {
                // Result from user 3
                TS_1, "unused", ACCEPTED, USER_3,
                { TaskInput{SOURCE_1} },
                { TaskOutput{{rect(700, 700, 800, 800)}} }
            }
        }}
    };

    auto tsResults = mergeTasksResults(idToTaskSuite, taskSuiteIdToResults);

    // Validate results.
    // User 1 pointed the same area 2 times. It will not be counted
    BOOST_ASSERT(!tsResults.empty());
    const auto& taskResults = tsResults[0].taskResults;
    BOOST_ASSERT(!taskResults.empty());
    BOOST_CHECK_EQUAL(taskResults[0].input.source, SOURCE_1);
    BOOST_CHECK(taskResults[0].output.rectangles.empty());
}


} // test
} // toloka
} // mrc
} // maps
