"""Tests for Legal Template routes and schemas (legal_template_routes.py)."""

import json
import pytest
from datetime import datetime
from unittest.mock import MagicMock, patch
from fastapi.testclient import TestClient
from fastapi import FastAPI

from compliance.api.legal_template_routes import (
    LegalTemplateCreate,
    LegalTemplateUpdate,
    VALID_DOCUMENT_TYPES,
    VALID_STATUSES,
    router,
)
from compliance.api.db_utils import row_to_dict as _row_to_dict

DEFAULT_TENANT_ID = "9282a473-5c95-4b3a-bf78-0ecc0ec71d3e"
from classroom_engine.database import get_db

app = FastAPI()
app.include_router(router)

DEFAULT_TENANT = DEFAULT_TENANT_ID
TEMPLATE_ID = "ffffffff-0001-0001-0001-000000000001"
UNKNOWN_ID = "aaaaaaaa-9999-9999-9999-999999999999"

DSE_PLACEHOLDERS = [
    "{{COMPANY_NAME}}", "{{COMPANY_ADDRESS}}", "{{CONTACT_EMAIL}}", "{{VERSION_DATE}}"
]


# =============================================================================
# Helpers
# =============================================================================

def make_template_row(overrides=None):
    data = {
        "id": TEMPLATE_ID,
        "tenant_id": DEFAULT_TENANT,
        "document_type": "privacy_policy",
        "title": "Datenschutzerklärung (DSGVO-konform)",
        "description": "Vollständige DSE gemäß DSGVO Art. 13/14",
        "content": "# Datenschutzerklärung\n\n## 1. Verantwortlicher\n\n{{COMPANY_NAME}}",
        "placeholders": DSE_PLACEHOLDERS,
        "language": "de",
        "jurisdiction": "DE",
        "license_id": "mit",
        "license_name": "MIT License",
        "source_name": "BreakPilot Compliance",
        "attribution_required": False,
        "is_complete_document": True,
        "version": "1.0.0",
        "status": "published",
        "created_at": datetime(2024, 1, 1),
        "updated_at": datetime(2024, 1, 1),
        # Attribution columns (Migration 019)
        "source_url": None,
        "source_repo": None,
        "source_file_path": None,
        "source_retrieved_at": None,
        "attribution_text": "Self-authored by BreakPilot Compliance (MIT License, 2026-03-04).",
        "inspiration_sources": [{"source": "Self-authored", "license": "MIT"}],
    }
    if overrides:
        data.update(overrides)
    row = MagicMock()
    row._mapping = data
    return row


def make_db_mock():
    db = MagicMock()
    return db


# =============================================================================
# TestLegalTemplateSchemas
# =============================================================================

class TestLegalTemplateSchemas:
    def test_create_schema_defaults(self):
        """LegalTemplateCreate sets sensible defaults."""
        payload = LegalTemplateCreate(
            document_type="privacy_policy",
            title="Test DSE",
            content="# Test"
        )
        assert payload.language == "de"
        assert payload.jurisdiction == "DE"
        assert payload.license_id == "mit"
        assert payload.license_name == "MIT License"
        assert payload.source_name == "BreakPilot Compliance"
        assert payload.attribution_required is False
        assert payload.is_complete_document is True
        assert payload.version == "1.0.0"
        assert payload.status == "published"

    def test_create_schema_with_placeholders(self):
        """LegalTemplateCreate accepts placeholder list."""
        payload = LegalTemplateCreate(
            document_type="impressum",
            title="Impressum",
            content="# Impressum\n{{COMPANY_NAME}}",
            placeholders=["{{COMPANY_NAME}}", "{{CEO_NAME}}"]
        )
        assert len(payload.placeholders) == 2
        assert "{{COMPANY_NAME}}" in payload.placeholders

    def test_update_schema_all_optional(self):
        """LegalTemplateUpdate: all fields optional."""
        payload = LegalTemplateUpdate()
        d = payload.model_dump(exclude_unset=True)
        assert d == {}

    def test_update_schema_partial(self):
        """LegalTemplateUpdate partial: only set fields serialized."""
        payload = LegalTemplateUpdate(status="archived", title="Neue DSE")
        d = payload.model_dump(exclude_unset=True)
        assert d == {"status": "archived", "title": "Neue DSE"}

    def test_valid_document_types_constant(self):
        """VALID_DOCUMENT_TYPES contains all 58 expected types (Migration 020+051+054+056+073)."""
        # Original types
        assert "privacy_policy" in VALID_DOCUMENT_TYPES
        assert "terms_of_service" in VALID_DOCUMENT_TYPES
        assert "impressum" in VALID_DOCUMENT_TYPES
        assert "cookie_policy" in VALID_DOCUMENT_TYPES
        # Renamed types (Migration 020)
        assert "dpa" in VALID_DOCUMENT_TYPES
        assert "widerruf" in VALID_DOCUMENT_TYPES
        # New types (Migration 020)
        assert "nda" in VALID_DOCUMENT_TYPES
        assert "sla" in VALID_DOCUMENT_TYPES
        assert "acceptable_use" in VALID_DOCUMENT_TYPES
        assert "community_guidelines" in VALID_DOCUMENT_TYPES
        assert "copyright_policy" in VALID_DOCUMENT_TYPES
        assert "cloud_service_agreement" in VALID_DOCUMENT_TYPES
        assert "data_usage_clause" in VALID_DOCUMENT_TYPES
        assert "cookie_banner" in VALID_DOCUMENT_TYPES
        assert "agb" in VALID_DOCUMENT_TYPES
        assert "clause" in VALID_DOCUMENT_TYPES
        # Security concepts (Migration 051)
        assert "it_security_concept" in VALID_DOCUMENT_TYPES
        assert "data_protection_concept" in VALID_DOCUMENT_TYPES
        assert "backup_recovery_concept" in VALID_DOCUMENT_TYPES
        assert "logging_concept" in VALID_DOCUMENT_TYPES
        assert "incident_response_plan" in VALID_DOCUMENT_TYPES
        assert "access_control_concept" in VALID_DOCUMENT_TYPES
        assert "risk_management_concept" in VALID_DOCUMENT_TYPES
        # Policy templates (Migration 054) — spot check
        assert "information_security_policy" in VALID_DOCUMENT_TYPES
        assert "data_protection_policy" in VALID_DOCUMENT_TYPES
        assert "business_continuity_policy" in VALID_DOCUMENT_TYPES
        # CRA Cybersecurity (Migration 056)
        assert "cybersecurity_policy" in VALID_DOCUMENT_TYPES
        # DSFA template
        assert "dsfa" in VALID_DOCUMENT_TYPES
        # Module document templates (Migration 073)
        assert "vvt_register" in VALID_DOCUMENT_TYPES
        assert "tom_documentation" in VALID_DOCUMENT_TYPES
        assert "loeschkonzept" in VALID_DOCUMENT_TYPES
        assert "pflichtenregister" in VALID_DOCUMENT_TYPES
        # Total: 16 original + 7 security concepts + 29 policies + 1 CRA + 1 DSFA + 4 module docs = 58
        assert len(VALID_DOCUMENT_TYPES) == 58
        # Old names must NOT be present after rename
        assert "data_processing_agreement" not in VALID_DOCUMENT_TYPES
        assert "withdrawal_policy" not in VALID_DOCUMENT_TYPES

    def test_create_schema_attribution_fields(self):
        """LegalTemplateCreate accepts attribution fields (uses dpa, post-Migration 020)."""
        payload = LegalTemplateCreate(
            document_type="dpa",
            title="AVV Test",
            content="# AVV\n{{CONTROLLER_NAME}}",
            source_url="https://github.com/example/legal",
            source_repo="https://github.com/example/legal",
            source_file_path="dpa-de.md",
            attribution_text="Self-authored MIT.",
            inspiration_sources=[{"source": "test", "license": "MIT"}],
        )
        assert payload.source_url == "https://github.com/example/legal"
        assert payload.attribution_text == "Self-authored MIT."
        assert len(payload.inspiration_sources) == 1

    def test_update_schema_inspiration_sources(self):
        """LegalTemplateUpdate accepts inspiration_sources list."""
        payload = LegalTemplateUpdate(
            inspiration_sources=[{"source": "Self-authored", "license": "MIT"}]
        )
        d = payload.model_dump(exclude_unset=True)
        assert "inspiration_sources" in d
        assert d["inspiration_sources"][0]["license"] == "MIT"

    def test_valid_statuses_constant(self):
        """VALID_STATUSES contains expected values."""
        assert "published" in VALID_STATUSES
        assert "draft" in VALID_STATUSES
        assert "archived" in VALID_STATUSES


# =============================================================================
# TestLegalTemplateDB
# =============================================================================

class TestLegalTemplateDB:
    def test_row_to_dict_converts_datetime(self):
        """_row_to_dict converts datetime to ISO string."""
        row = make_template_row()
        result = _row_to_dict(row)
        assert result["created_at"] == "2024-01-01T00:00:00"
        assert result["updated_at"] == "2024-01-01T00:00:00"

    def test_row_to_dict_preserves_strings(self):
        """_row_to_dict preserves string fields unchanged."""
        row = make_template_row()
        result = _row_to_dict(row)
        assert result["title"] == "Datenschutzerklärung (DSGVO-konform)"
        assert result["license_id"] == "mit"
        assert result["document_type"] == "privacy_policy"

    def test_row_to_dict_preserves_list(self):
        """_row_to_dict preserves list fields (placeholders)."""
        row = make_template_row()
        result = _row_to_dict(row)
        assert isinstance(result["placeholders"], list)
        assert "{{COMPANY_NAME}}" in result["placeholders"]


# =============================================================================
# TestLegalTemplateSearch
# =============================================================================

class TestLegalTemplateSearch:
    def setup_method(self):
        self.db = make_db_mock()
        app.dependency_overrides[get_db] = lambda: self.db
        self.client = TestClient(app)

    def teardown_method(self):
        app.dependency_overrides.clear()

    def _setup_list(self, rows, total=None):
        count_row = MagicMock()
        count_row.__getitem__ = lambda self, i: total if total is not None else len(rows)
        self.db.execute.return_value.fetchone.side_effect = [count_row]
        self.db.execute.return_value.fetchall.return_value = rows

    def test_list_returns_200(self):
        """GET /legal-templates returns 200 with templates list."""
        rows = [make_template_row()]
        count_mock = MagicMock()
        count_mock.__getitem__ = lambda self, i: 1

        execute_results = []
        first_call = MagicMock()
        first_call.fetchone.return_value = count_mock
        second_call = MagicMock()
        second_call.fetchall.return_value = rows
        self.db.execute.side_effect = [first_call, second_call]

        res = self.client.get("/legal-templates")
        assert res.status_code == 200
        data = res.json()
        assert "templates" in data
        assert "total" in data

    def test_list_filter_by_document_type(self):
        """GET /legal-templates?document_type=impressum passes filter."""
        count_mock = MagicMock()
        count_mock.__getitem__ = lambda self, i: 0
        first_call = MagicMock()
        first_call.fetchone.return_value = count_mock
        second_call = MagicMock()
        second_call.fetchall.return_value = []
        self.db.execute.side_effect = [first_call, second_call]

        res = self.client.get("/legal-templates?document_type=impressum")
        assert res.status_code == 200
        # Verify the SQL call used document_type filter
        call_args = self.db.execute.call_args_list[0]
        sql_str = str(call_args[0][0])
        assert "document_type" in sql_str

    def test_create_invalid_document_type_returns_400(self):
        """POST /legal-templates with invalid document_type returns 400."""
        res = self.client.post("/legal-templates", json={
            "document_type": "unknown_type",
            "title": "Test",
            "content": "# Test"
        })
        assert res.status_code == 400
        assert "document_type" in res.json()["detail"]

    def test_create_invalid_status_returns_400(self):
        """POST /legal-templates with invalid status returns 400."""
        res = self.client.post("/legal-templates", json={
            "document_type": "privacy_policy",
            "title": "Test",
            "content": "# Test",
            "status": "invalid_status"
        })
        assert res.status_code == 400

    def test_get_nonexistent_returns_404(self):
        """GET /legal-templates/{id} for unknown ID returns 404."""
        self.db.execute.return_value.fetchone.return_value = None
        res = self.client.get(f"/legal-templates/{UNKNOWN_ID}")
        assert res.status_code == 404

    def test_update_no_fields_returns_400(self):
        """PUT /legal-templates/{id} with empty body returns 400."""
        res = self.client.put(f"/legal-templates/{TEMPLATE_ID}", json={})
        assert res.status_code == 400
        assert "No fields" in res.json()["detail"]

    def test_delete_nonexistent_returns_404(self):
        """DELETE /legal-templates/{id} for unknown ID returns 404."""
        result_mock = MagicMock()
        result_mock.rowcount = 0
        self.db.execute.return_value = result_mock
        res = self.client.delete(f"/legal-templates/{UNKNOWN_ID}")
        assert res.status_code == 404

    def test_status_endpoint_returns_200(self):
        """GET /legal-templates/status returns dynamic count structure."""
        # Mock 3 sequential execute calls: total, by_status, by_type
        total_mock = MagicMock()
        total_mock.__getitem__ = lambda self, i: 20

        status_rows = [
            MagicMock(**{"__getitem__": lambda s, i: ["published", 18][i]}),
            MagicMock(**{"__getitem__": lambda s, i: ["draft", 2][i]}),
        ]
        type_rows = [
            MagicMock(**{"__getitem__": lambda s, i: ["privacy_policy", 2][i]}),
            MagicMock(**{"__getitem__": lambda s, i: ["dpa", 2][i]}),
            MagicMock(**{"__getitem__": lambda s, i: ["nda", 2][i]}),
            MagicMock(**{"__getitem__": lambda s, i: ["widerruf", 1][i]}),
        ]

        call1 = MagicMock(); call1.fetchone.return_value = total_mock
        call2 = MagicMock(); call2.fetchall.return_value = status_rows
        call3 = MagicMock(); call3.fetchall.return_value = type_rows
        self.db.execute.side_effect = [call1, call2, call3]

        res = self.client.get("/legal-templates/status")
        assert res.status_code == 200
        data = res.json()
        assert "total" in data
        assert "by_type" in data
        assert "by_status" in data
        assert "dpa" in data["by_type"]
        assert "nda" in data["by_type"]
        # Old names must not appear (they were renamed)
        assert "data_processing_agreement" not in data["by_type"]
        assert "withdrawal_policy" not in data["by_type"]

    def test_sources_endpoint_returns_list(self):
        """GET /legal-templates/sources returns sources list."""
        self.db.execute.return_value.fetchall.return_value = [
            ("BreakPilot Compliance",),
        ]
        res = self.client.get("/legal-templates/sources")
        assert res.status_code == 200
        data = res.json()
        assert "sources" in data
        assert "BreakPilot Compliance" in data["sources"]


# =============================================================================
# TestLegalTemplateSeed
# =============================================================================

class TestLegalTemplateSeed:
    """Validate that seed template structures are correct."""

    def test_dse_placeholders_present(self):
        """DSE template row has expected placeholder tokens."""
        row = make_template_row({
            "document_type": "privacy_policy",
            "placeholders": [
                "{{COMPANY_NAME}}", "{{COMPANY_ADDRESS}}", "{{COMPANY_CITY}}",
                "{{CONTACT_EMAIL}}", "{{DPO_NAME}}", "{{DPO_EMAIL}}",
                "{{SUPERVISORY_AUTHORITY}}", "{{VERSION_DATE}}"
            ]
        })
        result = _row_to_dict(row)
        assert "{{COMPANY_NAME}}" in result["placeholders"]
        assert "{{CONTACT_EMAIL}}" in result["placeholders"]
        assert "{{VERSION_DATE}}" in result["placeholders"]
        assert len(result["placeholders"]) == 8

    def test_impressum_has_ceo_placeholder(self):
        """Impressum template row has CEO_NAME placeholder."""
        row = make_template_row({
            "document_type": "impressum",
            "placeholders": [
                "{{COMPANY_NAME}}", "{{COMPANY_LEGAL_FORM}}", "{{COMPANY_ADDRESS}}",
                "{{COMPANY_CITY}}", "{{CEO_NAME}}", "{{COMPANY_PHONE}}",
                "{{CONTACT_EMAIL}}", "{{WEBSITE_URL}}", "{{REGISTER_SECTION}}", "{{VAT_SECTION}}"
            ]
        })
        result = _row_to_dict(row)
        assert "{{CEO_NAME}}" in result["placeholders"]
        assert "{{WEBSITE_URL}}" in result["placeholders"]

    def test_agb_has_pricing_placeholder(self):
        """AGB template row has PRICING_SECTION placeholder."""
        row = make_template_row({
            "document_type": "terms_of_service",
            "placeholders": [
                "{{COMPANY_NAME}}", "{{SERVICE_DESCRIPTION}}", "{{PRICING_SECTION}}",
                "{{PAYMENT_TERMS}}", "{{COMPANY_CITY}}", "{{PRIVACY_POLICY_URL}}", "{{VERSION_DATE}}"
            ]
        })
        result = _row_to_dict(row)
        assert "{{PRICING_SECTION}}" in result["placeholders"]
        assert "{{PAYMENT_TERMS}}" in result["placeholders"]
        assert "{{PRIVACY_POLICY_URL}}" in result["placeholders"]

    def test_all_seeds_use_mit_license(self):
        """All seed templates declare MIT license and no attribution required."""
        for doc_type in ["privacy_policy", "terms_of_service", "impressum"]:
            row = make_template_row({"document_type": doc_type})
            result = _row_to_dict(row)
            assert result["license_id"] == "mit"
            assert result["license_name"] == "MIT License"
            assert result["attribution_required"] is False
            assert result["is_complete_document"] is True

    def test_dpa_template_has_controller_processor_placeholders(self):
        """AVV/DPA template has CONTROLLER_NAME and PROCESSOR_NAME placeholders."""
        row = make_template_row({
            "document_type": "dpa",
            "placeholders": [
                "{{CONTROLLER_NAME}}", "{{CONTROLLER_ADDRESS}}", "{{CONTROLLER_CITY}}",
                "{{PROCESSOR_NAME}}", "{{PROCESSOR_ADDRESS}}", "{{PROCESSOR_CITY}}",
                "{{SERVICE_DESCRIPTION}}", "{{DATA_CATEGORIES}}", "{{DATA_SUBJECTS}}",
                "{{CONTRACT_DATE}}", "{{SUPERVISORY_AUTHORITY}}"
            ]
        })
        result = _row_to_dict(row)
        assert "{{CONTROLLER_NAME}}" in result["placeholders"]
        assert "{{PROCESSOR_NAME}}" in result["placeholders"]
        assert "{{DATA_CATEGORIES}}" in result["placeholders"]

    def test_withdrawal_policy_has_public_domain_license(self):
        """Widerrufsbelehrung uses Public Domain license (based on §5 UrhG source)."""
        row = make_template_row({
            "document_type": "widerruf",
            "license_id": "public_domain",
            "license_name": "Public Domain / Gemeinfrei",
            "source_url": "https://www.gesetze-im-internet.de/egbgb/anlage_1.html",
            "attribution_text": "Basiert auf der amtlichen Musterwiderrufsbelehrung.",
            "inspiration_sources": [{"source": "EGBGB Anlage 1", "license": "Public Domain gemäß §5 UrhG"}],
        })
        result = _row_to_dict(row)
        assert result["document_type"] == "widerruf"
        assert result["license_id"] == "public_domain"
        assert result["source_url"] == "https://www.gesetze-im-internet.de/egbgb/anlage_1.html"
        assert result["attribution_text"] is not None

    def test_attribution_text_stored_in_row(self):
        """attribution_text and inspiration_sources are returned in _row_to_dict."""
        row = make_template_row({
            "attribution_text": "Self-authored by BreakPilot Compliance (MIT License, 2026-03-04).",
            "inspiration_sources": [{"source": "Self-authored", "license": "MIT"}],
            "source_url": None,
            "source_repo": None,
            "source_file_path": None,
            "source_retrieved_at": None,
        })
        result = _row_to_dict(row)
        assert "Self-authored" in result["attribution_text"]
        assert result["inspiration_sources"][0]["license"] == "MIT"

    def test_english_privacy_policy_eu_jurisdiction(self):
        """EN Privacy Policy template uses EU jurisdiction."""
        row = make_template_row({
            "document_type": "privacy_policy",
            "language": "en",
            "jurisdiction": "EU",
            "title": "Privacy Policy (GDPR-compliant, EU)",
        })
        result = _row_to_dict(row)
        assert result["language"] == "en"
        assert result["jurisdiction"] == "EU"

    def test_cookie_policy_type_valid(self):
        """cookie_policy is accepted as valid document_type."""
        assert "cookie_policy" in VALID_DOCUMENT_TYPES

    def test_widerruf_type_valid(self):
        """widerruf is accepted as valid document_type (renamed from withdrawal_policy)."""
        assert "widerruf" in VALID_DOCUMENT_TYPES
        assert "withdrawal_policy" not in VALID_DOCUMENT_TYPES

    def test_dpa_type_valid(self):
        """dpa is accepted as valid document_type (renamed from data_processing_agreement)."""
        assert "dpa" in VALID_DOCUMENT_TYPES
        assert "data_processing_agreement" not in VALID_DOCUMENT_TYPES


# =============================================================================
# TestLegalTemplateNewTypes
# =============================================================================

class TestLegalTemplateNewTypes:
    """Validate new document types added in Migration 020."""

    def test_all_58_types_present(self):
        """VALID_DOCUMENT_TYPES has exactly 58 entries (16 + 7 security + 29 policies + 1 CRA + 1 DSFA + 4 module docs)."""
        assert len(VALID_DOCUMENT_TYPES) == 58

    def test_new_types_are_valid(self):
        """All Migration 020 new types are accepted."""
        new_types = ["nda", "sla", "acceptable_use", "community_guidelines",
                     "copyright_policy", "cloud_service_agreement",
                     "data_usage_clause", "cookie_banner", "agb", "clause"]
        for t in new_types:
            assert t in VALID_DOCUMENT_TYPES, f"Type '{t}' missing from VALID_DOCUMENT_TYPES"

    def test_nda_template_has_parties_placeholders(self):
        """NDA template has DISCLOSING_PARTY and RECEIVING_PARTY placeholders."""
        row = make_template_row({
            "document_type": "nda",
            "title": "Geheimhaltungsvereinbarung (NDA) — DE",
            "language": "de",
            "placeholders": [
                "{{DISCLOSING_PARTY}}", "{{RECEIVING_PARTY}}", "{{PURPOSE}}",
                "{{EFFECTIVE_DATE}}", "{{DURATION_YEARS}}", "{{PENALTY_AMOUNT}}",
                "{{JURISDICTION_CITY}}"
            ],
        })
        result = _row_to_dict(row)
        assert result["document_type"] == "nda"
        assert "{{DISCLOSING_PARTY}}" in result["placeholders"]
        assert "{{RECEIVING_PARTY}}" in result["placeholders"]
        assert "{{PURPOSE}}" in result["placeholders"]

    def test_nda_en_template_is_valid(self):
        """NDA EN template has EU jurisdiction and English language."""
        row = make_template_row({
            "document_type": "nda",
            "title": "Non-Disclosure Agreement (NDA) — EN (EU)",
            "language": "en",
            "jurisdiction": "EU",
        })
        result = _row_to_dict(row)
        assert result["document_type"] == "nda"
        assert result["language"] == "en"
        assert result["jurisdiction"] == "EU"

    def test_sla_template_has_availability_placeholder(self):
        """SLA template includes AVAILABILITY_PERCENT placeholder."""
        row = make_template_row({
            "document_type": "sla",
            "placeholders": [
                "{{SERVICE_PROVIDER}}", "{{CUSTOMER}}", "{{SERVICE_NAME}}",
                "{{AVAILABILITY_PERCENT}}", "{{SUPPORT_EMAIL}}", "{{JURISDICTION_CITY}}"
            ],
        })
        result = _row_to_dict(row)
        assert result["document_type"] == "sla"
        assert "{{AVAILABILITY_PERCENT}}" in result["placeholders"]

    def test_acceptable_use_type_valid(self):
        """acceptable_use is a valid document type."""
        assert "acceptable_use" in VALID_DOCUMENT_TYPES

    def test_acceptable_use_template_structure(self):
        """AUP template has required placeholders."""
        row = make_template_row({
            "document_type": "acceptable_use",
            "language": "en",
            "jurisdiction": "EU",
            "placeholders": [
                "{{COMPANY_NAME}}", "{{SERVICE_NAME}}", "{{EFFECTIVE_DATE}}",
                "{{ABUSE_EMAIL}}", "{{COMPANY_ADDRESS}}", "{{CONTACT_EMAIL}}"
            ],
        })
        result = _row_to_dict(row)
        assert result["document_type"] == "acceptable_use"
        assert "{{ABUSE_EMAIL}}" in result["placeholders"]

    def test_community_guidelines_type_valid(self):
        """community_guidelines is a valid document type."""
        assert "community_guidelines" in VALID_DOCUMENT_TYPES

    def test_agb_type_valid(self):
        """agb is a valid document type (German AGB, distinct from terms_of_service)."""
        assert "agb" in VALID_DOCUMENT_TYPES

    def test_agb_template_has_pricing_and_termination(self):
        """AGB template has PRICING_SECTION and TERMINATION_NOTICE_DAYS placeholders."""
        row = make_template_row({
            "document_type": "agb",
            "placeholders": [
                "{{COMPANY_NAME}}", "{{COMPANY_ADDRESS}}", "{{VERSION_DATE}}",
                "{{SERVICE_NAME}}", "{{SERVICE_DESCRIPTION}}", "{{PRICING_SECTION}}",
                "{{PAYMENT_DAYS}}", "{{PRIVACY_POLICY_URL}}",
                "{{TERMINATION_NOTICE_DAYS}}", "{{JURISDICTION_CITY}}"
            ],
        })
        result = _row_to_dict(row)
        assert result["document_type"] == "agb"
        assert "{{PRICING_SECTION}}" in result["placeholders"]
        assert "{{TERMINATION_NOTICE_DAYS}}" in result["placeholders"]

    def test_clause_type_is_modular(self):
        """clause document type can be a non-complete document (is_complete_document=False)."""
        row = make_template_row({
            "document_type": "clause",
            "is_complete_document": False,
            "language": "en",
        })
        result = _row_to_dict(row)
        assert result["document_type"] == "clause"
        assert result["is_complete_document"] is False

    def test_cloud_service_agreement_type_valid(self):
        """cloud_service_agreement is a valid document type."""
        assert "cloud_service_agreement" in VALID_DOCUMENT_TYPES

    def test_data_usage_clause_is_non_complete(self):
        """data_usage_clause is typically a clause (non-complete document)."""
        assert "data_usage_clause" in VALID_DOCUMENT_TYPES

    def test_cookie_banner_type_valid(self):
        """cookie_banner is a valid document type."""
        assert "cookie_banner" in VALID_DOCUMENT_TYPES

    def test_copyright_policy_type_valid(self):
        """copyright_policy is a valid document type."""
        assert "copyright_policy" in VALID_DOCUMENT_TYPES

    def test_create_nda_with_new_type(self):
        """POST /legal-templates accepts nda as document_type."""
        from fastapi.testclient import TestClient
        db = MagicMock()
        app2 = __import__('fastapi', fromlist=['FastAPI']).FastAPI()
        app2.include_router(router)
        from classroom_engine.database import get_db
        app2.dependency_overrides[get_db] = lambda: db

        row = make_template_row({"document_type": "nda", "title": "NDA Test"})
        db.execute.return_value.fetchone.return_value = row
        db.commit = MagicMock()

        client = TestClient(app2)
        res = client.post("/legal-templates", json={
            "document_type": "nda",
            "title": "NDA Test",
            "content": "# NDA\n{{DISCLOSING_PARTY}}"
        })
        assert res.status_code == 201

    def test_create_with_old_dpa_name_rejected(self):
        """POST /legal-templates rejects data_processing_agreement (old name)."""
        from fastapi.testclient import TestClient
        db = MagicMock()
        app2 = __import__('fastapi', fromlist=['FastAPI']).FastAPI()
        app2.include_router(router)
        from classroom_engine.database import get_db
        app2.dependency_overrides[get_db] = lambda: db

        client = TestClient(app2)
        res = client.post("/legal-templates", json={
            "document_type": "data_processing_agreement",
            "title": "AVV Old Name",
            "content": "# AVV"
        })
        assert res.status_code == 400

    def test_create_with_old_withdrawal_policy_name_rejected(self):
        """POST /legal-templates rejects withdrawal_policy (old name)."""
        from fastapi.testclient import TestClient
        db = MagicMock()
        app2 = __import__('fastapi', fromlist=['FastAPI']).FastAPI()
        app2.include_router(router)
        from classroom_engine.database import get_db
        app2.dependency_overrides[get_db] = lambda: db

        client = TestClient(app2)
        res = client.post("/legal-templates", json={
            "document_type": "withdrawal_policy",
            "title": "Widerruf Old Name",
            "content": "# Widerruf"
        })
        assert res.status_code == 400
