from __future__ import annotations

import functools
from dataclasses import dataclass
from pathlib import Path
from typing import Callable, Any

import axipy


def copy_rect(rect: axipy.Rect) -> axipy.Rect:
    return axipy.Rect(rect.xmin, rect.ymin, rect.xmax, rect.ymax)


def _run_in_gui_decor(func: Callable) -> Callable:
    @functools.wraps(func)
    def inner(*args, **kwargs) -> Any:
        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_decor
    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_decor
    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_decor
    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,
        )


class ProgressWithStepMixIn:
    @dataclass
    class ProgressState:
        progress_max: int = 1000
        one_step: int = 1
        step_needed: bool = False
        n: int = 0

    def __init__(self, task: axipy.Task) -> None:
        self.__progress_state = self.ProgressState()
        self.__task = task

    def init_progress_with_step(self, task: axipy.Task, count: int) -> int:
        ProgressWithStepMixIn.__init__(self, task)

        if count > self.__progress_state.progress_max:
            self.__progress_state.one_step = count // self.__progress_state.progress_max
            self.__progress_state.step_needed = True

        if self.__progress_state.step_needed:
            new_max = self.__progress_state.progress_max
        else:
            new_max = count

        return new_max

    def add_value_progress_with_step(self) -> None:
        if self.__progress_state.step_needed:
            self.__progress_state.n += 1
            if self.__progress_state.n % self.__progress_state.one_step == 0:
                self.__task.value += 1
        else:
            self.__task.value += 1


def is_tab_table(table: axipy.Table) -> bool:
    if not type(table) is axipy.Table:
        return False
    return table.provider == axipy.provider_manager.tab.id
