"""Tests for bundeslaender.py — sanity over the parliament registry.

Stand: 16 Bundesländer + Sondereintrag ``BUND`` für den Deutschen
Bundestag (siehe #56). Der ``Bundesland``-Dataclass-Name ist historisch
und wird inzwischen als generischer Parlament-Slot verwendet — eine
Umbenennung ist als separates Refactoring-Item geplant.

Includes the #48 classification regression: TH must be ParlDok, HB must
be StarWeb, SN must be Eigensystem (not ParlDok).
"""
from app.bundeslaender import BUNDESLAENDER, get, aktive_bundeslaender, alle_bundeslaender


class TestRegistryStructure:
    def test_sixteen_bundeslaender_plus_bund(self):
        # 16 echte Bundesländer + BUND-Sondereintrag (#56)
        assert len(BUNDESLAENDER) == 17
        assert "BUND" in BUNDESLAENDER

    def test_codes_are_uppercase(self):
        for code in BUNDESLAENDER:
            assert code.isupper(), f"{code} is not uppercase"

    def test_each_entry_has_naechste_wahl_or_none(self):
        for code, bl in BUNDESLAENDER.items():
            assert bl.naechste_wahl is None or len(bl.naechste_wahl) == 10

    def test_wahlperiode_is_positive_integer(self):
        for bl in BUNDESLAENDER.values():
            assert isinstance(bl.wahlperiode, int) and bl.wahlperiode > 0


class TestActiveBundeslaender:
    def test_active_bundeslaender_include_phase_1_set(self):
        """At least the original four (NRW, LSA, MV, BE) plus any
        Phase-1 additions (BW after #29) must be active. The test
        avoids hardcoding the exact count so adding a new active
        Bundesland in a follow-up doesn't break this case."""
        active_codes = {bl.code for bl in aktive_bundeslaender()}
        original = {"NRW", "LSA", "MV", "BE"}
        assert original <= active_codes

    def test_alle_bundeslaender_returns_all(self):
        # 16 BL + BUND
        assert len(alle_bundeslaender()) == 17

    def test_alle_bundeslaender_active_first(self):
        out = alle_bundeslaender()
        active_codes = {bl.code for bl in aktive_bundeslaender()}
        # The first len(active) entries must all be active
        for bl in out[: len(active_codes)]:
            assert bl.code in active_codes


class TestGetHelper:
    def test_returns_bundesland_for_known_code(self):
        bl = get("NRW")
        assert bl is not None
        assert bl.name == "Nordrhein-Westfalen"

    def test_returns_none_for_unknown_code(self):
        assert get("XX") is None


class TestClassificationFix48:
    """Regression: #48 corrected three doku_system entries that the
    follow-up adapter issues depend on."""

    def test_th_is_parldok_not_starweb(self):
        assert BUNDESLAENDER["TH"].doku_system == "ParlDok"

    def test_hb_is_paris_starweb_variant(self):
        """PARiS war als StarWeb-Skin klassifiziert (#48), nach #21/#33
        differenzieren wir auf "PARiS" weil die Servlet-API-Konvention
        deutlich von der modernen StarWeb-Familie abweicht (Form-POST
        statt browse.tt.json/SearchAndDisplay).
        """
        assert BUNDESLAENDER["HB"].doku_system == "PARiS"

    def test_sn_uses_xml_export_not_parldok(self):
        """EDAS ist ASP.NET-Webforms (NICHT ParlDok-kompatibel) und
        zusätzlich per robots.txt vom Crawling ausgeschlossen. Wir lesen
        stattdessen einen wöchentlich manuell exportierten XML-Dump aus
        data/sn-edas-export.xml — Klassifikation entsprechend "EDAS-XML-Export"."""
        assert BUNDESLAENDER["SN"].doku_system == "EDAS-XML-Export"


class TestWahltermineSane:
    """All upcoming elections must be in chronological order and in the
    near future (sanity check that someone has not pasted a 1990 date)."""

    def test_no_election_before_2026(self):
        for bl in BUNDESLAENDER.values():
            if bl.naechste_wahl:
                assert bl.naechste_wahl >= "2026-01-01"

    def test_no_election_after_2035(self):
        for bl in BUNDESLAENDER.values():
            if bl.naechste_wahl:
                assert bl.naechste_wahl < "2035-01-01"
