# Copyright (C) 2022-2023 HERE Global B.V. and its affiliate(s).
# All rights reserved.
#
# This software and other materials contain proprietary information
# controlled by HERE and are protected by applicable copyright legislation.
# Any use and utilization of this software and other materials and
# disclosure to any third parties is conditional upon having a separate
# agreement with HERE for the access, use, utilization or disclosure of this
# software. In the absence of such agreement, the use of the software is not
# allowed.

"""Tests for here.inspector common functions."""

import os

from here.inspector import KeplerInspector, inspect, options
from shapely.geometry import LinearRing, LineString, Point, Polygon

TEST_API_KEY_VAR_NAME = "DOCKER_ENV_HLS_API_KEY"
TEST_API_KEY = os.environ.get(TEST_API_KEY_VAR_NAME)


def test_default():
    options.api_key = None
    i = KeplerInspector()
    m = i.backend()

    print(m)


def test_shapely():
    from shapely.geometry import LineString, Point, Polygon

    pt1 = Point(30, 50)
    pt2 = Point(32, 49)
    ls1 = LineString([(29, 49), (30, 51), (31, 49), (32, 50), (33, 51)])
    poly1 = Polygon([(28, 46), (28, 52), (29, 53), (31, 53), (33, 48), (32, 47), (28, 46)])
    options.api_key = None
    i = KeplerInspector()
    i.add_features([pt1, pt2], "A")  # collection of shapely geometries
    i.add_features([pt2, ls1, poly1], "B")  # collection of shapely geometries
    i.add_features([poly1])  # collection of shapely geometries
    i.add_features([])  # empty collection
    m = i.backend()

    data = [(data, type(m.data[data])) for data in m.data]
    expected = [
        ("A", dict),
        ("B", dict),
        ("Unnamed 1", dict),
        ("Unnamed 2", dict),
    ]

    assert data == expected


def test_geojson_features():
    from geojson import Feature, LineString, Point, Polygon

    pt1 = Feature(geometry=Point((30, 50)))
    pt2 = Feature(geometry=Point((32, 49)))
    ls1 = Feature(geometry=LineString([(29, 49), (30, 51), (31, 49), (32, 50), (33, 51)]))
    poly1 = Feature(
        geometry=Polygon(
            [
                [(2.38, 57.322), (23.194, -20.28), (-120.43, 19.15), (2.38, 57.322)],
                [(-5.21, 23.51), (15.21, -10.81), (-20.51, 1.51), (-5.21, 23.51)],
            ]
        )
    )

    def to_dict(x):
        return dict(x.items())

    options.api_key = None
    i = KeplerInspector()
    i.add_features([pt1, pt2], "A")  # single GeoJSON features
    i.add_features([pt2, ls1, poly1], "B")  # single GeoJSON features
    i.add_features([poly1])  # one single GeoJSON feature
    i.add_features([])
    i.add_features(list(map(to_dict, [pt1, pt2])), "C")  # single GeoJSON feature dictionaries
    i.add_features(
        [to_dict(g) for g in [pt2, ls1, poly1]], "D"
    )  # single GeoJSON feature dictionaries
    i.add_features(to_dict(poly1), "E")  # one single GeoJSON feature dictionary
    i.add_features(
        [to_dict(poly1), pt1], "F"
    )  # mix of  single GeoJSON feature dictionary or GeoJSON features
    m = i.backend()

    data = [(data, type(m.data[data])) for data in m.data]
    expected = [
        ("A", dict),
        ("B", dict),
        ("Unnamed 1", dict),
        ("Unnamed 2", dict),
        ("C", dict),
        ("D", dict),
        ("E", dict),
        ("F", dict),
    ]

    assert data == expected


def test_geojson_collections():
    from geojson import Feature, FeatureCollection, LineString, Point, Polygon

    pt1 = Feature(geometry=Point((30, 50)))
    pt2 = Feature(geometry=Point((32, 49)))
    ls1 = Feature(geometry=LineString([(29, 49), (30, 51), (31, 49), (32, 50), (33, 51)]))
    poly1 = Feature(
        geometry=Polygon(
            [
                [(2.38, 57.322), (23.194, -20.28), (-120.43, 19.15), (2.38, 57.322)],
                [(-5.21, 23.51), (15.21, -10.81), (-20.51, 1.51), (-5.21, 23.51)],
            ]
        )
    )

    def to_dict(x):
        return dict(x.items())

    c1 = FeatureCollection([pt1, pt2, ls1, poly1])
    c2 = to_dict(c1)
    c3 = FeatureCollection([])
    options.api_key = None
    i = KeplerInspector()
    i.add_features(c1, "C1")  # single GeoJSON feature collection
    i.add_features(c2, "C2")  # single GeoJSON feature collection dictionary
    i.add_features(c3, "C3")  # empty GeoJSON feature collection
    m = i.backend()

    data = [(data, type(m.data[data])) for data in m.data]
    expected = [("C1", dict), ("C2", dict), ("C3", dict)]

    assert data == expected


def test_geoseries():
    from geopandas import GeoSeries
    from shapely.geometry import LineString, Point, Polygon

    pt1 = Point(30, 50)
    pt2 = Point(32, 49)
    ls1 = LineString([(29, 49), (30, 51), (31, 49), (32, 50), (33, 51)])
    poly1 = Polygon([(28, 46), (28, 52), (29, 53), (31, 53), (33, 48), (32, 47), (28, 46)])

    gs1 = GeoSeries([pt1, pt2, poly1])
    gs2 = GeoSeries([pt1, ls1, poly1])
    gs3 = GeoSeries([pt1, pt2])

    assert gs1.dtype == "geometry"
    assert gs2.dtype == "geometry"
    assert gs3.dtype == "geometry"
    options.api_key = None
    i = KeplerInspector()
    i.add_features(gs1, "A")  # a GeoSeries
    i.add_features(gs2)  # a GeoSeries
    i.add_features(gs3, "B")  # a GeoSeries
    m = i.backend()

    data = [(data, type(m.data[data])) for data in m.data]
    expected = [
        ("A", dict),
        ("Unnamed 1", dict),
        ("B", dict),
    ]

    assert data == expected


def test_geodataframe():
    from geopandas import GeoDataFrame
    from shapely.geometry import LineString, Point, Polygon

    pt1 = Point(30, 50)
    pt2 = Point(32, 49)
    ls1 = LineString([(29, 49), (30, 51), (31, 49), (32, 50), (33, 51)])
    poly1 = Polygon([(28, 46), (28, 52), (29, 53), (31, 53), (33, 48), (32, 47), (28, 46)])

    gdf1 = GeoDataFrame({"geometry": [pt1, pt2, poly1]})
    gdf2 = GeoDataFrame(geometry=[pt1, ls1, poly1])
    gdf3 = GeoDataFrame(geometry=[pt1, pt2])
    options.api_key = None
    i = KeplerInspector()
    i.add_features(gdf1, "A")  # a GeoDataFrame
    i.add_features(gdf2)  # a GeoDataFrame
    i.add_features(gdf3, "B")  # a GeoDataFrame
    m = i.backend()

    data = [(data, type(m.data[data])) for data in m.data]
    expected = [
        ("A", dict),
        ("Unnamed 1", dict),
        ("B", dict),
    ]

    assert data == expected


def test_tiling_grid():
    from shapely.geometry import Point

    pt1 = Point(30, 50)
    pt2 = Point(32, 49)

    tiles1 = [4, 5]
    tiles2 = [17, 18, 19]
    options.api_key = None
    i = KeplerInspector()
    i.add_tiles(tiles1, name="G1")
    i.add_tiles(tiles2, name="G2")
    i.add_features([pt1, pt2], "X")
    m = i.backend()

    data = [(data, type(m.data[data])) for data in m.data]
    expected = [("G1", dict), ("G2", dict), ("X", dict)]

    assert data == expected


def test_single_shapes_inspect():
    """Test render single Shapely BaseGeometry subclasses with inspect."""
    from geojson.utils import coords
    from shapely.geometry import LineString

    try:
        options.api_key = None
        options.inspector_class = KeplerInspector
        line = LineString([[35, 50], [35, 55]])
        m = inspect(features=line, center=line.centroid)
        layers = [m.data[layer] for layer in m.data]

        assert len(layers) == 1
    finally:
        options.inspector_class = None


def test_single_shapes_ipyleafletinspector():
    """Test render single Shapely BaseGeometry subclasses with IpyleafletInspector."""
    from geojson.utils import coords
    from shapely.geometry import LineString

    options.api_key = None
    line = LineString([[30, 50], [35, 55]])
    insp = KeplerInspector()
    insp.set_center(line.centroid)
    insp.set_zoom(10)
    insp.add_features(line)
    m = insp.backend()
    layers = [m.data[layer] for layer in m.data]
    assert len(layers) == 1


def test_add_point():
    """Test by adding single point with IpyleafletInspector."""
    options.api_key = None
    insp = KeplerInspector()
    pt1 = Point(30, 50)
    insp.add_features(pt1)
    m = insp.backend()
    data = [(data, type(m.data[data])) for data in m.data]
    expected = [
        ("Unnamed 1", dict),
    ]
    assert data == expected


def test_no_area_polygon():
    """Test for polygon having 0 area and only one point with IpyleafletInspector."""
    options.api_key = None
    insp = KeplerInspector()
    poly1 = Polygon([(28, 46), (28, 46), (28, 46), (28, 46), (28, 46)])
    insp.add_features([poly1])  # collection of shapely geometries
    m = insp.backend()
    data = [(data, type(m.data[data])) for data in m.data]
    expected = [
        ("Unnamed 1", dict),
    ]
    assert data == expected


def test_no_length_line():
    """Test with line with 0 length"""
    options.api_key = None
    insp = KeplerInspector()
    line = LineString([[35, 35], [35, 35]])
    insp.add_features(line)
    m = insp.backend()
    data = [(data, type(m.data[data])) for data in m.data]
    expected = [
        ("Unnamed 1", dict),
    ]
    assert data == expected


def test_no_linearring():
    """Test with 0 area linear ring"""
    options.api_key = None
    insp = KeplerInspector()
    linear_ring = LinearRing(
        [
            (13, 13),
            (13, 13),
            (13, 13),
        ]
    )
    insp.add_features(linear_ring)
    m = insp.backend()
    data = [(data, type(m.data[data])) for data in m.data]
    expected = [("Unnamed 1", dict)]
    assert data == expected
