from typing import Optional

from jinja2 import Template

from folium.elements import Element, JSCSSMixin, MacroElement
from folium.map import Marker
from folium.utilities import parse_options


class OverlappingMarkerSpiderfier(JSCSSMixin, MacroElement):
    """
    A plugin that handles overlapping markers on a map by spreading them out in a spiral or circle pattern when clicked.

    This plugin is useful when you have multiple markers in close proximity that would otherwise be difficult to interact with.
    When a user clicks on a cluster of overlapping markers, they spread out in a 'spider' pattern, making each marker
    individually accessible.

    Markers are automatically identified and managed by the plugin, so there is no need to add them separately.
    Simply add the plugin to the map using `oms.add_to(map)`.

    Parameters
    ----------
    keep_spiderfied : bool, default True
        If true, markers stay spiderfied after clicking.
    nearby_distance : int, default 20
        Pixels away from a marker that is considered overlapping.
    leg_weight : float, default 1.5
        Weight of the spider legs.
    circle_spiral_switchover : int, default 9
        Number of markers at which to switch from circle to spiral pattern.

    Example
    -------
    >>> oms = OverlappingMarkerSpiderfier(
    ...     keep_spiderfied=True, nearby_distance=30, leg_weight=2.0
    ... )
    >>> oms.add_to(map)
    """

    _template = Template("""
        {% macro script(this, kwargs) %}
        (function () {
            try {
                var oms = new OverlappingMarkerSpiderfier(
                    {{ this._parent.get_name() }},
                    {{ this.options|tojson }}
                );

                oms.addListener('spiderfy', function() {
                    {{ this._parent.get_name() }}.closePopup();
                });

                {%- for marker in this.markers %}
                    oms.addMarker({{ marker.get_name() }});
                {%- endfor %}
            } catch (error) {
                console.error('Error initializing OverlappingMarkerSpiderfier:', error);
            }
        })();
        {% endmacro %}
        """)

    default_js = [
        (
            "overlappingmarkerjs",
            "https://cdnjs.cloudflare.com/ajax/libs/OverlappingMarkerSpiderfier-Leaflet/0.2.6/oms.min.js",
        )
    ]

    def __init__(
        self,
        keep_spiderfied: bool = True,
        nearby_distance: int = 20,
        leg_weight: float = 1.5,
        circle_spiral_switchover: int = 9,
        **kwargs,
    ):
        super().__init__()
        self._name = "OverlappingMarkerSpiderfier"
        self.options = parse_options(
            keep_spiderfied=keep_spiderfied,
            nearby_distance=nearby_distance,
            leg_weight=leg_weight,
            circle_spiral_switchover=circle_spiral_switchover,
            **kwargs,
        )

    def add_to(
        self, parent: Element, name: Optional[str] = None, index: Optional[int] = None
    ) -> Element:
        self._parent = parent
        self.markers = self._get_all_markers(parent)
        return super().add_to(parent, name=name, index=index)

    def _get_all_markers(self, element: Element) -> list:
        markers = []
        for child in element._children.values():
            if isinstance(child, Marker):
                markers.append(child)
            elif hasattr(child, "_children"):
                markers.extend(self._get_all_markers(child))
        return markers
