#include "maps/wikimap/mapspro/tools/ymapsdf-conversion/json2ymapsdf/lib/work/isocode/ad_geometry_builder.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 AdGeometryBuilderFixture: public YmapsdfFixture {
public:
    AdGeometryBuilderFixture()
        : YmapsdfFixture(
            globalFixture().pool().getMasterConnection(),
            "ad_geometry_builder", // YmapsdfFixture::SCHEMA
            YMAPSDF_CREATE_FILE)
    {
        applyQuery("CREATE SCHEMA " + OUT_SCHEMA);
        applySqlFile(YMAPSDF_CREATE_FILE, OUT_SCHEMA);
    }

    ~AdGeometryBuilderFixture() {
        applyQuery("DROP SCHEMA IF EXISTS " + OUT_SCHEMA + " CASCADE;");
    }

    const std::string OUT_SCHEMA = "output";
};


const auto PERIMETER_TOLERANCE = 0.9;
const size_t LEVEL_KIND = 1;

} // namespace


BOOST_FIXTURE_TEST_SUITE(main, AdGeometryBuilderFixture)

using json2ymapsdf::isocode::AdGeometryBuilder;

BOOST_AUTO_TEST_CASE(shouldBuildAdGeometry_simple) {
    applyQueryToSchema(
        SCHEMA,
        "INSERT INTO ad (ad_id, p_ad_id, level_kind, g_ad_id) VALUES"
        "(1, NULL, 1, NULL);"
        "INSERT INTO ad_face (ad_id, face_id, is_interior) VALUES"
        "(1, 1, false);"
        "INSERT INTO face_edge (face_id, edge_id) VALUES"
        "(1, 1);"
        "INSERT INTO edge (edge_id, f_node_id, t_node_id, shape) VALUES"
        "(1, 1, 1, ST_GeomFromText('LINESTRING(1 1, 3 1, 2 2, 1 1)', 4326));"
    );

    AdGeometryBuilder adGeomBuilder(
        globalFixture().pool(),
        SCHEMA,
        OUT_SCHEMA);

    adGeomBuilder.build(LEVEL_KIND);
    BOOST_CHECK(
        compareQueries(
            "SELECT ST_BuildArea(ST_Collect(shape)) FROM " + SCHEMA + ".edge",
            "SELECT shape FROM " + OUT_SCHEMA + ".ad_geom"));
    adGeomBuilder.check(LEVEL_KIND, PERIMETER_TOLERANCE);
}


BOOST_AUTO_TEST_CASE(shouldBuildAdGeometry_donut) {
    applyQueryToSchema(
        SCHEMA,
        "INSERT INTO ad (ad_id, p_ad_id, level_kind, g_ad_id) VALUES"
        "(1, NULL, 1, NULL);"
        "INSERT INTO ad_face (ad_id, face_id, is_interior) VALUES"
        "(1, 1, false),"
        "(1, 2, true);"
        "INSERT INTO face_edge (face_id, edge_id) VALUES"
        "(1, 1),"
        "(2, 2);"
        "INSERT INTO edge (edge_id, f_node_id, t_node_id, shape) VALUES"
        "(1, 1, 1, ST_GeomFromText('LINESTRING(0 0, 6 0, 3 3, 0 0)', 4326)),"
        "(2, 2, 2, ST_GeomFromText('LINESTRING(2 1, 4 1, 3 2, 2 1)', 4326));"
    );

    AdGeometryBuilder adGeomBuilder(
        globalFixture().pool(),
        SCHEMA,
        OUT_SCHEMA);

    adGeomBuilder.build(LEVEL_KIND);
    BOOST_CHECK(
        compareQueries(
            "SELECT ST_BuildArea(ST_Collect(shape)) FROM " + SCHEMA + ".edge",
            "SELECT shape FROM " + OUT_SCHEMA + ".ad_geom"));
    adGeomBuilder.check(LEVEL_KIND, PERIMETER_TOLERANCE);
}


BOOST_AUTO_TEST_CASE(shouldBuildAdGeometry_islands) {
    applyQueryToSchema(
        SCHEMA,
        "INSERT INTO ad (ad_id, p_ad_id, level_kind, g_ad_id) VALUES"
        "(1, NULL, 1, NULL);"
        "INSERT INTO ad_face (ad_id, face_id, is_interior) VALUES"
        "(1, 1, false),"
        "(1, 2, false);"
        "INSERT INTO face_edge (face_id, edge_id) VALUES"
        "(1, 1),"
        "(2, 2);"
        "INSERT INTO edge (edge_id, f_node_id, t_node_id, shape) VALUES"
        "(1, 1, 1, ST_GeomFromText('LINESTRING(0 0, 0 2, 1 1, 0 0)', 4326)),"
        "(2, 2, 2, ST_GeomFromText('LINESTRING(2 1, 4 1, 3 2, 2 1)', 4326));"
    );

    AdGeometryBuilder adGeomBuilder(
        globalFixture().pool(),
        SCHEMA,
        OUT_SCHEMA);

    adGeomBuilder.build(LEVEL_KIND);
    BOOST_CHECK(
        compareQueries(
            "SELECT ST_BuildArea(ST_Collect(shape)) FROM " + SCHEMA + ".edge",
            "SELECT shape FROM " + OUT_SCHEMA + ".ad_geom"));
    adGeomBuilder.check(LEVEL_KIND, PERIMETER_TOLERANCE);
}


BOOST_AUTO_TEST_CASE(shouldBuildAdGeometry_severalAds) {
    applyQueryToSchema(
        SCHEMA,
        "INSERT INTO ad (ad_id, p_ad_id, level_kind, g_ad_id) VALUES"
        "(1, NULL, 1, NULL),"
        "(2, NULL, 1, NULL);"
        "INSERT INTO ad_face (ad_id, face_id, is_interior) VALUES"
        "(1, 1, false),"
        "(2, 2, false);"
        "INSERT INTO face_edge (face_id, edge_id) VALUES"
        "(1, 1),"
        "(2, 2);"
        "INSERT INTO edge (edge_id, f_node_id, t_node_id, shape) VALUES"
        "(1, 1, 1, ST_GeomFromText('LINESTRING(0 0, 0 2, 1 1, 0 0)', 4326)),"
        "(2, 2, 2, ST_GeomFromText('LINESTRING(2 1, 4 1, 3 2, 2 1)', 4326));"
    );

    AdGeometryBuilder adGeomBuilder(
        globalFixture().pool(),
        SCHEMA,
        OUT_SCHEMA);

    adGeomBuilder.build(LEVEL_KIND);
    BOOST_CHECK(
        compareQueries(
            "SELECT ST_BuildArea(ST_Collect(shape)) FROM " + SCHEMA + ".edge WHERE edge_id = 1",
            "SELECT shape FROM " + OUT_SCHEMA + ".ad_geom WHERE ad_id = 1"));
    BOOST_CHECK(
        compareQueries(
            "SELECT ST_BuildArea(ST_Collect(shape)) FROM " + SCHEMA + ".edge WHERE edge_id = 2",
            "SELECT shape FROM " + OUT_SCHEMA + ".ad_geom WHERE ad_id = 2"));
    adGeomBuilder.check(LEVEL_KIND, PERIMETER_TOLERANCE);
}


BOOST_AUTO_TEST_CASE(shouldBuildAdGeometry_byLevelKind) {
    applyQueryToSchema(
        SCHEMA,
        "INSERT INTO ad (ad_id, p_ad_id, level_kind, g_ad_id) VALUES"
        "(1, NULL, 1, NULL),"
        "(2, NULL, 2, NULL);"
        "INSERT INTO ad_face (ad_id, face_id, is_interior) VALUES"
        "(1, 1, false),"
        "(2, 2, false);"
        "INSERT INTO face_edge (face_id, edge_id) VALUES"
        "(1, 1),"
        "(2, 2);"
        "INSERT INTO edge (edge_id, f_node_id, t_node_id, shape) VALUES"
        "(1, 1, 1, ST_GeomFromText('LINESTRING(0 0, 6 0, 3 3, 0 0)', 4326)),"
        "(2, 2, 2, ST_GeomFromText('LINESTRING(2 1, 4 1, 3 2, 2 1)', 4326));"
    );

    AdGeometryBuilder adGeomBuilder(
        globalFixture().pool(),
        SCHEMA,
        OUT_SCHEMA);

    adGeomBuilder.build(LEVEL_KIND);
    BOOST_CHECK(
        compareQueries(
            "SELECT ST_BuildArea(ST_Collect(shape)) FROM " + SCHEMA + ".edge WHERE edge_id = 1",
            "SELECT shape FROM " + OUT_SCHEMA + ".ad_geom WHERE ad_id = 1"));
    adGeomBuilder.check(LEVEL_KIND, PERIMETER_TOLERANCE);

    adGeomBuilder.build(2);
    BOOST_CHECK(
        compareQueries(
            "SELECT ST_BuildArea(ST_Collect(shape)) FROM " + SCHEMA + ".edge WHERE edge_id = 2",
            "SELECT shape FROM " + OUT_SCHEMA + ".ad_geom WHERE ad_id = 2"));
    adGeomBuilder.check(2, PERIMETER_TOLERANCE);
}


BOOST_AUTO_TEST_CASE(shouldBuildAdGeometry_withoutAlternativeAdId) {
    applyQueryToSchema(
        SCHEMA,
        "INSERT INTO ad (ad_id, p_ad_id, level_kind, g_ad_id) VALUES"
        "(1, NULL, 1, NULL),"
        "(2, NULL, 1, 1);"
        "INSERT INTO ad_face (ad_id, face_id, is_interior) VALUES"
        "(1, 1, false),"
        "(2, 2, false);"
        "INSERT INTO face_edge (face_id, edge_id) VALUES"
        "(1, 1),"
        "(2, 2);"
        "INSERT INTO edge (edge_id, f_node_id, t_node_id, shape) VALUES"
        "(1, 1, 1, ST_GeomFromText('LINESTRING(0 0, 0 2, 1 1, 0 0)', 4326)),"
        "(2, 2, 2, ST_GeomFromText('LINESTRING(2 1, 4 1, 3 2, 2 1)', 4326));"
    );

    AdGeometryBuilder adGeomBuilder(
        globalFixture().pool(),
        SCHEMA,
        OUT_SCHEMA);

    adGeomBuilder.build(LEVEL_KIND);
    BOOST_CHECK(
        compareQueries(
            "SELECT ST_BuildArea(ST_Collect(shape)) FROM " + SCHEMA + ".edge WHERE edge_id = 1",
            "SELECT shape FROM " + OUT_SCHEMA + ".ad_geom WHERE ad_id = 1"));
    adGeomBuilder.check(LEVEL_KIND, PERIMETER_TOLERANCE);

    BOOST_CHECK_EQUAL(1, applyQueryToSchema(OUT_SCHEMA, "SELECT * FROM ad_geom").size());
}


BOOST_AUTO_TEST_SUITE_END()

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