from __future__ import annotations

from collections.abc import *
from pathlib import Path

import axipy

SUPPORTED_TYPES: tuple[type[axipy.Geometry], ...] = axipy.Polygon, axipy.MultiPolygon
SUPPORTED_TYPES_MESSAGE: tuple[str, ...] = "Полигон", "Коллекция полигонов"


def run_in_gui(func):
    def inner(*args, **kwargs):
        return axipy.run_in_gui(func, *args, **kwargs)

    return inner


class NotifyResultsMixIn:
    __initialized: bool = False

    def __init__(self) -> None:
        self.__skip_existing_count: int = 0
        self.__skip_existing_first_file_name: Path | None = None

        self.__exceptions_count: int = 0
        self.__exceptions_first: Exception | None = None

        self.__skip_existing_was_checked: bool = False

    def check_exception(self, exc: Exception) -> None:
        if self.__exceptions_first is None:
            self.__exceptions_first = exc
        self.__exceptions_count += 1

    @run_in_gui
    def notify_exceptions(self, plugin_title: str) -> None:
        if self.__exceptions_count == 0:
            return None

        message = (
            f"Ошибка конвертации: '{self.__exceptions_first}'. Всего ошибок: {self.__exceptions_count}."
        )
        if self.__exceptions_count > 1:
            message += (
                "Полный список ошибок, доступен в панели 'Консоль Python'."
            )

        axipy.Notifications.push(
            plugin_title,
            message,
            axipy.Notifications.Warning,
        )

    def check_skipped(self, file_name: Path) -> bool:
        if not self.__initialized:
            NotifyResultsMixIn.__init__(self)
            self.__initialized = True

        if not self.__skip_existing_was_checked:
            self.__skip_existing_was_checked = True

        if file_name.exists():
            self.__skip_existing_count += 1
            if self.__skip_existing_first_file_name is None:
                self.__skip_existing_first_file_name = file_name
            return True

        return False

    @run_in_gui
    def notify_skipped(self, plugin_title: str) -> None:
        if not self.__skip_existing_was_checked:
            raise RuntimeError("check_skipped hasn't been called.")
        if self.__skip_existing_count == 0:
            return None

        message: str = (
            f"Файл '{self.__skip_existing_first_file_name}' уже существует в выходной папке. Файл пропущен.\n"
        )

        if self.__skip_existing_count > 1:
            message += f"(Всего пропущено: {self.__skip_existing_count})."

        axipy.Notifications.push(
            plugin_title,
            message,
            axipy.Notifications.Warning,
        )

    @run_in_gui
    def notify_results(
            self,
            plugin_title: str,
            successful_count: int,
            all_count: int,
    ) -> None:
        message = f"Конвертация завершена. Сконвертировано: {successful_count}/{all_count}."
        message_type = axipy.Notifications.Information

        if successful_count == all_count:
            message_type = axipy.Notifications.Success
        elif successful_count == 0:
            message_type = axipy.Notifications.Critical
        elif successful_count < all_count:
            message_type = axipy.Notifications.Warning

        axipy.Notifications.push(
            plugin_title,
            message,
            message_type,
        )


def _init_feature_geom_generator(
        items: Iterable[axipy.Feature],
        count: int,
        task: axipy.DialogTask,
) -> Generator[tuple[axipy.Feature, axipy.Geometry]]:
    progress_max: int = 1000
    one_step: int = 1
    step_needed: bool = False
    n: int = 0

    if count > progress_max:
        one_step = count // progress_max
        step_needed = True

    if step_needed:
        new_max = progress_max
    else:
        new_max = count

    # TODO: task.max resets value in DialogTask
    if task.max != new_max:
        task.max = new_max

    for f in items:
        if task.is_canceled:
            return None

        if step_needed:
            n += 1
            if n % one_step == 0:
                task.value += 1
        else:
            task.value += 1

        g = f.geometry
        if g is not None:
            yield f, g


def _init_geom_generator(
        items: Iterable[axipy.Feature],
        count: int,
        task: axipy.DialogTask,
) -> Generator[axipy.Geometry]:
    for _, g in _init_feature_geom_generator(items, count, task):
        yield g
