import abc
from typing import Optional

from axipy import Plugin
from axipy.concurrent import AxipyProgressHandler
from axipy.da import CollectionStyle, Feature, Geometry, GeometryType, Style, Table

from ..data_types import SearchGroupPatterns, SearchSettings
from ..utils import geometry_types_from_settings
from ..widgets.style_widget_interface import SelectByStyleWidgetInterface


class SelectStrategy(abc.ABC):

    def __init__(self, f: Feature, table: Table) -> None:
        super().__init__()
        self.__feature = f
        self.__table = table

    # @abc.abstractmethod
    # def search_patterns(self):
    #     ...

    @property
    def style(self) -> Style:
        return self.__feature.style

    @property
    def gemetry(self) -> Geometry:
        return self.__feature.geometry

    @property
    def feature(self) -> Feature:
        return self.__feature

    @property
    def table(self) -> Table:
        return self.__table

    @abc.abstractmethod
    def identify(self, style: Style) -> bool:
        """
        Используется для определения какая стратегия подходит для переданной фичи.
        """
        return False

    @abc.abstractmethod
    def settings_widget(self, iface: Plugin) -> Optional[SelectByStyleWidgetInterface]:
        return None

    @abc.abstractmethod
    def string_pattern_group_key(self) -> Optional[str]:
        return None

    def is_supported_style(self, target: Style, source: Style, group_patterns: SearchGroupPatterns) -> bool:
        raise RuntimeError()

    def extract_style_from_collection(self, collection_style: CollectionStyle) -> Optional[Style]:
        return None

    def is_supported_geometry(self, target: Geometry, group_patterns: SearchGroupPatterns) -> bool:
        return True

    def query_ids(self, ph: AxipyProgressHandler, table: Table, search_settins: SearchSettings):
        geometries = geometry_types_from_settings(search_settins.geometries)
        ids = []
        ph.set_max_progress(table.count())
        for f in table.items():
            ph.raise_if_canceled()
            if not (f.geometry and f.geometry.type in geometries):
                continue
            style = f.style
            if f.geometry.type == GeometryType.GeometryCollection and isinstance(style, CollectionStyle):
                style = self.extract_style_from_collection(style)
            if not isinstance(style, type(self.feature.style)):
                continue
            if self.is_supported_style(style, self.style, search_settins.groups) and self.is_supported_geometry(
                f.geometry, search_settins.groups
            ):
                ids.append(f.id)
            ph.add_progress(1)
        return ids
