import logging

from allauth.account.adapter import DefaultAccountAdapter
from allauth.core.exceptions import ImmediateHttpResponse
from allauth.socialaccount.adapter import DefaultSocialAccountAdapter
from django.contrib import messages
from django.contrib.auth.models import Group
from django.shortcuts import redirect

logger = logging.getLogger(__name__)


class CasdoorAccountAdapter(DefaultSocialAccountAdapter):

    def is_open_for_signup(self, request, sociallogin):
        return True

    def pre_social_login(self, request, sociallogin):
        """
        Runs on every SSO login (new and returning users).

        Blocks superusers (they must use local auth) and re-syncs group
        and is_staff claims for returning users so IdP changes take effect
        immediately without requiring a re-registration.
        """
        if sociallogin.user.id:
            user = sociallogin.user

            if user.is_superuser:
                logger.warning(
                    f"SSO login blocked for superuser {user.username}. "
                    "Superusers must use local authentication."
                )
                messages.error(
                    request,
                    "Superuser accounts must use local authentication.",
                )
                raise ImmediateHttpResponse(redirect("account_login"))

            extra_data = sociallogin.account.extra_data

            org_identifier = extra_data.get("organization", "")
            if org_identifier:
                self._assign_organization(user, org_identifier)

            groups = extra_data.get("groups", [])
            self._assign_groups(user, groups)

            user.is_staff = any(g in ["staff", "sme", "admin"] for g in groups)
            user.save(update_fields=["is_staff"])

    def populate_user(self, request, sociallogin, data):
        user = super().populate_user(request, sociallogin, data)

        email = data.get("email", "")
        user.username = email
        user.email = email

        user.first_name = data.get("given_name", "")
        user.last_name = data.get("family_name", "")

        if not user.first_name and not user.last_name:
            full_name = data.get("name", "")
            if full_name:
                parts = full_name.split(" ", 1)
                user.first_name = parts[0]
                user.last_name = parts[1] if len(parts) > 1 else ""

        user.is_superuser = False

        groups = data.get("groups", [])
        user.is_staff = any(g in ["staff", "sme", "admin"] for g in groups)

        return user

    def save_user(self, request, sociallogin, form=None):
        user = super().save_user(request, sociallogin, form)
        extra_data = sociallogin.account.extra_data

        org_identifier = extra_data.get("organization", "")
        if org_identifier:
            self._assign_organization(user, org_identifier)

        groups = extra_data.get("groups", [])
        self._assign_groups(user, groups)
        return user

    def _assign_organization(self, user, org_identifier):
        # Mnemosyne has no Organization model — no-op, preserved for future use.
        pass

    def _assign_groups(self, user, group_names):
        group_mapping = {
            "view_only": "View Only",
            "staff": "Staff",
            "sme": "SME",
            "admin": "Admin",
        }
        user.groups.clear()
        for casdoor_group in group_names:
            django_group_name = group_mapping.get(casdoor_group.lower())
            if django_group_name:
                group, _ = Group.objects.get_or_create(name=django_group_name)
                user.groups.add(group)
                logger.info(f"Added {user.username} to group {django_group_name}")


class LocalAccountAdapter(DefaultAccountAdapter):

    def is_open_for_signup(self, request):
        return False

    def authentication_failed(self, request, **kwargs):
        logger.warning(
            f"Local authentication failed from {request.META.get('REMOTE_ADDR')}"
        )
        super().authentication_failed(request, **kwargs)
