"""
Tests für PII Detector Service.
"""

import pytest
from llm_gateway.services.pii_detector import (
    PIIDetector,
    PIIType,
    PIIMatch,
    RedactionResult,
    get_pii_detector,
)


class TestPIIDetectorPatterns:
    """Tests für PII-Erkennung."""

    def setup_method(self):
        """Setup für jeden Test."""
        self.detector = PIIDetector()

    def test_detect_email(self):
        """Test E-Mail-Erkennung."""
        text = "Kontakt: max.mustermann@example.com für Rückfragen"
        matches = self.detector.detect(text)

        assert len(matches) == 1
        assert matches[0].type == PIIType.EMAIL
        assert matches[0].value == "max.mustermann@example.com"

    def test_detect_multiple_emails(self):
        """Test mehrerer E-Mail-Adressen."""
        text = "Von: a@b.de An: c@d.com CC: e@f.org"
        matches = self.detector.detect(text)

        assert len(matches) == 3
        assert all(m.type == PIIType.EMAIL for m in matches)

    def test_detect_german_phone(self):
        """Test deutsche Telefonnummer."""
        text = "Erreichbar unter 089 12345678"
        matches = self.detector.detect(text)

        assert len(matches) == 1
        assert matches[0].type == PIIType.PHONE

    def test_detect_phone_with_country_code(self):
        """Test Telefonnummer mit Landesvorwahl."""
        text = "Tel: +49 30 1234567"
        matches = self.detector.detect(text)

        assert len(matches) == 1
        assert matches[0].type == PIIType.PHONE

    def test_detect_iban(self):
        """Test IBAN-Erkennung."""
        text = "IBAN: DE89370400440532013000"
        matches = self.detector.detect(text)

        # IBAN sollte erkannt werden (evtl. auch als Telefon, aber IBAN hat Priorität)
        iban_matches = [m for m in matches if m.type == PIIType.IBAN]
        assert len(iban_matches) >= 1

    def test_detect_iban_with_spaces(self):
        """Test IBAN mit Leerzeichen."""
        text = "Konto: DE89 3704 0044 0532 0130 00"
        matches = self.detector.detect(text)

        # Bei überlappenden Matches gewinnt IBAN wegen höherer Priorität
        iban_matches = [m for m in matches if m.type == PIIType.IBAN]
        assert len(iban_matches) >= 1

    def test_detect_credit_card_visa(self):
        """Test Visa Kreditkarte."""
        text = "Karte: 4111 1111 1111 1111"
        matches = self.detector.detect(text)

        assert len(matches) == 1
        assert matches[0].type == PIIType.CREDIT_CARD

    def test_detect_credit_card_mastercard(self):
        """Test Mastercard."""
        text = "MC: 5500 0000 0000 0004"
        matches = self.detector.detect(text)

        # Kreditkarte hat höhere Priorität als Telefon
        cc_matches = [m for m in matches if m.type == PIIType.CREDIT_CARD]
        assert len(cc_matches) >= 1

    def test_detect_ip_address(self):
        """Test IP-Adresse."""
        text = "Server IP: 192.168.1.100"
        matches = self.detector.detect(text)

        assert len(matches) == 1
        assert matches[0].type == PIIType.IP_ADDRESS

    def test_detect_date_of_birth(self):
        """Test Geburtsdatum."""
        text = "Geboren am 15.03.1985"
        matches = self.detector.detect(text)

        assert len(matches) == 1
        assert matches[0].type == PIIType.DATE_OF_BIRTH

    def test_detect_date_single_digit(self):
        """Test Datum mit einstelligem Tag/Monat."""
        text = "DOB: 1.5.1990"
        matches = self.detector.detect(text)

        assert len(matches) == 1
        assert matches[0].type == PIIType.DATE_OF_BIRTH

    def test_no_false_positive_year(self):
        """Test dass Jahre allein nicht erkannt werden."""
        text = "Im Jahr 2024 wurde das System eingeführt"
        matches = self.detector.detect(text)

        # Sollte kein DOB sein
        assert all(m.type != PIIType.DATE_OF_BIRTH for m in matches)


class TestPIIDetectorRedaction:
    """Tests für PII-Redaktion."""

    def setup_method(self):
        """Setup für jeden Test."""
        self.detector = PIIDetector()

    def test_redact_email(self):
        """Test E-Mail Redaktion."""
        text = "Mail an test@example.com senden"
        result = self.detector.redact(text)

        assert result.pii_found is True
        assert "test@example.com" not in result.redacted_text
        assert "[EMAIL_REDACTED]" in result.redacted_text
        assert result.original_text == text

    def test_redact_multiple_pii(self):
        """Test Redaktion mehrerer PII-Typen."""
        text = "Kontakt: max@test.de, Tel: 089 123456"
        result = self.detector.redact(text)

        assert result.pii_found is True
        assert len(result.matches) == 2
        assert "[EMAIL_REDACTED]" in result.redacted_text
        assert "[PHONE_REDACTED]" in result.redacted_text

    def test_redact_preserves_structure(self):
        """Test dass Textstruktur erhalten bleibt."""
        text = "Von: a@b.de\nAn: c@d.de"
        result = self.detector.redact(text)

        assert "\n" in result.redacted_text
        assert "Von:" in result.redacted_text
        assert "An:" in result.redacted_text

    def test_no_pii_returns_original(self):
        """Test ohne PII gibt Original zurück."""
        text = "Keine personenbezogenen Daten hier"
        result = self.detector.redact(text)

        assert result.pii_found is False
        assert result.redacted_text == text
        assert len(result.matches) == 0


class TestPIIDetectorContainsPII:
    """Tests für schnelle PII-Prüfung."""

    def setup_method(self):
        """Setup für jeden Test."""
        self.detector = PIIDetector()

    def test_contains_pii_with_email(self):
        """Test contains_pii mit E-Mail."""
        assert self.detector.contains_pii("test@example.com") is True

    def test_contains_pii_with_phone(self):
        """Test contains_pii mit Telefon."""
        assert self.detector.contains_pii("+49 89 123456") is True

    def test_contains_pii_without_pii(self):
        """Test contains_pii ohne PII."""
        assert self.detector.contains_pii("Schulrecht Bayern") is False


class TestPIIDetectorConfiguration:
    """Tests für Detector-Konfiguration."""

    def test_custom_enabled_types(self):
        """Test mit eingeschränkten PII-Typen."""
        detector = PIIDetector(enabled_types=[PIIType.EMAIL])

        # E-Mail wird erkannt
        assert len(detector.detect("test@example.com")) == 1

        # Telefon wird nicht erkannt
        assert len(detector.detect("+49 89 123456")) == 0

    def test_empty_enabled_types(self):
        """Test mit leerer Typen-Liste."""
        detector = PIIDetector(enabled_types=[])

        # Nichts wird erkannt
        assert len(detector.detect("test@example.com +49 89 123456")) == 0


class TestPIIMatch:
    """Tests für PIIMatch Dataclass."""

    def test_pii_match_creation(self):
        """Test PIIMatch erstellen."""
        match = PIIMatch(
            type=PIIType.EMAIL,
            value="test@example.com",
            start=0,
            end=16,
            replacement="[EMAIL_REDACTED]",
        )

        assert match.type == PIIType.EMAIL
        assert match.value == "test@example.com"
        assert match.start == 0
        assert match.end == 16


class TestRedactionResult:
    """Tests für RedactionResult Dataclass."""

    def test_redaction_result_creation(self):
        """Test RedactionResult erstellen."""
        result = RedactionResult(
            original_text="test@example.com",
            redacted_text="[EMAIL_REDACTED]",
            matches=[],
            pii_found=True,
        )

        assert result.pii_found is True
        assert result.original_text == "test@example.com"
        assert result.redacted_text == "[EMAIL_REDACTED]"


class TestGetPIIDetectorSingleton:
    """Tests für Singleton Pattern."""

    def test_singleton_returns_same_instance(self):
        """Test dass get_pii_detector Singleton zurückgibt."""
        detector1 = get_pii_detector()
        detector2 = get_pii_detector()
        assert detector1 is detector2


class TestPIIRealWorldExamples:
    """Tests mit realistischen Beispielen."""

    def setup_method(self):
        """Setup für jeden Test."""
        self.detector = PIIDetector()

    def test_school_query_without_pii(self):
        """Test Schulanfrage ohne PII."""
        query = "Welche Regeln gelten für Datenschutz an Schulen in Bayern?"
        result = self.detector.redact(query)

        assert result.pii_found is False
        assert result.redacted_text == query

    def test_school_query_with_email(self):
        """Test Schulanfrage mit E-Mail."""
        query = "Wie kontaktiere ich lehrer.mueller@schule.de wegen Datenschutz?"
        result = self.detector.redact(query)

        assert result.pii_found is True
        assert "lehrer.mueller@schule.de" not in result.redacted_text
        assert "[EMAIL_REDACTED]" in result.redacted_text

    def test_parent_letter_with_multiple_pii(self):
        """Test Elternbrief mit mehreren PII."""
        text = """
        Sehr geehrte Familie Müller,
        bitte rufen Sie unter 089 12345678 an oder
        schreiben Sie an eltern@schule.de.
        IBAN für Klassenfahrt: DE89370400440532013000
        """
        result = self.detector.redact(text)

        assert result.pii_found is True
        # Mindestens 3 Matches (Telefon, E-Mail, IBAN)
        # Überlappende werden gefiltert (IBAN hat Priorität über Telefon)
        assert len(result.matches) >= 3
        assert "[PHONE_REDACTED]" in result.redacted_text
        assert "[EMAIL_REDACTED]" in result.redacted_text
        assert "[IBAN_REDACTED]" in result.redacted_text
