"""
Tests für Eigentumsverhältnisse und deren Zusammenführung mit Mietverhältnissen.

Fachliche Grundlage: xin, HANDOVER_vportal_nutzungsverhaeltnisse.md –
"Im Portal gibt es keine Eigentümer und keine Mieter, sondern ausschließlich
Nutzer. Der Eigentümer bekommt keine Sonderrolle."
"""
from datetime import timedelta

from django.test import Client, TestCase, override_settings
from django.utils import timezone

from app_admin.mixins import get_accessible_reuids
from app_admin.models import AdminToken, AdminUser
from app_nutzer.models import ReuEigentuemer, ReuMietV, User
from app_real_estate.models import RealEstate, RealEstateUnit
from core.services.nutzungsverhaeltnisse import (
    ART_EIGENTUEMER,
    ART_MIETER,
    lade_verhaeltnisse,
    ueberlappende_verhaeltnisse,
    verhaeltnisse_fuer_user,
)

HEUTE = timezone.now().date()


def _re():
    return RealEstate.objects.get_or_create(
        REID=1,
        defaults={'Street': 'Bonnerstr.', 'HouseNumber': '16', 'City': 'Trier'},
    )[0]


def _person(personen_id, email):
    u = User(PersonenID=personen_id, email=email, SalutationID=1, lastname='Muster')
    u.set_unusable_password()
    u.save()
    return u


class EigentuemerZugriffTest(TestCase):
    def setUp(self):
        self.reu = RealEstateUnit.objects.create(REUID=67, REID=_re(), REUDesc='DG')
        self.eigentuemer = _person(50, 'eigentuemer@test.de')

    def test_eigentuemer_ohne_mietvertrag_hat_zugriff(self):
        """Genau der Fall, der vorher 403 bzw. 404 lieferte."""
        self.assertEqual(get_accessible_reuids(self.eigentuemer), [])

        ReuEigentuemer.objects.create(
            EigentumID=1, REUID=self.reu, EigentuemerID=50,
            ValidFrom=HEUTE - timedelta(days=100), ValidUntil=None,
        )
        self.assertIn(67, get_accessible_reuids(self.eigentuemer))

    def test_art_wird_gesetzt(self):
        ReuEigentuemer.objects.create(
            EigentumID=1, REUID=self.reu, EigentuemerID=50,
            ValidFrom=HEUTE - timedelta(days=100), ValidUntil=None,
        )
        nv = verhaeltnisse_fuer_user(self.eigentuemer)[0]
        self.assertEqual(nv.art, ART_EIGENTUEMER)
        self.assertTrue(nv.ist_eigentuemer)
        self.assertEqual(nv.quell_id, 1)

    def test_beendetes_eigentum_gibt_keinen_zugriff(self):
        """Gleiche Regel wie beim Mietvertrag – keine Sonderrolle."""
        ReuEigentuemer.objects.create(
            EigentumID=1, REUID=self.reu, EigentuemerID=50,
            ValidFrom=HEUTE - timedelta(days=100),
            ValidUntil=HEUTE - timedelta(days=1),
        )
        self.assertNotIn(67, get_accessible_reuids(self.eigentuemer))

    def test_kuenftiges_eigentum_gibt_noch_keinen_zugriff(self):
        ReuEigentuemer.objects.create(
            EigentumID=1, REUID=self.reu, EigentuemerID=50,
            ValidFrom=HEUTE + timedelta(days=10), ValidUntil=None,
        )
        self.assertNotIn(67, get_accessible_reuids(self.eigentuemer))

    def test_fremdes_eigentum_bleibt_aussen_vor(self):
        anderer = _person(51, 'anderer@test.de')
        ReuEigentuemer.objects.create(
            EigentumID=1, REUID=self.reu, EigentuemerID=51,
            ValidFrom=HEUTE - timedelta(days=10), ValidUntil=None,
        )
        self.assertNotIn(67, get_accessible_reuids(self.eigentuemer))
        self.assertIn(67, get_accessible_reuids(anderer))


class MergeTest(TestCase):
    """Ein Nutzer kann gleichzeitig Mieter der einen und Eigentümer der
    anderen Einheit sein."""

    def setUp(self):
        re = _re()
        self.reu_a = RealEstateUnit.objects.create(REUID=101, REID=re, REUDesc='Whg. A')
        self.reu_b = RealEstateUnit.objects.create(REUID=102, REID=re, REUDesc='Whg. B')
        self.nutzer = _person(60, 'beides@test.de')

        ReuMietV.objects.create(
            MietVID=1, REUID=self.reu_a, MieterID=60,
            ContractValidFrom=HEUTE - timedelta(days=100), ContractValidUntil=None,
        )
        ReuEigentuemer.objects.create(
            EigentumID=1, REUID=self.reu_b, EigentuemerID=60,
            ValidFrom=HEUTE - timedelta(days=50), ValidUntil=None,
        )

    def test_beide_einheiten_zugaenglich(self):
        result = get_accessible_reuids(self.nutzer)
        self.assertIn(101, result)
        self.assertIn(102, result)

    def test_beide_arten_in_einer_liste(self):
        arten = {nv.art for nv in verhaeltnisse_fuer_user(self.nutzer)}
        self.assertEqual(arten, {ART_MIETER, ART_EIGENTUEMER})

    def test_weg_fall_eigentuemer_mieter_eigentuemer(self):
        """Eigentümer nutzt selbst, vermietet, nutzt wieder selbst.

        In xin sind das drei getrennte, überschneidungsfreie Perioden
        derselben Einheit – kein Mietvertrag mit Lücke.
        """
        reu = RealEstateUnit.objects.create(REUID=103, REID=_re(), REUDesc='Whg. C')
        ReuEigentuemer.objects.create(
            EigentumID=2, REUID=reu, EigentuemerID=60,
            ValidFrom=HEUTE - timedelta(days=900), ValidUntil=HEUTE - timedelta(days=601),
        )
        ReuMietV.objects.create(
            MietVID=2, REUID=reu, MieterID=61,
            ContractValidFrom=HEUTE - timedelta(days=600),
            ContractValidUntil=HEUTE - timedelta(days=301),
        )
        ReuEigentuemer.objects.create(
            EigentumID=3, REUID=reu, EigentuemerID=60,
            ValidFrom=HEUTE - timedelta(days=300), ValidUntil=None,
        )

        alle = lade_verhaeltnisse(reuids=[103], nur_aktive=False)
        self.assertEqual(
            [nv.art for nv in alle],
            [ART_EIGENTUEMER, ART_MIETER, ART_EIGENTUEMER],
        )
        # Aktuell läuft nur die letzte Periode.
        aktiv = lade_verhaeltnisse(reuids=[103])
        self.assertEqual([nv.quell_id for nv in aktiv], [3])


class UeberlappungTest(TestCase):
    def setUp(self):
        self.reu = RealEstateUnit.objects.create(REUID=67, REID=_re(), REUDesc='DG')
        ReuMietV.objects.create(
            MietVID=135, REUID=self.reu, MieterID=70,
            ContractValidFrom=HEUTE - timedelta(days=100), ContractValidUntil=None,
        )

    def test_offenes_verhaeltnis_kollidiert(self):
        treffer = ueberlappende_verhaeltnisse(67, HEUTE, None, ausser=(ART_MIETER, 189))
        self.assertEqual([t.quell_id for t in treffer], [135])

    def test_eigenes_update_kollidiert_nicht_mit_sich_selbst(self):
        treffer = ueberlappende_verhaeltnisse(
            67, HEUTE - timedelta(days=100), None, ausser=(ART_MIETER, 135),
        )
        self.assertEqual(treffer, [])

    def test_eigentuemer_kollidiert_mit_mieter(self):
        """Ohne Prüfung gegen Nutzungsperioden bliebe das unentdeckt."""
        treffer = ueberlappende_verhaeltnisse(67, HEUTE, None, ausser=(ART_EIGENTUEMER, 1))
        self.assertEqual(len(treffer), 1)
        self.assertEqual(treffer[0].art, ART_MIETER)

    def test_lueckenlos_anschliessend_kollidiert_nicht(self):
        ReuMietV.objects.filter(MietVID=135).update(
            ContractValidUntil=HEUTE - timedelta(days=1)
        )
        treffer = ueberlappende_verhaeltnisse(67, HEUTE, None, ausser=(ART_MIETER, 189))
        self.assertEqual(treffer, [])


class ApiTest(TestCase):
    def setUp(self):
        admin = AdminUser(username='sync', email='sync@test.de')
        admin.set_unusable_password()
        admin.save()
        roh = AdminToken.generate_token()
        token = AdminToken(user=admin)
        token.set_token(roh)
        token.save()
        self.auth = {'HTTP_AUTHORIZATION': f'Bearer {roh}'}
        self.reu = RealEstateUnit.objects.create(REUID=67, REID=_re(), REUDesc='DG')

    def test_eigentumsverhaeltnis_anlegen(self):
        c = Client()
        resp = c.put(
            '/api/eigentuemer',
            data={'EigentumID': 1, 'REUID': 67, 'EigentuemerID': 50,
                  'ValidFrom': str(HEUTE - timedelta(days=10))},
            content_type='application/json', **self.auth,
        )
        self.assertEqual(resp.status_code, 200, resp.content)
        self.assertEqual(ReuEigentuemer.objects.count(), 1)

    def test_ohne_enddatum_ist_erlaubt(self):
        """Der Fall, an dem es vorher scheiterte: Eigentümer hat kein Ende."""
        c = Client()
        resp = c.put(
            '/api/eigentuemer',
            data={'EigentumID': 1, 'REUID': 67, 'EigentuemerID': 50,
                  'ValidFrom': str(HEUTE), 'ValidUntil': None},
            content_type='application/json', **self.auth,
        )
        self.assertEqual(resp.status_code, 200, resp.content)
        self.assertIsNone(ReuEigentuemer.objects.get(EigentumID=1).ValidUntil)

    def test_ueberlappung_wird_mit_409_abgelehnt(self):
        c = Client()
        c.put('/api/mietvs',
              data={'MietVID': 135, 'REUID': 67, 'MieterID': 70,
                    'ContractValidFrom': str(HEUTE - timedelta(days=100))},
              content_type='application/json', **self.auth)

        resp = c.put(
            '/api/eigentuemer',
            data={'EigentumID': 1, 'REUID': 67, 'EigentuemerID': 50,
                  'ValidFrom': str(HEUTE - timedelta(days=50))},
            content_type='application/json', **self.auth,
        )
        self.assertEqual(resp.status_code, 409, resp.content)
        self.assertIn('REU 67', resp.json()['detail'])

    def test_anschliessender_zeitraum_wird_akzeptiert(self):
        c = Client()
        c.put('/api/mietvs',
              data={'MietVID': 135, 'REUID': 67, 'MieterID': 70,
                    'ContractValidFrom': str(HEUTE - timedelta(days=100)),
                    'ContractValidUntil': str(HEUTE - timedelta(days=1))},
              content_type='application/json', **self.auth)

        resp = c.put(
            '/api/eigentuemer',
            data={'EigentumID': 1, 'REUID': 67, 'EigentuemerID': 50,
                  'ValidFrom': str(HEUTE)},
            content_type='application/json', **self.auth,
        )
        self.assertEqual(resp.status_code, 200, resp.content)

    def test_liste_je_immobilie(self):
        ReuEigentuemer.objects.create(
            EigentumID=1, REUID=self.reu, EigentuemerID=50,
            ValidFrom=HEUTE - timedelta(days=10), ValidUntil=None,
        )
        resp = Client().get('/api/eigentuemer/1', **self.auth)
        self.assertEqual(resp.status_code, 200, resp.content)
        self.assertEqual(len(resp.json()), 1)


# Seite wird gerendert – ohne collectstatic fehlt sonst das Manifest.
@override_settings(
    STATICFILES_STORAGE='django.contrib.staticfiles.storage.StaticFilesStorage')
class StartseiteOhneVerhaeltnisTest(TestCase):
    """Nach dem Login landet jeder auf '/'. Ohne laufendes Verhaeltnis gab
    die View None zurueck -> 500 direkt nach der Anmeldung."""

    def setUp(self):
        self.reu = RealEstateUnit.objects.create(REUID=67, REID=_re(), REUDesc='DG')
        self.nutzer = _person(60, 'ehemalig@test.de')
        self.client = Client()
        self.client.force_login(
            self.nutzer, backend='django.contrib.auth.backends.ModelBackend')

    def test_ohne_verhaeltnis_kein_500(self):
        resp = self.client.get('/')
        self.assertEqual(resp.status_code, 200)
        self.assertContains(resp, 'Keine Nutzungseinheit')

    def test_beendeter_mietvertrag_kein_500(self):
        ReuMietV.objects.create(
            MietVID=601, REUID=self.reu, MieterID=60,
            ContractValidFrom=HEUTE - timedelta(days=400),
            ContractValidUntil=HEUTE - timedelta(days=30),
        )
        resp = self.client.get('/')
        self.assertEqual(resp.status_code, 200)

    def test_laufender_mietvertrag_leitet_weiter(self):
        ReuMietV.objects.create(
            MietVID=602, REUID=self.reu, MieterID=60,
            ContractValidFrom=HEUTE - timedelta(days=100), ContractValidUntil=None,
        )
        resp = self.client.get('/')
        self.assertEqual(resp.status_code, 302)
