from typing import List

from pytest import fixture, mark

from edsnlp.pipes.qualifiers.negation import Negation
from edsnlp.utils.examples import parse_example

negation_examples: List[str] = [
    "<ent polarity_=NEG>métas,tases</ent> : non",
    (
        "Pas de <ent negated=true>lésion pulmonaire avec "
        "l'absence de lésion secondaire</ent>."
    ),
    "Cancer non <ent negation=true>métastasé</ent>.",
    "Absence d'<ent negated=true>image osseuse d'allure évolutive</ent>.",
    "il n'y a pas de <ent polarity_=NEG>métas,tases</ent>",
    "Le patient n'est pas <ent polarity_=NEG>malade</ent>.",
    "Aucun <ent polarity_=NEG>traitement</ent>.",
    "Le <ent polarity_=AFF>scan</ent> révèle une grosseur.",
    "il y a des <ent polarity_=AFF>métastases</ent>",
    "aucun doute sur les <ent polarity_=AFF>métastases</ent>",
    "il n'y a pas de <ent polarity_=NEG>métastases</ent>",
    "il n'y a pas d' <ent polarity_=NEG>métastases</ent>",
    "il n'y a pas d'<ent polarity_=NEG>métastases</ent>",
    "il n'y a pas d'amélioration de la <ent negated=false>maladie</ent>",
    "<ent negated=true>maladie écartée",
    "Le patient ne <ent negated=true>fume</ent> pas.",
    "Le patient ne <ent negated=true>fume vraiment vraiment</ent> pas.",
    "Le patient ne <ent negated=false>fume</ent> que des cigares.",
    "Le résultat exclut un <ent negated=true>SMD</ent>",
    "Le résultat ne permet pas d'exclure un <ent negated=false>SMD</ent>",
    "Situation aggravée par une <ent negated=false>neutropénie fébrile</ent>."
    "Patient est traité d'une cure d'<ent negated=false>ALECTINIB</ent> depuis le ...",
]


@fixture
def negation_factory(blank_nlp):
    default_config = dict(
        pseudo=None,
        preceding=None,
        following=None,
        termination=None,
        verbs=None,
        attr="NORM",
        within_ents=False,
        explain=True,
        span_getter="ents",
    )

    def factory(on_ents_only, **kwargs) -> Negation:
        config = dict(**default_config)
        config.update(kwargs)

        return Negation(
            nlp=blank_nlp,
            on_ents_only=on_ents_only,
            **config,
        )

    return factory


@mark.parametrize("on_ents_only", [True, False])
def test_negation(blank_nlp, negation_factory, on_ents_only):
    negation = negation_factory(on_ents_only=on_ents_only)

    for example in negation_examples:
        text, entities = parse_example(example=example)

        doc = blank_nlp(text)
        doc.ents = [
            doc.char_span(ent.start_char, ent.end_char, label="ent") for ent in entities
        ]

        doc = negation(doc)

        for entity, ent in zip(entities, doc.ents):
            for modifier in entity.modifiers:
                assert bool(ent._.negation_cues) == (modifier.value in {True, "NEG"})

                assert (
                    getattr(ent._, modifier.key) == modifier.value
                ), f"{modifier.key} labels don't match."

                if not on_ents_only:
                    for token in ent:
                        assert (
                            getattr(token._, modifier.key) == modifier.value
                        ), f"{modifier.key} labels don't match."


def test_negation_within_ents(blank_nlp, negation_factory):
    negation = negation_factory(on_ents_only=True, within_ents=True)

    examples = [
        "<ent negated=true>Lésion pulmonaire avec absence de lésion secondaire</ent>.",
    ]

    for example in examples:
        text, entities = parse_example(example=example)

        doc = blank_nlp(text)
        doc.ents = [
            doc.char_span(ent.start_char, ent.end_char, label="ent") for ent in entities
        ]

        doc = negation(doc)

        for entity, ent in zip(entities, doc.ents):
            for modifier in entity.modifiers:
                assert bool(ent._.negation_cues) == (modifier.value in {True, "NEG"})

                assert (
                    getattr(ent._, modifier.key) == modifier.value
                ), f"{modifier.key} labels don't match."
