#include "test_utils.h"

#include <library/cpp/testing/gtest/gtest.h>
#include <library/cpp/testing/common/env.h>
#include <maps/wikimap/mapspro/services/mrc/libs/position_improvment/impl/match_function.h>

namespace maps::mrc::pos_improvment::tests {

TEST(match_function_tests, test_match_function_perfect_input)
{
    std::vector<double> f {5, 5, 5, 5, 5, 5};

    std::vector<double> h {5, 5, 5, 5, 5, 5};
    std::vector<double> g {1, 1, 1, 1, 1, 1};

    EXPECT_THAT(matchFunction(h, g, 10), testing::Eq(f));
}

TEST(match_function_tests, test_match_function1)
{
    std::vector<double> f {5, 5, 5, 5, 5, 5};

    std::vector<double> h {5, 5, 4, 5, 7, 5};
    std::vector<double> g {0, 0, 0, 0, 0, 0};

    EXPECT_THAT(matchFunction(h, g, 10), testing::Eq(f));
}

TEST(match_function_tests, test_match_function2)
{
    std::vector<double> f {5, 5, 5, 5, 5, 5};

    std::vector<double> h {5, 5, 10000000, 5, 5, 5};
    std::vector<double> g {-1, -1, -1, -1, -1, -1};

    EXPECT_THAT(matchFunction(h, g, 3), testing::Eq(f));
}

TEST(match_function_tests, test_match_function3)
{
    std::vector<double> f {5, 5, 5, 5, 5, 5};

    std::vector<double> h {5, 5, 9, 9, 5, 5};
    std::vector<double> g {1, 1, 1, 1, 1, 1};

    EXPECT_THAT(matchFunction(h, g, 5), testing::Eq(f));
}

TEST(match_function_tests, test_match_function_ascending)
{
    std::vector<double> f {10, 11, 12, 16, 17, 18};

    std::vector<double> h {10, 10, 12, 16, 17, 12};
    std::vector<double> g {20, 21, 22, 26, 27, 28};

    EXPECT_THAT(matchFunction(h, g, 10), testing::Eq(f));
}

TEST(match_function_tests, test_match_function_descending)
{
    std::vector<double> f {19, 17, 15, 13, 12, 11};

    std::vector<double> h {10, 17, 15, 13, 12, 20};
    std::vector<double> g {39, 37, 35, 33, 32, 31};

    EXPECT_THAT(matchFunction(h, g, 10), testing::Eq(f));
}

TEST(match_function_tests, test_match_function4)
{
    std::vector<double> f {1, 2, 3, 3, 2, 1};

    std::vector<double> h {1, 2, 3, 3, 6, 8};
    std::vector<double> g {4, 5, 6, 6, 5, 4};

    EXPECT_THAT(matchFunction(h, g, 10), testing::Eq(f));
}

TEST(match_function_tests, test_match_single_error_in_derivative)
{
    std::vector<double> f {1, 2, 3, 4, 5, 6};

    std::vector<double> h {1, 2, 3, 4, 5, 6};
    std::vector<double> g {3, 4, 5, 7, 8, 9}; // error between 5 and 7 points

    EXPECT_THAT(matchFunction(h, g, 5), testing::Eq(f));
}

TEST(match_function_tests, test_match_function_test_weight)
{
    std::vector<double> h {5, 7, 5, 7, 5, 5};
    std::vector<double> g {1, 1, 1, 1, 1, 1};

    std::vector<double> f1 {5, 5, 5, 5, 5, 5};
    EXPECT_THAT(matchFunction(h, g, 10), testing::Eq(f1));

    // h[x] items with value 7 have bigger weight
    ValueWeightFunction getWeight2 = [](int64_t, int64_t curIndex) {
        if (curIndex == 1 || curIndex == 3) {
            return 3;
        } else {
            return 1;
        }
    };
    std::vector<double> f2 {7, 7, 7, 7, 7, 7};
    EXPECT_THAT(matchFunction(h, g, 10, getWeight2), testing::Eq(f2));

    // this weight function reduces averaging
    ValueWeightFunction getWeight3 = [](int64_t baseIndex, int64_t curIndex) {
        if (curIndex == baseIndex) {
            return 6;
        } else {
            return 1;
        }
    };
    std::vector<double> f3 {5, 7, 5, 7, 5, 5};
    EXPECT_THAT(matchFunction(h, g, 10, getWeight3), testing::Eq(f3));
}

} // namespace maps::mrc::pos_improvment::tests
