"""Ventilation des impacts sur les libertés par public.

Le rapport ne dresse pas de tableau « texte × public ». En revanche il consacre
un chapitre à chacun des trois publics :

===========  ===============================================
Chapitre     Public
===========  ===============================================
`sec04`      Libertés individuelles : analyse croisée
`sec05`      Libertés associatives : le chantier latent
`sec06`      Entreprises : obligations nouvelles et ouvertures
===========  ===============================================

Un texte développé dans le chapitre 5 est donc, par construction du rapport, un
texte qui touche les libertés associatives. C'est ce placement — un choix
éditorial explicite, pas une inférence — qui sert de base à la ventilation.

Le **sens** de l'impact vient d'abord de la cotation « statut libertés » de
l'annexe, seule donnée cotée à la main. À défaut — les textes non promulgués ne
sont pas cotés — il est déduit du vocabulaire de la phrase, et le texte est
alors marqué « à vérifier ».
"""

from __future__ import annotations

import re
from dataclasses import dataclass

from pipeline.modeles import Impact, Public, RegimeLibertes, SensImpact
from pipeline.parseurs.chapitres import LectureChapitres
from pipeline.parseurs.dates_fr import sans_accents

CHAPITRE_PAR_PUBLIC: dict[str, Public] = {
    "sec04": Public.INDIVIDUS,
    "sec05": Public.ASSOCIATIONS,
    "sec06": Public.ENTREPRISES,
}

SENS_PAR_REGIME: dict[RegimeLibertes, SensImpact] = {
    RegimeLibertes.DROITS_PLUS: SensImpact.POSITIF,
    RegimeLibertes.CONTROLE_PLUS: SensImpact.NEGATIF,
    RegimeLibertes.MIXTE: SensImpact.MIXTE,
    RegimeLibertes.NEUTRE: SensImpact.NEUTRE,
}

# Vocabulaire de restriction et d'extension de droits, tel qu'employé par le
# rapport. Les listes sont volontairement courtes : un mot ambigu ferait plus
# de dégâts qu'un impact manquant.
_RESTRICTION = (
    "restriction", "restreint", "sanction", "amende", "interdiction", "interdit",
    "peine", "penal", "controle prefectoral", "surveillance", "fichage",
    "dissolution", "retrait", "suspension", "obligation declarative", "censure",
    "expropriation", "retention", "fouille", "reduit", "durci", "aggrave",
)
_EXTENSION = (
    "droit opposable", "nouveau droit", "garantie", "protection", "reparation",
    "extension des droits", "acces", "recours", "assistance", "indemnisation",
    "simplification", "allegement", "ouverture", "facilite", "reserve aux pme",
)


@dataclass(slots=True)
class Ventilation:
    """Impacts déduits pour un texte, et ce qui les a motivés."""

    impacts: dict[Public, Impact]
    deduit: bool  # vrai si le sens n'a pas pu venir de la cotation manuelle


MOTIF_VERIFICATION = (
    "Sens de l'impact déduit du vocabulaire du rapport : l'annexe ne cote le "
    "régime de libertés que pour les lois promulguées."
)


def ventiler(
    texte_id: str,
    lecture: LectureChapitres,
    *,
    regime: RegimeLibertes | None,
) -> Ventilation:
    """Répartit l'effet d'un texte entre individus, associations et entreprises."""
    impacts: dict[Public, Impact] = {}
    deduit = False

    for mention in lecture.mentions.get(texte_id, []):
        public = CHAPITRE_PAR_PUBLIC.get(mention.chapitre)
        if public is None or public in impacts:
            continue

        phrases = " ".join(mention.phrases)
        if regime is not None:
            sens = SENS_PAR_REGIME[regime]
        else:
            sens = _sens_du_vocabulaire(phrases)
            deduit = True

        impacts[public] = Impact(sens=sens, note=_note(mention.phrases))

    return Ventilation(impacts=impacts, deduit=deduit and bool(impacts))


def _sens_du_vocabulaire(fragment: str) -> SensImpact:
    plat = sans_accents(fragment).lower()
    restrictions = sum(1 for mot in _RESTRICTION if _present(mot, plat))
    extensions = sum(1 for mot in _EXTENSION if _present(mot, plat))

    if restrictions and extensions:
        return SensImpact.MIXTE
    if restrictions:
        return SensImpact.NEGATIF
    if extensions:
        return SensImpact.POSITIF
    return SensImpact.NEUTRE


def _present(expression: str, plat: str) -> bool:
    return re.search(rf"(?<!\w){re.escape(expression)}\w{{0,3}}(?!\w)", plat) is not None


def _note(phrases: list[str]) -> str:
    """Première phrase substantielle du chapitre, comme justification affichable."""
    from pipeline.parseurs.markdown import nettoyer

    for phrase in phrases:
        propre = nettoyer(phrase)
        if len(propre) > 60:
            return propre[:400]
    return ""
