"""Record sign-in attempts. Wired up in AccountsConfig.ready()."""
from django.contrib.auth.signals import user_logged_in, user_login_failed
from django.dispatch import receiver

from .models import LoginEvent


def _client_ip(request):
    if request is None:
        return None
    forwarded = request.META.get("HTTP_X_FORWARDED_FOR", "")
    if forwarded:
        return forwarded.split(",")[0].strip()
    return request.META.get("REMOTE_ADDR")


def _user_agent(request):
    if request is None:
        return ""
    return request.META.get("HTTP_USER_AGENT", "")[:300]


def _source(ip, user_agent):
    """Did a person's browser do this?

    Not `request is None`: a server-side `login()` usually DOES pass a request
    object — Django's test client builds a bare `HttpRequest()` with empty
    META — so the request is present and tells you nothing. What actually
    separates the two is that a browser always sends an address and a
    User-Agent, and an in-process call has neither. Django stamps `last_login`
    either way, which is how owners who had never signed in came to show a
    "last signed in" date.
    """
    return LoginEvent.WEB if (ip or user_agent) else LoginEvent.INTERNAL


@receiver(user_logged_in)
def record_login(sender, request, user, **kwargs):
    ip, user_agent = _client_ip(request), _user_agent(request)
    LoginEvent.objects.create(
        user=user,
        username=user.get_username(),
        succeeded=True,
        ip=ip,
        user_agent=user_agent,
        source=_source(ip, user_agent),
    )


@receiver(user_login_failed)
def record_failed_login(sender, credentials, request=None, **kwargs):
    # `credentials` is already scrubbed of the password by Django.
    LoginEvent.objects.create(
        user=None,
        username=(credentials or {}).get("username", "")[:254],
        succeeded=False,
        ip=_client_ip(request),
        user_agent=_user_agent(request),
        source=_source(_client_ip(request), _user_agent(request)),
    )
