#include "maps/wikimap/mapspro/tools/ymapsdf-conversion/json2ymapsdf/lib/work/isocode/propagate_ad.h"

#include <maps/wikimap/mapspro/tools/ymapsdf-conversion/json2ymapsdf/tests/fixtures/ymapsdf_fixture.h>

#include <yandex/maps/wiki/unittest/localdb.h>
#include <yandex/maps/wiki/unittest/query_helpers.h>
#include <boost/test/unit_test.hpp>

namespace maps {
namespace wiki {
namespace tests {
namespace {

unittest::RandomDatabaseFixture& globalFixture()
{
    static unittest::RandomDatabaseFixture fixture;
    return fixture;
}


class PropagateAdFixture: public YmapsdfFixture {
public:
    PropagateAdFixture()
        : YmapsdfFixture(
            globalFixture().pool().getMasterConnection(),
            "propagate_ad", // YmapsdfFixture::SCHEMA
            YMAPSDF_EXPORT_FILE)
    {
        // Remove constraints to make tests easier (no need to fill additional stuff).
        applyQueryToSchema(
            SCHEMA,
            "ALTER TABLE ad_center DROP CONSTRAINT ad_center_node_id_fkey;"
            "ALTER TABLE ad_face DROP CONSTRAINT ad_face_face_id_fkey;"
            "ALTER TABLE ad_face_patch DROP CONSTRAINT ad_face_patch_face_id_fkey;"
            "ALTER TABLE edge ALTER shape DROP NOT NULL;"
            "ALTER TABLE edge DROP CONSTRAINT edge_f_node_id_fkey;"
            "ALTER TABLE edge DROP CONSTRAINT edge_t_node_id_fkey;"
            "ALTER TABLE face_edge DROP CONSTRAINT face_edge_face_id_fkey;"
        );
    }
};

} // namespace


BOOST_FIXTURE_TEST_SUITE(main, PropagateAdFixture)

using json2ymapsdf::isocode::propagateAd;

BOOST_AUTO_TEST_CASE(shouldPropagateThroughAllLevels)
{
    applyQueryToSchema(
        SCHEMA,
        "INSERT INTO ad (ad_id, p_ad_id, level_kind, isocode) VALUES"
        "(1, NULL, 1, 'AA'),"
        "(2,    1, 2, NULL),"
        "(3,    2, 3, NULL),"
        "(4,    3, 4, NULL),"
        "(5,    4, 5, NULL),"
        "(6,    5, 6, NULL),"
        "(7,    6, 7, NULL);"
    );

    propagateAd(globalFixture().pool(), SCHEMA);

    BOOST_CHECK(hasIsocodes("ad", 1, {"AA"}));
    BOOST_CHECK(hasIsocodes("ad", 2, {"AA"}));
    BOOST_CHECK(hasIsocodes("ad", 3, {"AA"}));
    BOOST_CHECK(hasIsocodes("ad", 4, {"AA"}));
    BOOST_CHECK(hasIsocodes("ad", 5, {"AA"}));
    BOOST_CHECK(hasIsocodes("ad", 6, {"AA"}));
    BOOST_CHECK(hasIsocodes("ad", 7, {"AA"}));
}


BOOST_AUTO_TEST_CASE(shouldPropagateForDifferentCountries)
{
    applyQueryToSchema(
        SCHEMA,
        "INSERT INTO ad (ad_id, p_ad_id, level_kind, isocode) VALUES"
        "( 1, NULL, 1, 'AA'),"
        "( 2,    1, 2, NULL),"
        "( 3,    2, 3, NULL),"
        "(11, NULL, 1, 'BB'),"
        "(12,   11, 2, NULL),"
        "(13,   12, 3, NULL);"
    );

    propagateAd(globalFixture().pool(), SCHEMA);

    BOOST_CHECK(hasIsocodes("ad",  1, {"AA"}));
    BOOST_CHECK(hasIsocodes("ad",  2, {"AA"}));
    BOOST_CHECK(hasIsocodes("ad",  3, {"AA"}));
    BOOST_CHECK(hasIsocodes("ad", 11, {"BB"}));
    BOOST_CHECK(hasIsocodes("ad", 12, {"BB"}));
    BOOST_CHECK(hasIsocodes("ad", 13, {"BB"}));
}


BOOST_AUTO_TEST_CASE(shouldNotPropagateStartingFromNonCountryLevel)
{
    applyQueryToSchema(
        SCHEMA,
        "INSERT INTO ad (ad_id, p_ad_id, level_kind, isocode) VALUES"
        "(1, NULL, 2, 'AA'),"
        "(2,    1, 3, NULL);"
    );

    propagateAd(globalFixture().pool(), SCHEMA);

    BOOST_CHECK(hasIsocodes("ad", 1, {}));
    BOOST_CHECK(hasIsocodes("ad", 2, {}));
}


BOOST_AUTO_TEST_CASE(shouldUseTopLevelIsocodes)
{
    applyQueryToSchema(
        SCHEMA,
        "INSERT INTO ad (ad_id, p_ad_id, level_kind, isocode) VALUES"
        "(1, NULL, 1, 'AA'),"
        "(2,    1, 2, 'BB'),"
        "(3,    2, 3, NULL);"
    );

    propagateAd(globalFixture().pool(), SCHEMA);

    BOOST_CHECK(hasIsocodes("ad", 1, {"AA"}));
    BOOST_CHECK(hasIsocodes("ad", 2, {"AA"}));
    BOOST_CHECK(hasIsocodes("ad", 3, {"AA"}));
}


BOOST_AUTO_TEST_CASE(shouldPropagateAdDowntoNodes)
{
    applyQueryToSchema(
        SCHEMA,
        "INSERT INTO ad (ad_id, p_ad_id, level_kind, isocode) VALUES"
        "(1, NULL, 1, 'AA'),"
        "(2,    1, 2, NULL),"
        "(3, NULL, 1, 'BB');"
        "INSERT INTO ad_face (ad_id, face_id, is_interior) VALUES"
        "(1, 1, false),"
        "(2, 2, true),"
        "(3, 3, false);"
        "INSERT INTO edge (edge_id, f_node_id, t_node_id) VALUES"
        "(1, 1, 10),"
        "(2, 2, 20),"
        "(3, 3, 30);"
        "INSERT INTO face_edge (face_id, edge_id) VALUES"
        "(1, 1),"
        "(2, 2),"
        "(3, 3);"
    );

    propagateAd(globalFixture().pool(), SCHEMA);

    BOOST_CHECK(hasIsocodes("face", 1, {"AA"}));
    BOOST_CHECK(hasIsocodes("edge", 1, {"AA"}));
    BOOST_CHECK(hasIsocodes("node", 1, {"AA"}));
    BOOST_CHECK(hasIsocodes("node", 10, {"AA"}));

    BOOST_CHECK(hasIsocodes("face", 2, {"AA"}));
    BOOST_CHECK(hasIsocodes("edge", 2, {"AA"}));
    BOOST_CHECK(hasIsocodes("node", 2, {"AA"}));
    BOOST_CHECK(hasIsocodes("node", 20, {"AA"}));

    BOOST_CHECK(hasIsocodes("face", 3, {"BB"}));
    BOOST_CHECK(hasIsocodes("edge", 3, {"BB"}));
    BOOST_CHECK(hasIsocodes("node", 3, {"BB"}));
    BOOST_CHECK(hasIsocodes("node", 30, {"BB"}));
}


BOOST_AUTO_TEST_CASE(shouldPropagateAdToFacePatches)
{
    applyQueryToSchema(
        SCHEMA,
        "INSERT INTO ad (ad_id, p_ad_id, level_kind, isocode) VALUES"
        "(1, NULL, 1, 'AA'),"
        "(2,    1, 2, NULL),"
        "(3, NULL, 1, 'BB');"
        "INSERT INTO ad_face_patch (ad_id, face_id, is_excluded) VALUES"
        "(1, 1, false),"
        "(2, 2, true),"
        "(3, 3, false);"
    );

    propagateAd(globalFixture().pool(), SCHEMA);

    BOOST_CHECK(hasIsocodes("face", 1, {"AA"}));
    BOOST_CHECK(hasIsocodes("face", 2, {"AA"}));
    BOOST_CHECK(hasIsocodes("face", 3, {"BB"}));
}


BOOST_AUTO_TEST_CASE(shouldPropagateAdToAdCenter)
{
    applyQueryToSchema(
        SCHEMA,
        "INSERT INTO ad (ad_id, p_ad_id, level_kind, isocode) VALUES"
        "(1, NULL, 1, 'AA'),"
        "(2,    1, 2, NULL);"
        "INSERT INTO ad_center (ad_id, node_id) VALUES"
        "(1, 1),"
        "(2, 2);"
    );

    propagateAd(globalFixture().pool(), SCHEMA);

    BOOST_CHECK(hasIsocodes("node", 1, {"AA"}));
    BOOST_CHECK(hasIsocodes("node", 2, {"AA"}));
}


BOOST_AUTO_TEST_CASE(shouldPropagateSubstitutedIsocodesIfNoParent)
{
    // According to `g_ad_id` description, ADs with non-null `g_ad_id` must not
    // have child ADs nor any other related objects.
    applyQueryToSchema(
        SCHEMA,
        "INSERT INTO ad (ad_id, p_ad_id, level_kind, isocode, g_ad_id) VALUES"
        "(1, NULL, 1, 'AA', NULL),"
        "(2, NULL, 1, 'BB',    1),"
        "(3, NULL, 4, NULL,    1);"
    );

    propagateAd(globalFixture().pool(), SCHEMA);

    BOOST_CHECK(hasIsocodes("ad", 1, {"AA"}));
    BOOST_CHECK(hasIsocodes("ad", 2, {"AA"}));
    BOOST_CHECK(hasIsocodes("ad", 2, {"AA"}));
}


BOOST_AUTO_TEST_CASE(semiRealExample_Crimea)
{
    // see https://wiki.yandex-team.ru/maps/dev/core/wikimap/mapspro/krm/#ad2
    applyQueryToSchema(
        SCHEMA,
        "INSERT INTO ad (ad_id, p_ad_id, level_kind, isocode, g_ad_id) VALUES"
        "(1, NULL, 1, 'RU', NULL)," // Russia
        "(2, NULL, 1, 'UA', NULL)," // Ukraine
        "(3,    1, 2, NULL, NULL)," // Republic of Crimea (Russia's point of view)
        "(4,    2, 2, NULL,    3)," // Republic of Crimea (UN's point of view)
        "(5,    1, 2, 'XC',    3);" // Republic of Crimea (as an independent country)
    );

    propagateAd(globalFixture().pool(), SCHEMA);

    BOOST_CHECK(hasIsocodes("ad", 1, {"RU"}));
    BOOST_CHECK(hasIsocodes("ad", 2, {"UA"}));
    BOOST_CHECK(hasIsocodes("ad", 3, {"RU"}));
    BOOST_CHECK(hasIsocodes("ad", 4, {"UA"}));
    BOOST_CHECK(hasIsocodes("ad", 5, {"RU"}));
}


BOOST_AUTO_TEST_SUITE_END()

} // namespace tests
} // namespace wiki
} // namespace maps
