"""Anmelde-Log, IP-Tabelle, Besuche und die Abruf-Endpoints fuer xin."""

import hashlib

import pytest
from django.test import Client
from rest_framework.test import APIClient

from app_admin.models import AdminToken, AdminUser
from app_nutzer.models import NutzerAnmeldung, NutzerAnmeldungIP, NutzerBesuch, User

pytestmark = pytest.mark.django_db

PASSWORT = 'geheim-123'
TOKEN = 'anmelde-log-test-token'


@pytest.fixture
def nutzer():
    return User.objects.create_user('nutzer@example.de', PASSWORT, PersonenID=4711)


@pytest.fixture
def api_client():
    admin = AdminUser.objects.create(username='api', email='api@admin.de', is_staff=True, is_active=True)
    AdminToken.objects.create(user=admin, key_hash=hashlib.sha256(TOKEN.encode()).hexdigest())
    c = APIClient()
    c.credentials(HTTP_AUTHORIZATION=f'Bearer {TOKEN}')
    return c


def _anmelden(email, passwort=PASSWORT, client=None):
    client = client or Client(REMOTE_ADDR='10.1.2.3', HTTP_USER_AGENT='pytest')
    client.post('/users/authenticate', {'email': email, 'password': passwort})
    return client


class TestErfassung:
    def test_erfolgreiche_anmeldung_mit_ip_und_besuch(self, nutzer):
        _anmelden(nutzer.email)

        a = NutzerAnmeldung.objects.get()
        assert a.PersonenID == 4711 and a.erfolgreich
        assert a.ip_info.ip == '10.1.2.3'
        assert a.ip_info.user_agent == 'pytest'
        assert NutzerBesuch.objects.filter(PersonenID=4711).count() == 1

    def test_fehlversuch_bekannter_nutzer(self, nutzer):
        _anmelden(nutzer.email, passwort='falsch')

        a = NutzerAnmeldung.objects.get()
        assert not a.erfolgreich
        assert not NutzerBesuch.objects.exists()

    def test_fehlversuch_unbekannte_email_wird_nicht_erfasst(self, nutzer):
        _anmelden('fremd@example.de', passwort='falsch')
        assert not NutzerAnmeldung.objects.exists()

    def test_staff_nutzer_wird_nicht_erfasst(self):
        User.objects.create_user('staff@example.de', PASSWORT, PersonenID=1, is_staff=True)
        _anmelden('staff@example.de')
        assert not NutzerAnmeldung.objects.exists()
        assert not NutzerBesuch.objects.exists()

    def test_besuch_nur_einmal_pro_tag(self, nutzer):
        c = _anmelden(nutzer.email)
        c.get('/login')
        c.get('/login')
        _anmelden(nutzer.email)  # neue Session, selber Tag

        assert NutzerAnmeldung.objects.count() == 2
        assert NutzerBesuch.objects.count() == 1

    def test_besuch_ohne_neue_anmeldung(self, nutzer):
        c = Client()
        c.force_login(nutzer, backend='django.contrib.auth.backends.ModelBackend')
        c.get('/login')
        assert NutzerBesuch.objects.filter(PersonenID=4711).count() == 1
        assert not NutzerAnmeldung.objects.exists()

    def test_ips_loeschen_laesst_log_stehen(self, nutzer):
        from django.core.management import call_command
        _anmelden(nutzer.email)
        call_command('anmelde_ips_loeschen', tage=0)
        assert NutzerAnmeldung.objects.count() == 1
        assert not NutzerAnmeldungIP.objects.exists()


class TestApi:
    def test_anmeldungen_inkrementell(self, nutzer, api_client):
        _anmelden(nutzer.email)
        _anmelden(nutzer.email, passwort='falsch')
        erste_id = NutzerAnmeldung.objects.order_by('id').first().id

        alle = api_client.get('/api/anmeldungen').data
        assert [x['erfolgreich'] for x in alle] == [True, False]
        assert alle[0]['ip'] == '10.1.2.3'

        neu = api_client.get(f'/api/anmeldungen?seit_id={erste_id}').data
        assert len(neu) == 1 and neu[0]['erfolgreich'] is False

    def test_anmeldung_ohne_ip(self, nutzer, api_client):
        _anmelden(nutzer.email)
        NutzerAnmeldungIP.objects.all().delete()
        assert api_client.get('/api/anmeldungen').data[0]['ip'] is None

    def test_besuche(self, nutzer, api_client):
        _anmelden(nutzer.email)
        daten = api_client.get('/api/besuche?seit_id=0').data
        assert len(daten) == 1 and daten[0]['PersonenID'] == 4711

    def test_ohne_token_401(self):
        assert APIClient().get('/api/anmeldungen').status_code == 401
