"""Fake map used for testing."""

# pylint: disable=too-few-public-methods
# pylint: disable=too-many-arguments
# pylint: disable=too-many-instance-attributes
# pylint: disable=too-many-positional-arguments

import unittest

import ee
import ipywidgets
import traitlets

from geemap import ee_tile_layers


class FakeMap:
    """A fake map used for initializing widgets."""

    def __init__(self):
        self.default_style = {}
        self.interaction_handlers = set()
        self.scale = 1024
        self.zoom = 7
        self.layers = []
        self.ee_layers = {}
        self.geojson_layers = []
        self.controls = []
        self.add = unittest.mock.MagicMock()

        self._recognized_attrs = self.__dict__.keys()

    def __setattr__(self, k, v):
        if hasattr(self, "_recognized_attrs") and k not in self._recognized_attrs:
            raise AttributeError(f"{k} is not a recognized attr")
        super().__setattr__(k, v)

    def on_interaction(self, func, remove=False):
        if remove:
            if func not in self.interaction_handlers:
                raise ValueError("Removing an unknown on_interaction func.")

            self.interaction_handlers.remove(func)
        else:
            if func in self.interaction_handlers:
                raise ValueError("This on_interaction func already exists.")

            self.interaction_handlers.add(func)

    def click(self, coordinates, event_type):
        for handler in self.interaction_handlers:
            handler(coordinates=coordinates, type=event_type)

    def get_scale(self):
        return self.scale

    @property
    def bounds(self):
        return ((1, 2), (3, 4))

    def find_layer_index(self, name) -> int:
        layers = self.layers

        for index, layer in enumerate(layers):
            if layer.name == name:
                return index

        return -1

    def add_layer(
        self,
        ee_object,
        vis_params=None,
        name=None,
        shown=True,
        opacity=1.0,
    ):
        layer = ee_object
        if isinstance(
            ee_object,
            (
                ee.FeatureCollection,
                ee.Feature,
                ee.Geometry,
                ee.Image,
            ),
        ):
            layer = ee_tile_layers.EELeafletTileLayer(
                ee_object, vis_params, name, shown, opacity
            )
            self.ee_layers[name] = {
                "ee_object": ee_object,
                "ee_layer": layer,
                "vis_params": vis_params,
            }
        self.layers.append(layer)

    def remove_layer(self, layer):
        if isinstance(layer, str):
            layer = self.ee_layers[layer]["ee_layer"]
        self.layers.remove(layer)
        del self.ee_layers[layer.name]

    def get_layer_names(self):
        return [layer.name for layer in self.layers]

    def zoom_to_bounds(self, _):
        pass

    def substitute(self, old_layer, new_layer):
        i = self.find_layer_index(old_layer)
        if i >= 0:
            self.layers[i] = new_layer

    def add_basemap(self, basemap="HYBRID", show=True, **kwargs):
        del kwargs  # Unused.
        self.add_layer(FakeTileLayer(name=basemap, visible=show))

    def _add_legend(
        self,
        title=None,
        keys=None,
        colors=None,
        position=None,
        builtin_legend=None,
        layer_name=None,
        add_header=None,
        widget_args=None,
        **kwargs,
    ):
        del (
            title,
            keys,
            colors,
            position,
            builtin_legend,
            add_header,
            widget_args,
            kwargs,
        )
        if layer := self.ee_layers.get(layer_name):
            layer["legend"] = {}

    def _add_colorbar(
        self,
        vis_params=None,
        cmap=None,
        discrete=None,
        label=None,
        orientation=None,
        position=None,
        transparent_bg=None,
        layer_name=None,
        font_size=None,
        axis_off=None,
        max_width=None,
        **kwargs,
    ):
        del (
            vis_params,
            cmap,
            discrete,
            label,
            orientation,
            position,
            transparent_bg,
            font_size,
            axis_off,
            max_width,
            kwargs,
        )
        if layer := self.ee_layers.get(layer_name):
            layer["colorbar"] = {}

    @property
    def cursor_style(self):
        return self.default_style.get("cursor")


class FakeEeTileLayer:
    def __init__(self, name="test-layer", visible=True, opacity=1.0):
        self.name = name
        self.visible = visible
        self.opacity = opacity

    def observe(self, func, names):
        pass

    def calculate_vis_minmax(
        self,
        *,
        bounds: ee.Geometry | ee.Feature | ee.FeatureCollection,
        bands: list[str] | None = None,
        percent: float | None = None,
        sigma: float | None = None,
    ) -> tuple[float, float]:
        del bounds, bands, percent, sigma  # Unused.
        return 21, 42


class FakeTileLayer(ipywidgets.Widget):
    """A fake tile layer."""

    name = traitlets.Unicode("").tag(sync=True)
    visible = traitlets.Bool(True).tag(sync=True)
    opacity = traitlets.Float(1).tag(sync=True)
    loading = traitlets.Bool(False).tag(sync=True)

    def __init__(self, name="test-layer", visible=True, opacity=1.0):
        super().__init__()
        self.name = name
        self.visible = visible
        self.opacity = opacity


class FakeGeoJSONLayer:
    def __init__(self, name="test-layer", visible=True, style=None):
        self.name = name
        self.visible = visible
        self.style = style or {}

    def observe(self, func, names):
        pass
