"""
Переиспользуемые миксины для представлений mainapp.

Этот модуль содержит миксины для стандартизации общей логики в представлениях,
включая проверку прав доступа, обработку координат и сообщений.
"""

# Standard library imports
from datetime import datetime
from typing import Optional, Tuple

# Django imports
from django.contrib import messages
from django.contrib.auth.mixins import UserPassesTestMixin
from django.contrib.gis.geos import Point


class RoleRequiredMixin(UserPassesTestMixin):
    """
    Mixin для проверки роли пользователя.

    Проверяет, что пользователь имеет одну из требуемых ролей для доступа к представлению.

    Attributes:
        required_roles (list): Список допустимых ролей для доступа.
                              По умолчанию ['admin', 'moderator'].

    Example:
        class MyView(RoleRequiredMixin, View):
            required_roles = ['admin', 'moderator']

            def get(self, request):
                # Только пользователи с ролью admin или moderator могут получить доступ
                return render(request, 'template.html')
    """

    required_roles = ["admin", "moderator"]

    def test_func(self) -> bool:
        """
        Проверяет, имеет ли пользователь требуемую роль.

        Returns:
            bool: True если пользователь имеет одну из требуемых ролей, иначе False.
        """
        if not self.request.user.is_authenticated:
            return False

        if not hasattr(self.request.user, "customuser"):
            return False

        return self.request.user.customuser.role in self.required_roles


class CoordinateProcessingMixin:
    """
    Mixin для обработки координат из POST данных форм.

    Предоставляет методы для извлечения и обработки координат различных типов
    (геолокация, кубсат, оперативники) из POST запроса и применения их к объекту Geo.

    Note: Координаты Кубсата и оперативников теперь хранятся в модели Source,
    а не в модели Geo, но для совместимости в форме все еще могут быть поля
    для этих координат.
    """

    def process_coordinates(self, geo_instance, prefix: str = "geo") -> None:
        """
        Обрабатывает координаты из POST данных и применяет их к объекту Geo.

        Извлекает координаты геолокации из POST запроса
        и устанавливает соответствующие поля объекта Geo.

        Args:
            geo_instance: Экземпляр модели Geo для обновления координат.
            prefix (str): Префикс для полей формы (по умолчанию 'geo').

        Note:
            Метод ожидает следующие поля в request.POST:
            - geo_longitude, geo_latitude: координаты геолокации
        """
        # Обрабатываем координаты геолокации
        geo_coords = self._extract_coordinates("geo")
        if geo_coords:
            geo_instance.coords = Point(geo_coords[0], geo_coords[1], srid=4326)

    def _extract_coordinates(self, coord_type: str) -> Optional[Tuple[float, float]]:
        """
        Извлекает координаты указанного типа из POST данных.

        Args:
            coord_type (str): Тип координат ('geo', 'kupsat', 'valid').

        Returns:
            Optional[Tuple[float, float]]: Кортеж (longitude, latitude) или None,
                                          если координаты не найдены или невалидны.
        """
        longitude_key = f"{coord_type}_longitude"
        latitude_key = f"{coord_type}_latitude"

        longitude = self.request.POST.get(longitude_key)
        latitude = self.request.POST.get(latitude_key)

        if longitude and latitude:
            try:
                return (float(longitude), float(latitude))
            except (ValueError, TypeError):
                return None
        return None

    def process_timestamp(self, geo_instance) -> None:
        """
        Обрабатывает дату и время из POST данных и применяет к объекту Geo.

        Args:
            geo_instance: Экземпляр модели Geo для обновления timestamp.

        Note:
            Метод ожидает следующие поля в request.POST:
            - timestamp_date: дата в формате YYYY-MM-DD
            - timestamp_time: время в формате HH:MM
        """
        timestamp_date = self.request.POST.get("timestamp_date")
        timestamp_time = self.request.POST.get("timestamp_time")

        if timestamp_date and timestamp_time:
            try:
                naive_datetime = datetime.strptime(
                    f"{timestamp_date} {timestamp_time}", "%Y-%m-%d %H:%M"
                )
                geo_instance.timestamp = naive_datetime
            except ValueError:
                # Если формат даты/времени неверный, пропускаем
                pass


class FormMessageMixin:
    """
    Mixin для стандартизации сообщений об успехе и ошибках в формах.

    Автоматически добавляет сообщения пользователю при успешной или неуспешной
    обработке формы.

    Attributes:
        success_message (str): Сообщение при успешной обработке формы.
        error_message (str): Сообщение при ошибке обработки формы.

    Example:
        class MyFormView(FormMessageMixin, FormView):
            success_message = "Данные успешно сохранены!"
            error_message = "Ошибка при сохранении данных"

            def form_valid(self, form):
                # Автоматически добавит success_message
                return super().form_valid(form)
    """

    success_message = "Операция выполнена успешно"
    error_message = "Произошла ошибка при обработке формы"

    def form_valid(self, form):
        """
        Обрабатывает валидную форму и добавляет сообщение об успехе.

        Args:
            form: Валидная форма Django.

        Returns:
            HttpResponse: Результат обработки родительского метода form_valid.
        """
        if self.success_message:
            messages.success(self.request, self.success_message)
        return super().form_valid(form)

    def form_invalid(self, form):
        """
        Обрабатывает невалидную форму и добавляет сообщение об ошибке.

        Args:
            form: Невалидная форма Django.

        Returns:
            HttpResponse: Результат обработки родительского метода form_invalid.
        """
        if self.error_message:
            messages.error(self.request, self.error_message)
        return super().form_invalid(form)

    def get_success_message(self) -> str:
        """
        Возвращает сообщение об успехе.

        Может быть переопределен в подклассах для динамического формирования сообщения.

        Returns:
            str: Сообщение об успехе.
        """
        return self.success_message

    def get_error_message(self) -> str:
        """
        Возвращает сообщение об ошибке.

        Может быть переопределен в подклассах для динамического формирования сообщения.

        Returns:
            str: Сообщение об ошибке.
        """
        return self.error_message
