"""Tests for the plotlymap module."""

import unittest

import pandas as pd

try:
    from plotly import graph_objects as go
    from geemap import plotlymap

    PLOTLY_AVAILABLE = True
except ImportError:
    PLOTLY_AVAILABLE = False


@unittest.skipUnless(PLOTLY_AVAILABLE, "plotly not available")
class PlotlymapTest(unittest.TestCase):

    def test_map_init_default_params(self):
        m = plotlymap.Map(ee_initialize=False)
        self.assertEqual(m.layout.mapbox.center.lat, 20)
        self.assertEqual(m.layout.mapbox.center.lon, 0)
        self.assertEqual(m.layout.mapbox.zoom, 1)
        self.assertEqual(m.layout.mapbox.style, "open-street-map")
        self.assertEqual(m.layout.height, 600)
        self.assertEqual(len(m.data), 1)
        self.assertEqual(m.get_layers(), {})
        self.assertEqual(m.get_tile_layers(), {})
        self.assertEqual(m.get_data_layers(), {})
        self.assertIsNone(m.find_layer_index("nonexistent"))

    def test_map_init_custom_center(self):
        m = plotlymap.Map(center=(40, -100), ee_initialize=False)
        self.assertEqual(m.layout.mapbox.center.lat, 40)
        self.assertEqual(m.layout.mapbox.center.lon, -100)

    def test_map_init_custom_zoom(self):
        m = plotlymap.Map(zoom=10, ee_initialize=False)
        self.assertEqual(m.layout.mapbox.zoom, 10)

    def test_map_init_custom_height(self):
        m = plotlymap.Map(height=800, ee_initialize=False)
        self.assertEqual(m.layout.height, 800)

    def test_map_init_custom_basemap(self):
        m = plotlymap.Map(basemap="carto-positron", ee_initialize=False)
        self.assertEqual(m.layout.mapbox.style, "carto-positron")

    def test_set_center_with_zoom(self):
        m = plotlymap.Map(ee_initialize=False)
        m.set_center(lat=37.8, lon=-122.4, zoom=12)
        self.assertEqual(m.layout.mapbox.center.lat, 37.8)
        self.assertEqual(m.layout.mapbox.center.lon, -122.4)
        self.assertEqual(m.layout.mapbox.zoom, 12)

    def test_set_center_without_zoom(self):
        m = plotlymap.Map(ee_initialize=False)
        m.set_center(lat=37.8, lon=-122.4)
        self.assertEqual(m.layout.mapbox.center.lat, 37.8)
        self.assertEqual(m.layout.mapbox.center.lon, -122.4)
        self.assertEqual(m.layout.mapbox.zoom, 1)

    def test_add_controls_string_and_list(self):
        m = plotlymap.Map(ee_initialize=False)
        m.add_controls("drawline")
        self.assertIn("drawline", m.layout.modebar.add)
        m.add_controls(["drawline", "drawopenpath"])
        self.assertIn("drawopenpath", m.layout.modebar.add)

    def test_add_controls_invalid_type_raises(self):
        m = plotlymap.Map(ee_initialize=False)
        with self.assertRaises(ValueError):
            m.add_controls(12345)

    def test_remove_controls_string_and_list(self):
        m = plotlymap.Map(ee_initialize=False)
        m.remove_controls("zoomin")
        self.assertIn("zoomin", m.layout.modebar.remove)
        m.remove_controls(["zoomin", "zoomout"])
        self.assertIn("zoomout", m.layout.modebar.remove)

    def test_remove_controls_invalid_type_raises(self):
        m = plotlymap.Map(ee_initialize=False)
        with self.assertRaises(ValueError):
            m.remove_controls(12345)

    def test_add_tile_layer(self):
        m = plotlymap.Map(ee_initialize=False)
        m.add_tile_layer(
            url="https://tile.example.com/{z}/{x}/{y}.png",
            name="Test Layer",
        )
        self.assertIn("Test Layer", m.get_tile_layers())

    def test_add_layer(self):
        m = plotlymap.Map(ee_initialize=False)
        layer = go.Scattermapbox(lat=[37.8], lon=[-122.4])
        m.add_layer(layer, name="My Layer")
        self.assertIn("My Layer", m.get_data_layers())

    def test_find_layer_index_data_layer(self):
        m = plotlymap.Map(ee_initialize=False)
        layer = go.Scattermapbox(lat=[37.8], lon=[-122.4], name="pts")
        m.add_layer(layer)
        self.assertEqual(m.find_layer_index("pts"), 1)

    def test_clear_layers_keeps_basemap(self):
        m = plotlymap.Map(ee_initialize=False)
        layer = go.Scattermapbox(lat=[37.8], lon=[-122.4], name="extra")
        m.add_layer(layer)
        m.clear_layers()
        self.assertEqual(len(m.data), 1)

    def test_clear_layers_with_basemap(self):
        m = plotlymap.Map(ee_initialize=False)
        m.clear_layers(clear_basemap=True)
        self.assertEqual(len(m.data), 0)

    def test_add_heatmap_dataframe(self):
        m = plotlymap.Map(ee_initialize=False)
        df = pd.DataFrame(
            {
                "latitude": [37.8, 37.7],
                "longitude": [-122.4, -122.3],
                "value": [1.0, 0.5],
            }
        )
        m.add_heatmap(df)
        self.assertEqual(len(m.data), 2)

    def test_add_heatmap_invalid_data_raises(self):
        m = plotlymap.Map(ee_initialize=False)
        with self.assertRaises(ValueError):
            m.add_heatmap({"invalid": "data"})

    def test_add_basemap_invalid_raises(self):
        m = plotlymap.Map(ee_initialize=False)
        with self.assertRaises(ValueError):
            m.add_basemap("INVALID_BASEMAP_XYZ")

    def test_add_ee_layer_invalid_type_raises(self):
        m = plotlymap.Map(ee_initialize=False)
        with self.assertRaises(AttributeError):
            m.add_ee_layer("not_an_ee_object")

    def test_addlayer_alias(self):
        m = plotlymap.Map(ee_initialize=False)
        self.assertEqual(m.addLayer, m.add_ee_layer)

    def test_remove_layer(self):
        m = plotlymap.Map(ee_initialize=False)
        m.add_tile_layer(
            url="https://tile.example.com/{z}/{x}/{y}.png",
            name="removable",
        )
        self.assertIn("removable", m.get_tile_layers())
        m.remove_layer("removable")
        self.assertNotIn("removable", m.get_tile_layers())

    def test_set_layer_visibility(self):
        m = plotlymap.Map(ee_initialize=False)
        layer = go.Scattermapbox(lat=[0], lon=[0], name="vis_test")
        m.add_layer(layer)
        m.set_layer_visibility("vis_test", show=False)
        index = m.find_layer_index("vis_test")
        self.assertFalse(m.data[index].visible)

    def test_set_layer_opacity(self):
        m = plotlymap.Map(ee_initialize=False)
        m.add_tile_layer(
            url="https://tile.example.com/{z}/{x}/{y}.png",
            name="opacity_test",
        )
        m.set_layer_opacity("opacity_test", opacity=0.5)
        index = m.find_layer_index("opacity_test")
        self.assertEqual(m.layout.mapbox.layers[index].opacity, 0.5)


if __name__ == "__main__":
    unittest.main()
