# pylint: disable=no-self-use,unsubscriptable-object

import re

import pytest
from selenium.webdriver.common.by import By

from . import AbstractViewsTests, factory_build_layers, get_test_default_layers, skip_if_ci
from .selenium.page import IndexPage


@pytest.fixture(scope="function")
@pytest.mark.usefixtures("dbsession", "transact")
def layer_wms_test_data(dbsession, transact):
    del transact

    from c2cgeoportal_commons.models.main import LayerWMS, OGCServer

    servers = [OGCServer(name="server_{}".format(i)) for i in range(0, 4)]
    for i, server in enumerate(servers):
        server.url = "http://wms.geo.admin.ch_{}".format(i)
        server.image_type = "image/jpeg" if i % 2 == 0 else "image/png"

    def layer_builder(i):
        layer = LayerWMS(name="layer_wms_{}".format(i))
        layer.layer = "layer_{}".format(i)
        layer.public = 1 == i % 2
        layer.geo_table = "geotable_{}".format(i)
        layer.ogc_server = servers[i % 4]
        layer.style = "décontrasté"
        return layer

    datas = factory_build_layers(layer_builder, dbsession)
    datas["servers"] = servers
    datas["default"] = get_test_default_layers(dbsession, servers[1])

    dbsession.flush()

    yield datas


@pytest.mark.usefixtures("layer_wms_test_data", "test_app")
class TestLayerWMSViews(AbstractViewsTests):

    _prefix = "/layers_wms"

    def test_index_rendering(self, test_app):
        resp = self.get(test_app)

        self.check_left_menu(resp, "WMS Layers")

        expected = [
            ("actions", "", "false"),
            ("id", "id", "true"),
            ("name", "Name"),
            ("description", "Description"),
            ("public", "Public"),
            ("geo_table", "Geo table"),
            ("exclude_properties", "Exclude properties"),
            ("layer", "WMS layer name"),
            ("style", "Style"),
            ("time_mode", "Time mode"),
            ("time_widget", "Time widget"),
            ("ogc_server", "OGC server"),
            ("dimensions", "Dimensions", "false"),
            ("interfaces", "Interfaces"),
            ("restrictionareas", "Restriction areas", "false"),
            ("parents_relation", "Parents", "false"),
            ("metadatas", "Metadatas", "false"),
        ]
        self.check_grid_headers(resp, expected)

    def test_grid_complex_column_val(self, test_app, layer_wms_test_data):
        json = self.check_search(test_app, sort="name", total=26)

        row = json["rows"][0]
        layer = layer_wms_test_data["layers"][0]

        assert layer.id == int(row["_id_"])
        assert layer.name == row["name"]
        assert "restrictionarea_0, restrictionarea_2" == row["restrictionareas"]
        assert "server_0" == row["ogc_server"]
        assert "desktop, edit" == row["interfaces"]
        assert "Date: 2017, 1988; CLC: all" == row["dimensions"]
        assert "layer_group_0, layer_group_3" == row["parents_relation"]
        assert 'copyable: true, snappingConfig: {"tolerance": 50}' == row["metadatas"]

    def test_grid_sort_on_ogc_server(self, test_app, layer_wms_test_data):
        json = self.check_search(test_app, sort="ogc_server")
        for i, layer in enumerate(
            sorted(layer_wms_test_data["layers"], key=lambda layer: (layer.ogc_server.name, layer.id))
        ):
            if i == 10:
                break
            assert str(layer.id) == json["rows"][i]["_id_"]

    def test_grid_search(self, test_app):
        # check search on ogc_server.name
        self.check_search(test_app, "server_0", total=7)

        # check search on interfaces
        self.check_search(test_app, "mobile", total=9)

    def test_grid_empty_dimension(self, test_app, layer_wms_test_data):
        from c2cgeoportal_commons.models.main import Dimension

        layer = layer_wms_test_data["layers"][0]
        layer.dimensions.append(Dimension(name="Empty", value=None))
        json = self.check_search(test_app, layer.name, total=1)
        row = json["rows"][0]
        assert "Empty: NULL" in row["dimensions"]

    def test_new_no_default(self, test_app, layer_wms_test_data, dbsession):
        default_wms = layer_wms_test_data["default"]["wms"]
        default_wms.name = "so_can_I_not_be found"
        dbsession.flush()

        form = self.get_item(test_app, "new").form

        assert "" == self.get_first_field_named(form, "id").value
        assert "" == self.get_first_field_named(form, "name").value
        assert "" == self.get_first_field_named(form, "layer").value
        assert "" == self.get_first_field_named(form, "ogc_server_id").value
        assert "disabled" == self.get_first_field_named(form, "time_mode").value
        assert "slider" == self.get_first_field_named(form, "time_widget").value

    def test_new_default(self, test_app, layer_wms_test_data):
        default_wms = layer_wms_test_data["default"]["wms"]

        form = self.get_item(test_app, "new").form

        assert "" == self.get_first_field_named(form, "id").value
        assert "" == self.get_first_field_named(form, "name").value
        assert "" == self.get_first_field_named(form, "layer").value
        assert str(default_wms.ogc_server.id) == self.get_first_field_named(form, "ogc_server_id").value
        assert default_wms.time_mode == self.get_first_field_named(form, "time_mode").value
        assert default_wms.time_widget == self.get_first_field_named(form, "time_widget").value

    def test_base_edit(self, test_app, layer_wms_test_data):
        layer = layer_wms_test_data["layers"][10]

        form = self.get_item(test_app, layer.id).form

        assert "layer_wms_10" == self.get_first_field_named(form, "name").value
        assert "" == self.get_first_field_named(form, "description").value

    def test_public_checkbox_edit(self, test_app, layer_wms_test_data):
        layer = layer_wms_test_data["layers"][10]
        form = self.get_item(test_app, layer.id).form
        assert not form["public"].checked

        layer = layer_wms_test_data["layers"][11]
        form = self.get_item(test_app, layer.id).form
        assert form["public"].checked

    def test_edit(self, test_app, layer_wms_test_data, dbsession):
        layer = layer_wms_test_data["layers"][0]

        form = self.get_item(test_app, layer.id).form

        assert str(layer.id) == self.get_first_field_named(form, "id").value
        assert "hidden" == self.get_first_field_named(form, "id").attrs["type"]
        assert layer.name == self.get_first_field_named(form, "name").value
        assert str(layer.description or "") == self.get_first_field_named(form, "description").value
        assert layer.public is False
        assert layer.public == form["public"].checked
        assert str(layer.geo_table or "") == form["geo_table"].value
        assert str(layer.exclude_properties or "") == form["exclude_properties"].value
        assert str(layer.ogc_server_id) == form["ogc_server_id"].value
        assert str(layer.layer or "") == form["layer"].value
        assert str(layer.style or "") == form["style"].value
        assert str(layer.time_mode) == form["time_mode"].value
        assert str(layer.time_widget) == form["time_widget"].value

        interfaces = layer_wms_test_data["interfaces"]
        assert set((interfaces[0].id, interfaces[2].id)) == set(i.id for i in layer.interfaces)
        self._check_interfaces(form, interfaces, layer)

        ras = layer_wms_test_data["restrictionareas"]
        assert set((ras[0].id, ras[2].id)) == set(i.id for i in layer.restrictionareas)
        self._check_restrictionsareas(form, ras, layer)

        new_values = {
            "name": "new_name",
            "description": "new description",
            "public": True,
            "geo_table": "new_geo_table",
            "exclude_properties": "property1,property2",
            "ogc_server_id": str(layer_wms_test_data["servers"][1].id),
            "layer": "new_wmslayername",
            "style": "new_style",
            "time_mode": "range",
            "time_widget": "datepicker",
        }

        for key, value in new_values.items():
            self.set_first_field_named(form, key, value)
        form["interfaces"] = [interfaces[1].id, interfaces[3].id]
        form["restrictionareas"] = [ras[1].id, ras[3].id]

        resp = form.submit("submit")
        assert str(layer.id) == re.match(
            r"http://localhost{}/(.*)\?msg_col=submit_ok".format(self._prefix), resp.location
        ).group(1)

        dbsession.expire(layer)
        for key, value in new_values.items():
            if isinstance(value, bool):
                assert value == getattr(layer, key)
            else:
                assert str(value or "") == str(getattr(layer, key) or "")
        assert set([interfaces[1].id, interfaces[3].id]) == set(
            [interface.id for interface in layer.interfaces]
        )
        assert set([ras[1].id, ras[3].id]) == set([ra.id for ra in layer.restrictionareas])

    def test_submit_new(self, dbsession, test_app, layer_wms_test_data):
        from c2cgeoportal_commons.models.main import LayerWMS

        resp = test_app.post(
            "/layers_wms/new",
            {
                "name": "new_name",
                "description": "new description",
                "public": True,
                "geo_table": "new_geo_table",
                "exclude_properties": "property1,property2",
                "ogc_server_id": str(layer_wms_test_data["servers"][1].id),
                "layer": "new_wmslayername",
                "style": "new_style",
                "time_mode": "range",
                "time_widget": "datepicker",
            },
            status=302,
        )

        layer = dbsession.query(LayerWMS).filter(LayerWMS.name == "new_name").one()
        assert str(layer.id) == re.match(
            r"http://localhost/layers_wms/(.*)\?msg_col=submit_ok", resp.location
        ).group(1)

    def test_duplicate(self, layer_wms_test_data, test_app, dbsession):
        from c2cgeoportal_commons.models.main import LayerWMS

        layer = layer_wms_test_data["layers"][3]

        resp = test_app.get("/layers_wms/{}/duplicate".format(layer.id), status=200)
        form = resp.form

        assert "" == self.get_first_field_named(form, "id").value
        assert layer.name == self.get_first_field_named(form, "name").value
        assert str(layer.description or "") == self.get_first_field_named(form, "description").value
        assert layer.public is True
        assert layer.public == form["public"].checked
        assert str(layer.geo_table or "") == form["geo_table"].value
        assert str(layer.exclude_properties or "") == form["exclude_properties"].value
        assert str(layer.ogc_server_id) == form["ogc_server_id"].value
        assert str(layer.layer or "") == form["layer"].value
        assert str(layer.style or "") == form["style"].value
        assert str(layer.time_mode) == form["time_mode"].value
        assert str(layer.time_widget) == form["time_widget"].value
        interfaces = layer_wms_test_data["interfaces"]
        assert set((interfaces[3].id, interfaces[1].id)) == set(i.id for i in layer.interfaces)
        self._check_interfaces(form, interfaces, layer)

        ras = layer_wms_test_data["restrictionareas"]
        assert set((ras[3].id, ras[0].id)) == set(i.id for i in layer.restrictionareas)
        self._check_restrictionsareas(form, ras, layer)

        self._check_dimensions(resp.html, layer.dimensions, duplicated=True)

        self.set_first_field_named(form, "name", "clone")
        resp = form.submit("submit")

        layer = dbsession.query(LayerWMS).filter(LayerWMS.name == "clone").one()
        assert str(layer.id) == re.match(
            r"http://localhost/layers_wms/(.*)\?msg_col=submit_ok", resp.location
        ).group(1)
        assert layer.id == layer.dimensions[0].layer_id
        assert layer.id == layer.metadatas[0].item_id
        assert layer_wms_test_data["layers"][3].metadatas[0].name == layer.metadatas[0].name
        assert layer_wms_test_data["layers"][3].metadatas[1].name == layer.metadatas[1].name

    def test_convert_common_fields_copied(self, layer_wms_test_data, test_app, dbsession):
        from c2cgeoportal_commons.models.main import LayerWMTS, LayerWMS

        layer = layer_wms_test_data["layers"][3]

        assert 0 == dbsession.query(LayerWMTS).filter(LayerWMTS.name == layer.name).count()
        assert 1 == dbsession.query(LayerWMS).filter(LayerWMS.name == layer.name).count()

        resp = test_app.post("/layers_wms/{}/convert_to_wmts".format(layer.id), status=200)
        assert resp.json["success"]
        assert "http://localhost/layers_wmts/{}?msg_col=submit_ok".format(layer.id) == resp.json["redirect"]

        assert 1 == dbsession.query(LayerWMTS).filter(LayerWMTS.name == layer.name).count()
        assert 0 == dbsession.query(LayerWMS).filter(LayerWMS.name == layer.name).count()

        resp = test_app.get(resp.json["redirect"], status=200)
        form = resp.form

        assert str(layer.id) == self.get_first_field_named(form, "id").value
        assert layer.name == self.get_first_field_named(form, "name").value
        assert str(layer.description or "") == self.get_first_field_named(form, "description").value
        assert layer.public is True
        assert layer.public == form["public"].checked
        assert str(layer.geo_table or "") == form["geo_table"].value
        assert str(layer.exclude_properties or "") == form["exclude_properties"].value
        assert str(layer.layer or "") == form["layer"].value
        assert str(layer.style or "") == form["style"].value
        assert layer_wms_test_data["default"]["wmts"].url == self.get_first_field_named(form, "url").value
        assert layer_wms_test_data["default"]["wmts"].matrix_set == form["matrix_set"].value

        interfaces = layer_wms_test_data["interfaces"]
        self._check_interfaces(form, interfaces, layer)
        ras = layer_wms_test_data["restrictionareas"]
        self._check_restrictionsareas(form, ras, layer)
        self._check_dimensions(resp.html, layer.dimensions)

        assert (
            "Your submission has been taken into account."
            == resp.html.find("div", {"class": "msg-lbl"}).getText()
        )

    def test_convert_image_type_from_ogcserver(self, layer_wms_test_data, test_app):
        layer = layer_wms_test_data["layers"][3]

        resp = test_app.post("/layers_wms/{}/convert_to_wmts".format(layer.id), status=200)
        assert resp.json["success"]
        assert "http://localhost/layers_wmts/{}?msg_col=submit_ok".format(layer.id) == resp.json["redirect"]

        resp = test_app.get(resp.json["redirect"], status=200)
        assert "image/png" == resp.form["image_type"].value

        layer = layer_wms_test_data["layers"][2]
        resp = test_app.post("/layers_wms/{}/convert_to_wmts".format(layer.id), status=200)
        assert resp.json["success"]
        assert "http://localhost/layers_wmts/{}?msg_col=submit_ok".format(layer.id) == resp.json["redirect"]

        resp = test_app.get(resp.json["redirect"], status=200)
        assert "image/jpeg" == resp.form["image_type"].value

    def test_convert_without_wmts_defaults(self, test_app, layer_wms_test_data, dbsession):
        from c2cgeoportal_commons.models.main import LayerWMTS

        dbsession.delete(LayerWMTS.get_default(dbsession))
        layer = layer_wms_test_data["layers"][3]
        test_app.post("/layers_wms/{}/convert_to_wmts".format(layer.id), status=200)

    def test_unicity_validator(self, layer_wms_test_data, test_app):
        layer = layer_wms_test_data["layers"][2]
        resp = test_app.get("/layers_wms/{}/duplicate".format(layer.id), status=200)

        resp = resp.form.submit("submit")

        self._check_submission_problem(resp, "{} is already used.".format(layer.name))

    def test_unicity_validator_does_not_matter_amongst_cousin(self, layer_wms_test_data, test_app, dbsession):
        from c2cgeoportal_commons.models.main import LayerWMS, LayerGroup

        assert 1 == dbsession.query(LayerGroup).filter(LayerGroup.name == "layer_group_0").count()

        assert dbsession.query(LayerWMS).filter(LayerWMS.name == "layer_group_0").one_or_none() is None

        layer = layer_wms_test_data["layers"][2]
        resp = test_app.get("/layers_wms/{}/duplicate".format(layer.id), status=200)
        self.set_first_field_named(resp.form, "name", "layer_group_0")
        resp = resp.form.submit("submit")

        # layer = dbsession.query(LayerWMS). \
        #     filter(LayerWMS.name == 'layer_group_0'). \
        #     one()
        # assert str(layer.id) == re.match('http://localhost/layers_wms/(.*)', resp.location).group(1)

    def test_delete(self, test_app, dbsession):
        from c2cgeoportal_commons.models.main import LayerWMS, Layer, TreeItem

        layer_id = dbsession.query(LayerWMS.id).first().id

        test_app.delete("/layers_wms/{}".format(layer_id), status=200)

        assert dbsession.query(LayerWMS).get(layer_id) is None
        assert dbsession.query(Layer).get(layer_id) is None
        assert dbsession.query(TreeItem).get(layer_id) is None

    @pytest.mark.skip(reason="Contraint has to be added at model side, alambiced")
    def test_submit_new_no_layer_name(self, test_app):
        resp = test_app.post(
            "/layers_wms/new",
            {
                "name": "new_name",
                "description": "new description",
                "public": True,
                "geo_table": "new_geo_table",
                "exclude_properties": "property1,property2",
                "ogc_server_id": str(layer_wms_test_data["servers"][1].id),
                "style": "new_style",
                "time_mode": "range",
                "time_widget": "datepicker",
            },
            status=200,
        )

        assert (
            "There was a problem with your submission"
            == resp.html.select_one('div[class="error-msg-lbl"]').text
        )
        assert (
            "Errors have been highlighted below" == resp.html.select_one('div[class="error-msg-detail"]').text
        )
        assert ["WMS layer name"] == sorted(
            [(x.select_one("label").text.strip()) for x in resp.html.select("[class~='has-error']")]
        )


@skip_if_ci
@pytest.mark.selenium
@pytest.mark.usefixtures("selenium", "selenium_app", "layer_wms_test_data")
class TestLayerWMSSelenium:

    _prefix = "/layers_wms"

    def test_index(self, selenium, selenium_app, layer_wms_test_data):
        selenium.get(selenium_app + self._prefix)

        layer = layer_wms_test_data["layers"][5]
        index_page = IndexPage(selenium)
        index_page.select_language("en")
        index_page.check_pagination_info("Showing 1 to 25 of 26 rows", 10)
        index_page.select_page_size(10)
        index_page.check_pagination_info("Showing 1 to 10 of 26 rows", 10)
        index_page.wait_jquery_to_be_active()

        el = index_page.find_element(
            By.XPATH, '//td[contains(text(),"{}")]'.format(layer.geo_table), timeout=5
        )
        index_page.dbl_click(el)

        assert selenium.current_url.endswith("/layers_wms/{}".format(layer.id))
