#!/usr/bin/env python3
"""Génère la fiche produit Pseudonymisation en DOCX (deux versions)."""

from docx import Document
from docx.shared import Pt, Cm, Inches, RGBColor
from docx.enum.text import WD_ALIGN_PARAGRAPH
from docx.enum.table import WD_TABLE_ALIGNMENT
from docx.oxml.ns import qn
import os

# ── Helpers ──────────────────────────────────────────────────────────

def set_cell_shading(cell, color_hex: str):
    """Applique une couleur de fond à une cellule."""
    shading = cell._element.get_or_add_tcPr()
    shading_elem = shading.makeelement(qn('w:shd'), {
        qn('w:val'): 'clear',
        qn('w:color'): 'auto',
        qn('w:fill'): color_hex,
    })
    shading.append(shading_elem)


def add_heading_styled(doc, text, level=1, color=RGBColor(0x1A, 0x56, 0x8E)):
    h = doc.add_heading(text, level=level)
    for run in h.runs:
        run.font.color.rgb = color
    return h


def add_bullet(doc, text, bold_prefix=None):
    p = doc.add_paragraph(style='List Bullet')
    if bold_prefix:
        run = p.add_run(bold_prefix)
        run.bold = True
        p.add_run(text)
    else:
        p.add_run(text)
    return p


def add_para(doc, text, bold=False, italic=False, space_after=Pt(6)):
    p = doc.add_paragraph()
    run = p.add_run(text)
    run.bold = bold
    run.italic = italic
    p.paragraph_format.space_after = space_after
    return p


def add_table_row(table, cells_data, header=False, header_color='1A568E'):
    row = table.add_row()
    for i, text in enumerate(cells_data):
        cell = row.cells[i]
        cell.text = ''
        p = cell.paragraphs[0]
        run = p.add_run(text)
        run.font.size = Pt(10)
        if header:
            run.bold = True
            run.font.color.rgb = RGBColor(0xFF, 0xFF, 0xFF)
            set_cell_shading(cell, header_color)
        p.paragraph_format.space_before = Pt(4)
        p.paragraph_format.space_after = Pt(4)


def set_table_style(table):
    """Style sobre pour les tableaux."""
    table.alignment = WD_TABLE_ALIGNMENT.CENTER
    for row in table.rows:
        for cell in row.cells:
            for p in cell.paragraphs:
                p.paragraph_format.space_before = Pt(3)
                p.paragraph_format.space_after = Pt(3)


def add_section_break(doc):
    doc.add_page_break()


# ── Couleurs ─────────────────────────────────────────────────────────

BLUE_DARK = RGBColor(0x1A, 0x56, 0x8E)
BLUE_LIGHT = 'D6E8F7'
GRAY_LIGHT = 'F2F2F2'
GREEN = RGBColor(0x2E, 0x7D, 0x32)


# ══════════════════════════════════════════════════════════════════════
#  VERSION 1 — Synthétique (1 page recto, orientation portrait)
# ══════════════════════════════════════════════════════════════════════

def build_version_1(doc):
    # ── Titre ──
    title = doc.add_heading('Pseudonymisation Automatique\nde Documents Médicaux', level=0)
    title.alignment = WD_ALIGN_PARAGRAPH.CENTER
    for run in title.runs:
        run.font.color.rgb = BLUE_DARK
        run.font.size = Pt(22)

    # Sous-titre
    sub = doc.add_paragraph()
    sub.alignment = WD_ALIGN_PARAGRAPH.CENTER
    run = sub.add_run('Fiche produit — Version synthétique')
    run.font.size = Pt(12)
    run.font.color.rgb = RGBColor(0x66, 0x66, 0x66)
    run.italic = True
    sub.paragraph_format.space_after = Pt(16)

    # ── En bref ──
    add_heading_styled(doc, 'En bref', level=2)
    add_para(doc,
        'Logiciel d\'anonymisation automatique de documents médicaux, '
        'fonctionnant 100% en local sur un poste Windows. '
        'Aucune donnée ne sort de l\'établissement. '
        'Un fichier entre, un fichier anonymisé sort.',
        space_after=Pt(10))

    # ── Tableau comparatif ──
    add_heading_styled(doc, 'Pourquoi cet outil ?', level=2)
    table = doc.add_table(rows=0, cols=2)
    table.style = 'Table Grid'
    add_table_row(table, ['Situation actuelle', 'Avec l\'outil'], header=True)
    comparisons = [
        ('Anonymisation manuelle, chronophage\net source d\'erreurs',
         'Traitement automatique\nen quelques minutes'),
        ('Risque d\'oubli de données\npersonnelles (RGPD)',
         'Taux de détection > 99%\nvalidé sur corpus réel'),
        ('Mobilisation de personnel qualifié\nsur une tâche répétitive',
         'L\'équipe TIM vérifie,\nelle ne produit plus à la main'),
    ]
    for left, right in comparisons:
        add_table_row(table, [left, right])
    set_table_style(table)
    doc.add_paragraph()  # espace

    # ── Points clés ──
    add_heading_styled(doc, 'Points clés', level=2)
    points = [
        ('100% hors-ligne — ', 'aucune donnée ne transite par internet'),
        ('Un seul exécutable — ', 'pas d\'installation, pas de serveur, pas de GPU'),
        ('14 formats acceptés — ', 'PDF, Word, ODT, RTF, images, HTML...'),
        ('4 moteurs d\'IA — ', 'spécialisés en français clinique, fonctionnent en parallèle'),
        ('Paramétrable — ', 'whitelist et blacklist modifiables par l\'établissement'),
        ('Irréversible — ', 'masquage par rectangles noirs, pas de calque amovible'),
    ]
    for bold_part, normal_part in points:
        add_bullet(doc, normal_part, bold_prefix=bold_part)

    # ── Ce qui est masqué / préservé ──
    add_heading_styled(doc, 'Ce qui est masqué / préservé', level=2)
    table2 = doc.add_table(rows=0, cols=2)
    table2.style = 'Table Grid'
    add_table_row(table2, ['Masqué automatiquement', 'Préservé intégralement'], header=True)
    add_table_row(table2, [
        'Noms, prénoms, adresses,\ntéléphones, n° de sécu,\ndates de naissance,\nnoms d\'établissements,\ncodes-barres patients',
        'Diagnostics, traitements,\nposologies, actes médicaux,\nrésultats d\'examens,\ndates de séjour,\ncodage CIM / CCAM'
    ])
    set_table_style(table2)
    doc.add_paragraph()

    # ── Cas d'usage ──
    add_heading_styled(doc, 'Cas d\'usage', level=2)
    usages = [
        ('Contrôle T2A — ', 'anonymiser les pièces justificatives avant transmission'),
        ('Recherche clinique — ', 'constituer des corpus anonymisés'),
        ('Audits qualité / HAS — ', 'partager des dossiers sans exposition de données'),
        ('Formation — ', 'utiliser des cas réels anonymisés pour l\'enseignement'),
    ]
    for bold_part, normal_part in usages:
        add_bullet(doc, normal_part, bold_prefix=bold_part)


# ══════════════════════════════════════════════════════════════════════
#  VERSION 2 — Détaillée (multi-pages, par audience)
# ══════════════════════════════════════════════════════════════════════

def build_version_2(doc):
    # ── Page de titre ──
    for _ in range(4):
        doc.add_paragraph()

    title = doc.add_heading('Pseudonymisation Automatique\nde Documents Médicaux', level=0)
    title.alignment = WD_ALIGN_PARAGRAPH.CENTER
    for run in title.runs:
        run.font.color.rgb = BLUE_DARK
        run.font.size = Pt(26)

    doc.add_paragraph()
    sub = doc.add_paragraph()
    sub.alignment = WD_ALIGN_PARAGRAPH.CENTER
    run = sub.add_run('Fiche produit détaillée')
    run.font.size = Pt(14)
    run.font.color.rgb = RGBColor(0x66, 0x66, 0x66)

    doc.add_paragraph()
    audiences = doc.add_paragraph()
    audiences.alignment = WD_ALIGN_PARAGRAPH.CENTER
    run = audiences.add_run('Direction générale · DSI · Médecins · Équipe TIM')
    run.font.size = Pt(11)
    run.font.color.rgb = RGBColor(0x99, 0x99, 0x99)
    run.italic = True

    add_section_break(doc)

    # ── 1. Le problème ──
    add_heading_styled(doc, 'Le problème', level=1)
    add_para(doc,
        'Les établissements de santé manipulent quotidiennement des documents '
        'contenant des données personnelles de patients : comptes-rendus opératoires, '
        'résultats d\'examens, courriers médicaux, rapports d\'anatomo-pathologie...')
    add_para(doc,
        'Lors des contrôles T2A, audits qualité, recherches cliniques ou échanges '
        'inter-établissements, ces documents doivent être anonymisés conformément '
        'au RGPD et au cadre réglementaire santé.')
    add_para(doc,
        'L\'anonymisation manuelle est lente, coûteuse, et faillible : '
        'un oubli suffit à exposer l\'identité d\'un patient.',
        bold=True, space_after=Pt(12))

    # ── 2. La solution ──
    add_heading_styled(doc, 'La solution', level=1)
    add_para(doc,
        'Un logiciel autonome, fonctionnant intégralement en local, qui anonymise '
        'automatiquement vos documents en quelques minutes :')
    bullets = [
        'Détecte et masque les noms, prénoms, adresses, téléphones, numéros de sécurité sociale, dates de naissance, noms d\'établissements',
        'Préserve le contenu médical utile : diagnostics, traitements, actes, résultats, codage',
        'Produit un PDF anonymisé prêt à transmettre, avec masquage irréversible',
    ]
    for b in bullets:
        add_bullet(doc, b)
    doc.add_paragraph()

    # ── Tableau comparatif ──
    add_heading_styled(doc, 'Avant / Après', level=2)
    table = doc.add_table(rows=0, cols=2)
    table.style = 'Table Grid'
    add_table_row(table, ['Situation actuelle', 'Avec l\'outil'], header=True)
    comparisons = [
        ('Anonymisation manuelle, chronophage et source d\'erreurs',
         'Traitement automatique en quelques minutes'),
        ('Risque d\'oubli de données personnelles (RGPD, AI Act)',
         'Taux de détection > 99% validé sur corpus réel de contrôle T2A'),
        ('Mobilisation de personnel qualifié sur une tâche répétitive',
         'L\'équipe TIM se concentre sur la vérification, pas la production'),
        ('Dépendance à des outils cloud ou des prestataires externes',
         '100% local, aucune donnée ne quitte l\'établissement'),
    ]
    for left, right in comparisons:
        add_table_row(table, [left, right])
    set_table_style(table)

    add_section_break(doc)

    # ── 3. Conformité et sécurité (DG + DSI) ──
    add_heading_styled(doc, 'Conformité et sécurité', level=1)
    tag = doc.add_paragraph()
    run = tag.add_run('→ Direction générale, DSI')
    run.italic = True
    run.font.color.rgb = RGBColor(0x99, 0x99, 0x99)
    tag.paragraph_format.space_after = Pt(8)

    security_points = [
        ('Aucune connexion réseau', 'Le logiciel fonctionne entièrement hors-ligne. Aucun appel à un serveur externe, aucune API cloud, aucune télémétrie.'),
        ('Aucune donnée exfiltrée', 'Le traitement se fait intégralement en mémoire locale. Les documents source ne sont ni copiés ni transmis.'),
        ('Conformité RGPD / AI Act', 'L\'intelligence artificielle est embarquée dans l\'exécutable. Pas de sous-traitance, pas de transfert de données à un tiers.'),
        ('Masquage irréversible', 'Les zones anonymisées sont remplacées par des rectangles noirs directement dans le PDF. Il ne s\'agit pas d\'un calque amovible : l\'information est définitivement supprimée.'),
        ('Traçabilité', 'Les fichiers anonymisés sont nommés avec le préfixe "ANON_" pour identification immédiate.'),
    ]
    for title_text, desc in security_points:
        p = doc.add_paragraph()
        run_title = p.add_run(title_text + ' — ')
        run_title.bold = True
        p.add_run(desc)
        p.paragraph_format.space_after = Pt(6)

    add_section_break(doc)

    # ── 4. Déploiement (DSI) ──
    add_heading_styled(doc, 'Déploiement et maintenance', level=1)
    tag = doc.add_paragraph()
    run = tag.add_run('→ DSI')
    run.italic = True
    run.font.color.rgb = RGBColor(0x99, 0x99, 0x99)
    tag.paragraph_format.space_after = Pt(8)

    add_heading_styled(doc, 'Prérequis', level=2)
    prereqs = [
        'Windows 10 ou 11 (64 bits)',
        'Poste bureautique standard — pas de GPU dédié, pas de serveur requis',
        'Pas de droits administrateur nécessaires pour l\'exécution',
        'Pas de connexion internet requise',
    ]
    for pr in prereqs:
        add_bullet(doc, pr)

    add_heading_styled(doc, 'Installation', level=2)
    install_steps = [
        'Copier l\'exécutable (un seul fichier .exe) sur le poste',
        'Au premier lancement, le fichier de configuration est créé automatiquement à côté de l\'exécutable',
        'C\'est prêt — aucune autre manipulation nécessaire',
    ]
    for i, step in enumerate(install_steps, 1):
        p = doc.add_paragraph()
        run = p.add_run(f'{i}. ')
        run.bold = True
        p.add_run(step)

    add_heading_styled(doc, 'Mise à jour', level=2)
    add_para(doc, 'Remplacer l\'exécutable par la nouvelle version. La configuration personnalisée de l\'établissement est conservée (fichier séparé).')

    add_heading_styled(doc, 'Configuration inter-établissements', level=2)
    add_para(doc,
        'Les paramètres (whitelist, blacklist) sont exportables au format JSON depuis l\'interface. '
        'Un établissement peut envoyer sa configuration par email. '
        'Le service central fusionne les configurations et renvoie un fichier YAML consolidé.')

    add_section_break(doc)

    # ── 5. Utilisation quotidienne (TIM) ──
    add_heading_styled(doc, 'Utilisation quotidienne', level=1)
    tag = doc.add_paragraph()
    run = tag.add_run('→ Équipe TIM, médecins')
    run.italic = True
    run.font.color.rgb = RGBColor(0x99, 0x99, 0x99)
    tag.paragraph_format.space_after = Pt(8)

    add_heading_styled(doc, 'En 3 étapes', level=2)
    steps = [
        ('Sélectionner', 'Choisir un fichier ou un dossier entier depuis l\'interface'),
        ('Lancer', 'Cliquer sur "Lancer l\'anonymisation"'),
        ('Récupérer', 'Les documents anonymisés sont créés dans le dossier de sortie, prêts à transmettre'),
    ]
    for title_text, desc in steps:
        p = doc.add_paragraph()
        run_title = p.add_run(f'{title_text} — ')
        run_title.bold = True
        run_title.font.color.rgb = BLUE_DARK
        p.add_run(desc)
        p.paragraph_format.space_after = Pt(6)

    add_heading_styled(doc, 'Formats acceptés (14)', level=2)
    table_fmt = doc.add_table(rows=0, cols=2)
    table_fmt.style = 'Table Grid'
    add_table_row(table_fmt, ['Type', 'Formats'], header=True)
    formats = [
        ('Documents', 'PDF, Word (.docx), ODT, RTF, TXT, HTML'),
        ('Images', 'JPEG, PNG, TIFF, BMP'),
    ]
    for type_name, fmt_list in formats:
        add_table_row(table_fmt, [type_name, fmt_list])
    set_table_style(table_fmt)
    doc.add_paragraph()

    add_para(doc,
        'Les images et documents scannés sont traités par reconnaissance optique de caractères (OCR) '
        'intégrée — pas de logiciel tiers nécessaire.',
        italic=True)

    add_heading_styled(doc, 'Paramétrage', level=2)
    params = [
        ('Whitelist — ', 'liste de termes à ne jamais masquer (noms de services, sigles internes, noms de logiciels...). Modifiable directement dans l\'interface.'),
        ('Blacklist — ', 'liste de termes à toujours masquer (noms de praticiens spécifiques...). Modifiable directement dans l\'interface.'),
        ('Export/Import — ', 'les paramètres sont échangeables entre établissements par simple fichier JSON envoyé par email.'),
    ]
    for bold_part, normal_part in params:
        add_bullet(doc, normal_part, bold_prefix=bold_part)

    add_section_break(doc)

    # ── 6. Fiabilité (Médecins + TIM) ──
    add_heading_styled(doc, 'Fiabilité de la détection', level=1)
    tag = doc.add_paragraph()
    run = tag.add_run('→ Médecins, équipe TIM')
    run.italic = True
    run.font.color.rgb = RGBColor(0x99, 0x99, 0x99)
    tag.paragraph_format.space_after = Pt(8)

    add_para(doc,
        'Le logiciel combine 4 moteurs d\'intelligence artificielle spécialisés en français clinique '
        'qui fonctionnent simultanément et se recoupent pour maximiser la détection :')

    engines = [
        ('Modèle CamemBERT médical', 'entraîné spécifiquement sur des documents cliniques français'),
        ('Modèle de détection d\'entités', 'reconnaissance zero-shot de données personnelles'),
        ('Modèle spécialisé', 'fine-tuné sur plus de 1 000 documents médicaux réels'),
        ('Bases de référence nationales', '219 000 noms de famille, 36 000 prénoms, 7 300 médicaments, 108 000 établissements de santé'),
    ]
    for title_text, desc in engines:
        add_bullet(doc, ' — ' + desc, bold_prefix=title_text)
    doc.add_paragraph()

    add_para(doc,
        'Principe de précaution : en cas de doute, le logiciel masque. '
        'Il est préférable de masquer un terme médical par excès '
        'plutôt que de laisser visible un nom de patient.',
        bold=True, space_after=Pt(8))

    add_para(doc,
        'Taux de détection validé à plus de 99% sur un corpus réel de documents '
        'de contrôle T2A : comptes-rendus opératoires, anatomo-pathologie, bactériologie, '
        'anesthésie, courriers médicaux, comptes-rendus d\'hospitalisation.')

    add_section_break(doc)

    # ── 7. Cas d'usage ──
    add_heading_styled(doc, 'Cas d\'usage', level=1)

    use_cases = [
        ('Contrôle T2A',
         'Anonymiser les pièces justificatives (comptes-rendus, résultats, courriers) '
         'avant transmission à l\'organisme de contrôle.'),
        ('Recherche clinique',
         'Constituer des jeux de données anonymisés à partir de dossiers patients réels, '
         'sans risque d\'identification.'),
        ('Audits qualité et certification HAS',
         'Partager des cas cliniques lors des visites de certification '
         'sans exposer l\'identité des patients.'),
        ('Échanges inter-établissements',
         'Transmettre des comptes-rendus médicaux anonymisés dans le cadre '
         'de parcours de soins partagés.'),
        ('Formation et enseignement',
         'Utiliser des cas cliniques réels anonymisés pour la formation '
         'des internes et des équipes soignantes.'),
    ]
    for title_text, desc in use_cases:
        p = doc.add_paragraph()
        run_title = p.add_run(title_text + '\n')
        run_title.bold = True
        run_title.font.size = Pt(11)
        run_desc = p.add_run(desc)
        run_desc.font.size = Pt(10)
        p.paragraph_format.space_after = Pt(10)

    # ── 8. Questions fréquentes ──
    add_heading_styled(doc, 'Questions fréquentes', level=1)

    faq = [
        ('Combien de temps prend le traitement d\'un document ?',
         'En moyenne quelques dizaines de secondes par document. '
         'Un lot de 20 à 30 documents se traite en 10 à 15 minutes sur un poste standard.'),
        ('Que faire si un nom n\'a pas été détecté ?',
         'Ajouter le terme dans la blacklist via l\'interface. '
         'Il sera systématiquement masqué lors des prochains traitements.'),
        ('Que faire si un terme médical a été masqué par erreur ?',
         'Ajouter le terme dans la whitelist via l\'interface. '
         'Il ne sera plus jamais masqué.'),
        ('Faut-il une connexion internet ?',
         'Non. Le logiciel fonctionne intégralement hors-ligne. '
         'L\'intelligence artificielle est embarquée dans l\'exécutable.'),
        ('Les documents originaux sont-ils modifiés ?',
         'Non. Le logiciel crée une copie anonymisée. '
         'Les documents originaux restent intacts.'),
        ('Peut-on traiter un dossier entier d\'un coup ?',
         'Oui. Il suffit de sélectionner un dossier au lieu d\'un fichier. '
         'Tous les documents du dossier seront traités séquentiellement.'),
    ]
    for question, answer in faq:
        p = doc.add_paragraph()
        run_q = p.add_run(question + '\n')
        run_q.bold = True
        run_q.font.size = Pt(10)
        run_a = p.add_run(answer)
        run_a.font.size = Pt(10)
        p.paragraph_format.space_after = Pt(8)


# ══════════════════════════════════════════════════════════════════════
#  Construction du document final
# ══════════════════════════════════════════════════════════════════════

def main():
    doc = Document()

    # ── Style par défaut ──
    style = doc.styles['Normal']
    style.font.name = 'Calibri'
    style.font.size = Pt(10)
    style.paragraph_format.space_after = Pt(4)

    # Marges
    for section in doc.sections:
        section.top_margin = Cm(1.5)
        section.bottom_margin = Cm(1.5)
        section.left_margin = Cm(2)
        section.right_margin = Cm(2)

    # ════════════════════════════════════════════
    # VERSION 1 — Synthétique
    # ════════════════════════════════════════════
    build_version_1(doc)

    # ── Séparation ──
    add_section_break(doc)
    sep = doc.add_paragraph()
    sep.alignment = WD_ALIGN_PARAGRAPH.CENTER
    for _ in range(6):
        doc.add_paragraph()
    sep2 = doc.add_paragraph()
    sep2.alignment = WD_ALIGN_PARAGRAPH.CENTER
    run = sep2.add_run('— Version détaillée ci-après —')
    run.font.size = Pt(14)
    run.font.color.rgb = RGBColor(0x99, 0x99, 0x99)
    run.italic = True

    add_section_break(doc)

    # ════════════════════════════════════════════
    # VERSION 2 — Détaillée
    # ════════════════════════════════════════════
    build_version_2(doc)

    # ── Sauvegarde ──
    output_path = os.path.join(os.path.dirname(os.path.dirname(__file__)),
                               'Fiche_Produit_Pseudonymisation.docx')
    doc.save(output_path)
    print(f'Fiche produit générée : {output_path}')


if __name__ == '__main__':
    main()
