"""
CRA Document Templates — generates Markdown documents from project state.

Each template takes a project dict + optional context (sboms, vulnerabilities,
checks) and returns (title, markdown_content). Pure functions, no DB access.

Templates implement:
- doc_eu_conformity   — EU Declaration of Conformity (CRA Annex VII)
- doc_technical       — Technische Dokumentation (CRA Annex V)
- doc_cvd_policy      — Coordinated Vulnerability Disclosure Policy
- doc_update_policy   — Update / Patch Policy
- doc_sbom_report     — SBOM-Zusammenfassung
"""

from datetime import datetime
from typing import Optional


PATH_LABELS = {
    "self_assessment": "Modul A — Interne Kontrolle (Selbstbewertung durch Hersteller)",
    "harmonized_standard": "Harmonisierte Normen — Konformitaetsvermutung (noch nicht verfuegbar)",
    "eucc": "EUCC — EU-Cybersicherheitszertifikat",
    "notified_body": "Modul B+C — Pruefung durch benannte Stelle (Baumusterpruefung)",
}

CLASS_LABELS = {
    "NOT_IN_SCOPE": "Nicht im CRA-Scope",
    "STANDARD": "Standard-Produkt mit digitalen Elementen",
    "IMPORTANT_I": "Wichtiges Produkt — Annex III Klasse I",
    "IMPORTANT_II": "Wichtiges Produkt — Annex III Klasse II",
    "CRITICAL": "Kritisches Produkt — Annex IV",
}


def _today() -> str:
    return datetime.utcnow().strftime("%Y-%m-%d")


def _fmt_or_placeholder(v: Optional[str], placeholder: str = "[zu ergaenzen]") -> str:
    return v if v else placeholder


def doc_eu_conformity(project: dict, *, manufacturer: Optional[str] = None,
                      notified_body: Optional[str] = None) -> tuple[str, str]:
    """EU Declaration of Conformity nach CRA Annex VII.

    Pflichtfelder gemaess CRA Art. 28 + Annex VII:
    1. Produkt + Modell + eindeutige Kennung
    2. Hersteller + Anschrift
    3. (Optional) Bevollmaechtigter
    4. Konformitaetserklaerung mit Bezug auf CRA
    5. Verweis auf angewandte harmonisierte Normen
    6. Notified Body (falls Modul C)
    7. Datum + Ort + Unterschrift
    """
    name = project.get("name", "[zu ergaenzen]")
    classification = project.get("cra_classification", "unbewertet")
    path = project.get("conformity_path", "self_assessment")
    path_label = PATH_LABELS.get(path, path)
    class_label = CLASS_LABELS.get(classification, classification)
    desc = project.get("description") or project.get("intended_use") or ""

    title = f"EU-Konformitaetserklaerung — {name}"

    content = f"""# EU-Konformitaetserklaerung (DoC)
**Gemaess Verordnung (EU) 2024/2847 — Cyber Resilience Act, Anhang VII**

---

## 1. Eindeutige Kennung des Produkts

**Produktname:** {name}
**Modell / Typ:** {_fmt_or_placeholder(project.get('primary_language'), 'Software-Produkt')}
**Repository / Source:** {_fmt_or_placeholder(project.get('repo_url'), '[Repo-URL ergaenzen]')}
**Beschreibung:** {desc or '[Produktbeschreibung ergaenzen]'}

## 2. Hersteller

**Name:** {_fmt_or_placeholder(manufacturer, '[Hersteller-Name + Adresse ergaenzen]')}
**Anschrift:** [zu ergaenzen]
**Kontakt:** [zu ergaenzen]

## 3. Bevollmaechtigter (Authorised Representative)

[Falls Hersteller nicht in EU ansaessig — sonst entfallen]

## 4. Konformitaetserklaerung

Die alleinige Verantwortung fuer die Ausstellung dieser Konformitaetserklaerung
traegt der Hersteller. Der Gegenstand der oben beschriebenen Erklaerung
**erfuellt die einschlaegigen Harmonisierungsrechtsvorschriften der Union**:

- **Verordnung (EU) 2024/2847** (Cyber Resilience Act) — vollstaendig
  einschliesslich der **wesentlichen Cybersicherheitsanforderungen nach
  Anhang I**.

**CRA-Klassifizierung:** {class_label}
**Angewandtes Konformitaetsbewertungsverfahren:** {path_label}

## 5. Verweise auf angewandte harmonisierte Normen und technische Spezifikationen

- Harmonisierte Norm: DIN EN 40000-1-2 (Entwurf, Stand 11/2025)
- ETSI EN 303 645 (Consumer-IoT-Cybersicherheit)
- ISO/IEC 29147 (Vulnerability Disclosure)
- ISO/IEC 30111 (Vulnerability Handling Processes)
- ENISA Technical Implementation Guidance (NIS2-Bezug)

## 6. Notified Body

{'_' if not notified_body else notified_body}
{'Identifikationsnummer: [zu ergaenzen]' if notified_body else '(keine — Self-Assessment durch Hersteller)'}

## 7. Zusatzangaben

Diese Konformitaetserklaerung wurde unterzeichnet im Namen von:

**Ort, Datum:** [zu ergaenzen], {_today()}
**Name, Funktion:** [zu ergaenzen]
**Unterschrift:** ___________________________

---

*Diese Erklaerung wurde automatisch aus dem BreakPilot CRA-Modul generiert.
Pflichtangaben sind als `[zu ergaenzen]` markiert und muessen vor Inverkehrbringen
ausgefuellt + manuell unterzeichnet werden.*
"""
    coverage = {
        "doc_type": "doc_eu_conformity",
        "annex_anchor": "CRA Anhang VII",
        "fields_required": 7,
        "fields_filled": sum(1 for v in [name, classification, path, desc] if v and not str(v).startswith("[")),
    }
    return title, content, coverage


def doc_technical(project: dict) -> tuple[str, str, dict]:
    """Technische Dokumentation nach CRA Annex V."""
    name = project.get("name", "[Produktname]")
    desc = project.get("description") or project.get("intended_use") or ""

    title = f"Technische Dokumentation — {name}"
    content = f"""# Technische Dokumentation
**{name}**
Gemaess Verordnung (EU) 2024/2847 (CRA), Anhang V

Generiert am: {_today()}

---

## 1. Allgemeine Beschreibung des Produkts

**Produktname:** {name}
**Verwendungszweck:** {_fmt_or_placeholder(project.get('intended_use'))}
**Hauptfunktionen:** {desc or '[ergaenzen]'}

### 1.1 Technische Eigenschaften

- **Primaere Programmiersprache:** {_fmt_or_placeholder(project.get('primary_language'))}
- **Enthaelt Firmware:** {'Ja' if project.get('has_firmware') else 'Nein'}
- **Internet-Konnektivitaet:** {'Ja' if project.get('connected_to_internet') else 'Nein'}
- **Software-Updates:** {'Ja' if project.get('has_software_updates') else 'Nein'}
- **Verarbeitet personenbezogene Daten:** {'Ja' if project.get('processes_personal_data') else 'Nein'}
- **Kritische Infrastruktur:** {'Ja' if project.get('is_critical_infra_supplier') else 'Nein'}

## 2. Konzeption, Entwicklung und Produktion

### 2.1 Cybersicherheits-Risikobewertung (CRA Art. 13(2))

Risikobewertung wurde durchgefuehrt fuer die Klassifikation
**{CLASS_LABELS.get(project.get('cra_classification', ''), 'unbewertet')}**.

### 2.2 Secure-by-Design-Massnahmen

Siehe `doc_eu_conformity` Abschnitt 4 fuer die Einhaltung von Anhang I.
Detail-Mapping (40 atomare Annex-I-Anforderungen) ist im BreakPilot
CRA-Modul unter `/sdk/cra/{{projectId}}/requirements` einsehbar.

## 3. Bewertung der Konformitaet

### 3.1 Verfahren
{PATH_LABELS.get(project.get('conformity_path', ''), 'Nicht festgelegt')}

### 3.2 Klassifikation

**{CLASS_LABELS.get(project.get('cra_classification', ''), 'unbewertet')}**

Begruendung:
{chr(10).join(f'- {r}' for r in project.get('classification_rationale', []) or ['(keine Rationale erfasst)'])}

## 4. Kontaktangaben

**Hersteller:** [zu ergaenzen]
**Anschrift:** [zu ergaenzen]
**Cybersicherheits-Kontakt:** [zu ergaenzen, siehe CVD-Policy]

---

*Diese Datei wurde automatisch aus dem BreakPilot CRA-Modul generiert.
Sie ist ein Skelett — Hersteller muss alle `[ergaenzen]`-Stellen fuellen,
zusaetzliche Anhaenge (Architekturzeichnungen, Test-Berichte, SBOM) beilegen.*
"""
    coverage = {
        "doc_type": "doc_technical",
        "annex_anchor": "CRA Anhang V",
        "fields_required": 8,
        "fields_filled": sum(1 for v in [
            project.get("name"), project.get("intended_use"),
            project.get("cra_classification"), project.get("conformity_path"),
        ] if v),
    }
    return title, content, coverage


def doc_cvd_policy(project: dict, *, security_contact: Optional[str] = None) -> tuple[str, str, dict]:
    """Coordinated Vulnerability Disclosure Policy."""
    name = project.get("name", "[Produkt]")
    contact = _fmt_or_placeholder(security_contact, "security@[your-domain]")
    title = f"Coordinated Vulnerability Disclosure Policy — {name}"
    content = f"""# Coordinated Vulnerability Disclosure (CVD) Policy
**{name}**
Generiert am: {_today()}

Diese Policy beschreibt, wie Sicherheitsforscher und Nutzer Schwachstellen
verantwortungsvoll an uns melden koennen.

---

## Wir freuen uns ueber Schwachstellen-Meldungen

Wenn Sie eine Sicherheitsluecke in **{name}** entdeckt haben, melden Sie sie
bitte vertraulich an unsere Sicherheitsstelle. Wir verpflichten uns:

- **Eingangsbestaetigung innerhalb von 5 Werktagen**
- Kommunikation auf Augenhoehe waehrend der Triage
- Koordinierte Veroeffentlichung nach Patch-Verfuegbarkeit
- Public Acknowledgement (auf Wunsch)
- Kein juristisches Vorgehen gegen Forscher, die diese Policy befolgen

## Meldekanal

- **E-Mail:** {contact}
- **PGP-Key:** [Fingerprint einfuegen]
- **Web:** https://[your-domain]/.well-known/security.txt
- **Sprachen:** Deutsch, English

## Was wir brauchen

- Eindeutige Produkt-Identifikation: {name} + Version
- Reproduktionsschritte (PoC bevorzugt)
- Auswirkungsbeschreibung (Confidentiality / Integrity / Availability)
- (Optional) Vorschlag fuer Behebung

## Reaktionszeit-Ziele (SLAs)

| Severity (CVSS) | Triage | Patch verfuegbar |
|---|---|---|
| Kritisch (9.0+) | 24h | 30 Tage |
| Hoch (7.0-8.9) | 72h | 90 Tage |
| Mittel (4.0-6.9) | 7 Tage | 180 Tage |
| Niedrig (<4.0) | 14 Tage | naechster Release-Zyklus |

## Embargo + Disclosure

Wir bitten Sicherheitsforscher um **Embargo bis Patch ausgeliefert** (typ. 90 Tage).
Nach Patch koordinieren wir die Veroeffentlichung gemeinsam (Forscher +
Hersteller). CVE-Beantragung erfolgt durch uns.

## CRA-Bezug

Diese Policy ist Pflicht gemaess **CRA Anhang I Teil 2(5)** sowie
**ISO/IEC 29147**. Bei aktiv ausgenutzten Schwachstellen melden wir nach
**CRA Art. 14(2)**:

- **24h:** Fruehwarnung an ENISA / nationale CSIRT
- **72h:** Detaillierter Bericht
- **14 Tage:** Abschlussbericht nach Behebung

---

*Diese CVD-Policy wurde automatisch generiert. Hersteller-spezifische Angaben
(Kontakt, PGP, Domain) muessen ergaenzt werden vor Veroeffentlichung.*
"""
    coverage = {
        "doc_type": "doc_cvd_policy",
        "annex_anchor": "CRA Anhang I Teil 2(5), Art. 14",
        "fields_required": 3,
        "fields_filled": 1 if security_contact else 0,
    }
    return title, content, coverage


def doc_update_policy(project: dict) -> tuple[str, str, dict]:
    """Update / Patch Policy."""
    name = project.get("name", "[Produkt]")
    title = f"Update- und Patch-Policy — {name}"
    content = f"""# Update- und Patch-Policy
**{name}**
Generiert am: {_today()}

---

## Lifecycle-Support

Wir verpflichten uns, **Sicherheits-Updates fuer {name}** waehrend der
gesamten erwarteten Nutzungsdauer bereitzustellen, jedoch **mindestens 5 Jahre**
ab Inverkehrbringen — gemaess CRA Anhang I, 1(4).

## Patch-SLA (Severity-basiert)

| CVSS Score | Severity | Max. Patch-Zeit |
|---|---|---|
| 9.0–10.0 | Kritisch | **30 Tage** |
| 7.0–8.9 | Hoch | 90 Tage |
| 4.0–6.9 | Mittel | 180 Tage |
| <4.0 | Niedrig | naechster Release |

## Update-Mechanismus

- **Distribution:** Ueber sichere HTTPS-Kanaele
- **Authentizitaet:** Alle Updates sind digital signiert (X.509)
- **Integritaet:** SHA-256-Hash vor Installation geprueft
- **Rollback:** Manuelles Rollback auf vorherige Version moeglich
- **Manipulationsschutz:** Downgrade auf verwundbare Versionen blockiert

## Benachrichtigung

Bei kritischen Updates: aktive Benachrichtigung der Endnutzer ueber
{_fmt_or_placeholder(project.get('repo_url'), '[Kommunikationskanal ergaenzen]')}
binnen 24h nach Verfuegbarkeit.

## Security Advisories

Veroeffentlicht im CSAF-Format (Common Security Advisory Framework) unter:
- https://[your-domain]/security/advisories
- Maschinenlesbar fuer SCA-Tools

## End-of-Life

Mindestens **6 Monate vor EOL** wird der Termin bekannt gegeben. Letztes
Sicherheits-Update wird mit der EOL-Ankuendigung ausgeliefert.

## CRA-Bezug

Diese Policy erfuellt CRA Anhang I, 1(4) (sichere Updates) und
2(3) (Patch-Bereitstellung), sowie Art. 13 (Lifecycle-Support).

---

*Hersteller-spezifische Angaben muessen ergaenzt werden.*
"""
    coverage = {
        "doc_type": "doc_update_policy",
        "annex_anchor": "CRA Anhang I 1(4) + 2(3), Art. 13",
        "fields_required": 4,
        "fields_filled": 1 if project.get("has_software_updates") else 0,
    }
    return title, content, coverage


def doc_sbom_report(project: dict, latest_sbom: Optional[dict] = None) -> tuple[str, str, dict]:
    """SBOM-Zusammenfassung des aktuellsten Uploads."""
    name = project.get("name", "[Produkt]")
    title = f"SBOM-Bericht — {name}"

    if not latest_sbom:
        body = """## Kein SBOM hochgeladen

Bitte SBOM (CycloneDX oder SPDX) hochladen unter:
`/sdk/cra/{projectId}/sbom`

CRA Anhang I, Anforderung 23 verlangt ein maschinenlesbares
Software Bill of Materials fuer jedes Produkt mit digitalen Elementen.
"""
        fields_filled = 0
    else:
        summary = latest_sbom.get("summary") or {}
        sample = summary.get("sample_components") or summary.get("sample_packages") or []
        sample_list = "\n".join(f"- `{c.get('name', '?')}` v{c.get('version', '?')}" for c in sample[:10])
        body = f"""## Aktuelle Version

- **Datei:** {latest_sbom.get('filename', 'unknown')}
- **Format:** {latest_sbom.get('format', '?')} v{latest_sbom.get('spec_version', '?')}
- **Hochgeladen:** {latest_sbom.get('uploaded_at', '?')}
- **Komponenten:** {latest_sbom.get('component_count', 0)}
- **Scan-Status:** {latest_sbom.get('scan_status', '?')}

## Beispielkomponenten (Top 10)

{sample_list or '_(keine extrahiert)_'}

## Schwachstellen-Status

{'Scan ausstehend — wird durch separates Tool durchgefuehrt.' if latest_sbom.get('scan_status') == 'pending' else 'Siehe `/sdk/cra/{projectId}/vuln` fuer aktive Vulnerabilities.'}
"""
        fields_filled = 4

    content = f"""# SBOM-Bericht
**{name}**
Generiert am: {_today()}

{body}

---

## CRA-Bezug

Anhang I, Anforderung 23: maschinenlesbares SBOM (CycloneDX oder SPDX)
fuer jedes Produkt mit digitalen Elementen. Pflicht bei jedem Release zu
aktualisieren.

## Naechste Schritte

1. SBOM bei jedem Release-Update neu hochladen
2. osv.dev-Scan pruefen (Phase 3.5)
3. CVEs in `affected_components` der `/vuln`-Seite tracken
"""
    coverage = {
        "doc_type": "doc_sbom_report",
        "annex_anchor": "CRA Anhang I, Req. 23",
        "fields_required": 4,
        "fields_filled": fields_filled,
    }
    return title, content, coverage


# =============================================================================
# Registry — for the /documents/generate endpoint
# =============================================================================

DOC_GENERATORS = {
    "doc_eu_conformity": doc_eu_conformity,
    "doc_technical": doc_technical,
    "doc_cvd_policy": doc_cvd_policy,
    "doc_update_policy": doc_update_policy,
    "doc_sbom_report": doc_sbom_report,
}

DOC_TYPE_LABELS = {
    "doc_eu_conformity": "EU-Konformitaetserklaerung (Annex VII)",
    "doc_technical": "Technische Dokumentation (Annex V)",
    "doc_cvd_policy": "CVD Policy",
    "doc_update_policy": "Update- und Patch-Policy",
    "doc_sbom_report": "SBOM-Bericht",
}
