# django_core_micha/invitations/mixins.py

from django.contrib.auth import get_user_model
from django.contrib.auth.tokens import default_token_generator
from django.utils.encoding import force_bytes
from django.utils.http import urlsafe_base64_encode

from rest_framework import status
from rest_framework.decorators import action
from rest_framework.permissions import AllowAny, IsAuthenticated
from rest_framework.response import Response

from .serializers import InviteUserSerializer
from .emails import send_invite_or_reset_email

User = get_user_model()


class InviteActionsMixin:
    """
    Mixin für dein UserViewSet:

    - POST /api/users/invite/          → Einladung (neu oder bestehend)
    - POST /api/users/reset-request/   → Passwort vergessen (für bestehende User)
    - GET  /api/users/<pk>/invite-link/ → Invite-Link für Admins

    Erwartet:
      - user.profile.is_new (optional)
      - user.profile.is_invited (optional)
      - user.profile.role mit Wert "admin" für Admin-Erkennung
    """

    invite_serializer_class = InviteUserSerializer

    def _get_invite_serializer(self, *args, **kwargs):
        return self.invite_serializer_class(*args, **kwargs)

    def _mark_invited_profile(self, user, *, created: bool) -> None:
        profile = getattr(user, "profile", None)
        if not profile:
            return

        if created and hasattr(profile, "is_new"):
            profile.is_new = True

        if hasattr(profile, "is_invited"):
            profile.is_invited = True

        profile.save()

    def _is_admin_or_superuser(self, user) -> bool:
        if not user or not user.is_authenticated:
            return False
        if user.is_superuser:
            return True
        profile = getattr(user, "profile", None)
        return bool(profile and getattr(profile, "role", None) == "admin")

    def _build_frontend_url(self, request, user, *, is_new_user: bool) -> str:
        """
        Baut den Link, der im E-Mail landet,
        z.B. https://template.bigler-consult.ch/invite/<uid>/<token>/
             oder https://template.bigler-consult.ch/reset/<uid>/<token>/
        """
        token = default_token_generator.make_token(user)
        uid = urlsafe_base64_encode(force_bytes(user.pk))

        base = request.build_absolute_uri("/").rstrip("/")  # https://domain.tld
        if is_new_user:
            path = f"/invite/{uid}/{token}/"
        else:
            path = f"/reset/{uid}/{token}/"

        return f"{base}{path}"

    # ------------------------------------------------------------------ #
    # 1) Öffentlicher/Admin-Invite
    # ------------------------------------------------------------------ #
    @action(
        detail=False,
        methods=["post"],
        url_path="invite",
        authentication_classes=[],
        permission_classes=[AllowAny],
    )
    def invite(self, request):
        """
        - nicht eingeloggt: User lädt sich selbst ein (Email → Link setzen Passwort)
        - eingeloggt + Admin: Admin lädt beliebige Email-Adresse ein
        """
        serializer = self._get_invite_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        email = serializer.validated_data["email"]

        # Admin-Check bei eingeloggtem User
        if request.user and request.user.is_authenticated:
            if not self._is_admin_or_superuser(request.user):
                return Response(
                    {"detail": "Permission denied."},
                    status=status.HTTP_403_FORBIDDEN,
                )

        user, created = User.objects.get_or_create(
            email=email,
            defaults={"username": email},
        )

        self._mark_invited_profile(user, created=created)
        url = self._build_frontend_url(request, user, is_new_user=True)

        # WICHTIG: kein "request=" mehr, nur user/url/is_new_user
        send_invite_or_reset_email(
            user=user,
            url=url,
            is_new_user=True,
        )

        return Response(
            {"detail": f"Invitation sent to {email}", "created": created},
            status=status.HTTP_201_CREATED,
        )

    # ------------------------------------------------------------------ #
    # 2) Passwort-vergessen-Flow
    # ------------------------------------------------------------------ #
    @action(
        detail=False,
        methods=["post"],
        url_path="reset-request",
        permission_classes=[AllowAny],
        authentication_classes=[],
    )
    def reset_request(self, request):
        """
        Vergisst Passwort: schickt Reset-Link an bestehende Email.
        Nutzt denselben Template-Mechanismus wie Invite,
        aber mit is_new_user=False → andere Texte/Templates möglich.
        """
        email = request.data.get("email")
        if not email:
            return Response(
                {"detail": "Email not provided."},
                status=status.HTTP_400_BAD_REQUEST,
            )

        try:
            user = User.objects.get(email=email)
        except User.DoesNotExist:
            # Entweder 200 zurückgeben (nicht leaken) oder wie bisher 400:
            return Response(
                {"detail": "User not found."},
                status=status.HTTP_400_BAD_REQUEST,
            )

        url = self._build_frontend_url(request, user, is_new_user=False)

        send_invite_or_reset_email(
            user=user,
            url=url,
            is_new_user=False,
        )

        return Response(
            {"detail": f"Ein Link wurde an deine Mail-Adresse {email} geschickt"},
            status=status.HTTP_200_OK,
        )

    # ------------------------------------------------------------------ #
    # 3) Invite-Link für Admins (z.B. im Admin-UI anzeigen)
    # ------------------------------------------------------------------ #
    @action(
        detail=True,
        methods=["get"],
        url_path="invite-link",
        permission_classes=[IsAuthenticated],
    )
    def invite_link(self, request, pk=None):
        user = self.get_object()

        if not self._is_admin_or_superuser(request.user):
            return Response(
                {"detail": "Permission denied."},
                status=status.HTTP_403_FORBIDDEN,
            )

        url = self._build_frontend_url(request, user, is_new_user=True)

        return Response({"invite_link": url}, status=status.HTTP_200_OK)
