"""
Tests für die GDPR API Endpoints
"""

import pytest
from fastapi.testclient import TestClient
from unittest.mock import patch, AsyncMock, MagicMock
import sys
import os

# Add parent directory to path
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))


class TestDataCategories:
    """Tests für Datenkategorien-Endpoint"""

    def test_data_categories_structure(self):
        """Test that data categories have correct structure"""
        # Import the data categories from the GDPR API
        try:
            from gdpr_api import DATA_CATEGORIES
        except ImportError:
            pytest.skip("gdpr_api module not available")

        assert "essential" in DATA_CATEGORIES
        assert "optional" in DATA_CATEGORIES

        # Check essential categories
        for category in DATA_CATEGORIES["essential"]:
            assert "name" in category
            assert "description" in category
            assert "retention_days" in category
            assert "legal_basis" in category

        # Check optional categories
        for category in DATA_CATEGORIES["optional"]:
            assert "name" in category
            assert "description" in category
            assert "retention_days" in category
            assert "cookie_category" in category

    def test_retention_days_values(self):
        """Test that retention days are reasonable"""
        try:
            from gdpr_api import DATA_CATEGORIES
        except ImportError:
            pytest.skip("gdpr_api module not available")

        all_categories = DATA_CATEGORIES["essential"] + DATA_CATEGORIES["optional"]

        for category in all_categories:
            retention = category.get("retention_days")
            if retention is not None and isinstance(retention, int):
                assert retention > 0, f"Retention for {category['name']} should be positive"
                assert retention <= 3650, f"Retention for {category['name']} shouldn't exceed 10 years"


class TestGDPRCompliance:
    """Tests für GDPR Compliance"""

    def test_gdpr_rights_covered(self):
        """Test that all GDPR rights are addressable"""
        gdpr_rights = {
            "art_15": "Right of access",       # Auskunftsrecht
            "art_16": "Right to rectification", # Berichtigungsrecht
            "art_17": "Right to erasure",       # Löschungsrecht
            "art_18": "Right to restriction",   # Einschränkungsrecht
            "art_20": "Right to portability",   # Datenübertragbarkeit
            "art_21": "Right to object",        # Widerspruchsrecht
        }

        # These should all be implementable via the consent service
        for article, right in gdpr_rights.items():
            assert right is not None, f"GDPR {article} ({right}) should be covered"

    def test_mandatory_documents(self):
        """Test that mandatory legal documents are defined"""
        mandatory_docs = ["terms", "privacy"]

        for doc in mandatory_docs:
            assert doc in mandatory_docs, f"Document {doc} should be mandatory"

    def test_cookie_categories_defined(self):
        """Test that cookie categories follow GDPR requirements"""
        expected_categories = ["necessary", "functional", "analytics", "marketing"]

        # Necessary cookies must be allowed without consent
        assert "necessary" in expected_categories

        # Optional categories require consent
        optional = [c for c in expected_categories if c != "necessary"]
        assert len(optional) > 0


class TestRetentionPolicies:
    """Tests für Löschfristen"""

    def test_session_data_retention(self):
        """Test that session data has short retention"""
        session_retention_days = 1  # Expected: 24 hours max
        assert session_retention_days <= 7, "Session data should be retained for max 7 days"

    def test_audit_log_retention(self):
        """Test audit log retention complies with legal requirements"""
        # Audit logs must be kept for compliance but not indefinitely
        audit_retention_days = 1095  # 3 years
        assert audit_retention_days >= 365, "Audit logs should be kept for at least 1 year"
        assert audit_retention_days <= 3650, "Audit logs shouldn't be kept more than 10 years"

    def test_consent_record_retention(self):
        """Test that consent records are kept long enough for proof"""
        # § 7a UWG requires proof of consent for 3 years
        consent_retention_days = 1095
        assert consent_retention_days >= 1095, "Consent records must be kept for at least 3 years"

    def test_ip_address_retention(self):
        """Test IP address retention is minimized"""
        ip_retention_days = 28  # 4 weeks
        assert ip_retention_days <= 90, "IP addresses should not be stored longer than 90 days"


class TestDataMinimization:
    """Tests für Datensparsamkeit"""

    def test_password_not_stored_plain(self):
        """Test that passwords are never stored in plain text"""
        # This is a design requirement test
        assert True, "Passwords must be hashed with bcrypt"

    def test_unnecessary_data_not_collected(self):
        """Test that only necessary data is collected"""
        # User model should only contain necessary fields
        required_fields = ["id", "email", "password_hash", "created_at"]
        optional_fields = ["name", "role"]

        # No excessive personal data
        forbidden_fields = ["ssn", "credit_card", "date_of_birth", "address"]

        for field in forbidden_fields:
            assert field not in required_fields, f"Field {field} should not be required"


class TestAnonymization:
    """Tests für Anonymisierung"""

    def test_ip_anonymization(self):
        """Test IP address anonymization logic"""
        def anonymize_ip(ip: str) -> str:
            """Anonymize IPv4 by zeroing last octet"""
            parts = ip.split(".")
            if len(parts) == 4:
                parts[3] = "0"
                return ".".join(parts)
            return ip

        test_cases = [
            ("192.168.1.100", "192.168.1.0"),
            ("10.0.0.1", "10.0.0.0"),
            ("172.16.255.255", "172.16.255.0"),
        ]

        for original, expected in test_cases:
            assert anonymize_ip(original) == expected

    def test_user_data_anonymization(self):
        """Test user data anonymization for deleted accounts"""
        def anonymize_user_data(user_data: dict) -> dict:
            """Anonymize user data while keeping audit trail"""
            anonymized = user_data.copy()
            anonymized["email"] = f"deleted-{user_data['id']}@anonymized.local"
            anonymized["name"] = None
            anonymized["password_hash"] = None
            return anonymized

        original = {
            "id": "123",
            "email": "real@example.com",
            "name": "John Doe",
            "password_hash": "bcrypt_hash"
        }

        anonymized = anonymize_user_data(original)

        assert "@anonymized.local" in anonymized["email"]
        assert anonymized["name"] is None
        assert anonymized["password_hash"] is None
        assert anonymized["id"] == original["id"]  # ID preserved for audit


class TestExportFormat:
    """Tests für Datenexport-Format"""

    def test_export_includes_all_user_data(self):
        """Test that export includes all required data sections"""
        required_sections = [
            "user",           # Personal data
            "consents",       # Consent history
            "cookie_consents", # Cookie preferences
            "audit_log",      # Activity log
            "exported_at",    # Export timestamp
        ]

        # Mock export response
        mock_export = {
            "user": {},
            "consents": [],
            "cookie_consents": [],
            "audit_log": [],
            "exported_at": "2024-01-01T00:00:00Z"
        }

        for section in required_sections:
            assert section in mock_export, f"Export must include {section}"

    def test_export_is_machine_readable(self):
        """Test that export can be provided in machine-readable format"""
        import json

        mock_data = {
            "user": {"email": "test@example.com"},
            "exported_at": "2024-01-01T00:00:00Z"
        }

        # Should be valid JSON
        json_str = json.dumps(mock_data)
        parsed = json.loads(json_str)

        assert parsed == mock_data


class TestConsentValidation:
    """Tests für Consent-Validierung"""

    def test_consent_requires_version_id(self):
        """Test that consent requires a specific document version"""
        consent_request = {
            "document_type": "terms",
            "version_id": "version-123",
            "consented": True
        }

        assert "version_id" in consent_request
        assert consent_request["version_id"] is not None

    def test_consent_tracks_ip_and_timestamp(self):
        """Test that consent tracks IP and timestamp for proof"""
        consent_record = {
            "user_id": "user-123",
            "document_version_id": "version-123",
            "consented": True,
            "ip_address": "192.168.1.1",
            "consented_at": "2024-01-01T00:00:00Z"
        }

        assert "ip_address" in consent_record
        assert "consented_at" in consent_record

    def test_withdrawal_is_possible(self):
        """Test that consent can be withdrawn (Art. 7(3) GDPR)"""
        # Withdrawal should be as easy as giving consent
        withdraw_request = {
            "consent_id": "consent-123"
        }

        assert "consent_id" in withdraw_request


class TestSecurityHeaders:
    """Tests für Sicherheits-Header"""

    def test_required_security_headers(self):
        """Test that API responses include security headers"""
        required_headers = [
            "X-Content-Type-Options",    # nosniff
            "X-Frame-Options",           # DENY or SAMEORIGIN
            "Content-Security-Policy",   # CSP
            "X-XSS-Protection",          # Legacy but useful
        ]

        # These should be set by the application
        for header in required_headers:
            assert header is not None, f"Header {header} should be set"


if __name__ == "__main__":
    pytest.main([__file__, "-v"])
