import re

import pytest

from textacy import load_spacy_lang
from textacy import extract


@pytest.fixture(scope="module")
def en_nlp():
    return load_spacy_lang("en_core_web_sm")


@pytest.fixture(scope="module")
def es_nlp():
    return load_spacy_lang("es_core_news_sm")


@pytest.fixture(scope="module")
def sss_doc(en_nlp):
    text = (
        "In general, Burton DeWilde loves animals, but he does not love *all* animals. "
        "Burton loves his cats Rico and Isaac; Burton loved his cat Lucy. "
        "But Burton DeWilde definitely does not love snakes, spiders, or moths. "
        "Now you know that Burton loves animals and cats in particular."
    )
    return en_nlp(text)


@pytest.mark.parametrize(
    "text, svos_exp",
    [
        (
            "Burton loves cats.",
            [(["Burton"], ["loves"], ["cats"])],
        ),
        (
            "One of my cats was eating food.",
            [(["One"], ["was", "eating"], ["food"])],
        ),
        (
            "The first dog to arrive wins the treat.",
            [(["dog"], ["wins"], ["treat"])],
        ),
        (
            "Burton was loved by cats.",
            [(["Burton"], ["was", "loved"], ["cats"])],
        ),
        (
            "Food was eaten by my cat.",
            [(["Food"], ["was", "eaten"], ["cat"])],
        ),
        (
            "The treat was won by the first dog to arrive.",
            [(["treat"], ["was", "won"], ["dog"])],
        ),
        (
            "He and I love house cats and big dogs.",
            [(["He", "I"], ["love"], ["house", "cats", "dogs"])],
        ),
        (
            "We do love and did hate small dogs.",
            [(["We"], ["do", "love"], ["dogs"]), (["We"], ["did", "hate"], ["dogs"])],
        ),
        (
            "Rico eats food and plays fetch.",
            [(["Rico"], ["eats"], ["food"]), (["Rico"], ["plays"], ["fetch"])]
        ),
        (
            "What the cat did makes sense.",
            [
                (["cat"], ["did"], ["What"]),
                (["What", "the", "cat", "did"], ["makes"], ["sense"]),
            ],
        ),
        (
            "What the cat wanted was eaten by the dog.",
            [
                (["cat"], ["wanted"], ["What"]),
                (["What", "the", "cat", "wanted"], ["was", "eaten"], ["dog"]),
            ],
        ),
        (
            "Burton and Nick do not expect to adopt another cat.",
            [
                (
                    ["Burton", "Nick"],
                    ["do", "not", "expect"],
                    ["to", "adopt", "another", "cat"],
                )
            ],
        ),
        (
            "She and her friend did find, sell, and throw sea shells by the sea shore.",
            [
                (["She", "friend"], ["did", "find"], ["sea", "shells"]),
                (["She", "friend"], ["sell"], ["sea", "shells"]),
                (["She", "friend"], ["throw"], ["sea", "shells"]),
            ],
        ),
    ]
)
def test_subject_verb_object_triples(text, svos_exp, en_nlp):
    doc = en_nlp(text)
    svos = list(extract.subject_verb_object_triples(doc))
    assert all(
        hasattr(svo, attr)
        for svo in svos for attr in ["subject", "verb", "object"]
    )
    svos_obs = [
        (
            [tok.text for tok in subject],
            [tok.text for tok in verb],
            [tok.text for tok in object]
        )
        for subject, verb, object in svos
    ]
    assert svos_obs == svos_exp


@pytest.mark.parametrize(
    "entity, cue, fragment_len_range, exp",
    [
        (
            "Burton",
            "love",
            None,
            [
                (["Burton"], ["loves"], ["his", "cats", "Rico", "and", "Isaac"]),
                (["Burton"], ["loved"], ["his", "cat", "Lucy"]),
                (["Burton"], ["loves"], ["animals", "and", "cats"])
            ],
        ),
        (
            re.compile("Burton"),
            "love",
            None,
            [
                (["Burton"], ["loves"], ["his", "cats", "Rico", "and", "Isaac"]),
                (["Burton"], ["loved"], ["his", "cat", "Lucy"]),
                (["Burton"], ["loves"], ["animals", "and", "cats"])
            ],
        ),
        (
            "Burton( DeWilde)?",
            "love",
            None,
            [
                (["Burton", "DeWilde"], ["loves"], ["animals"]),
                (["Burton"], ["loves"], ["his", "cats", "Rico", "and", "Isaac"]),
                (["Burton"], ["loved"], ["his", "cat", "Lucy"]),
                (
                    ["Burton", "DeWilde"],
                    ["does", "not", "love"],
                    ["snakes", ",", "spiders", ",", "or", "moths"]
                ),
                (["Burton"], ["loves"], ["animals", "and", "cats"])
            ],
        ),
        (
            "Burton",
            "love",
            (None, 4),
            [
                (["Burton"], ["loved"], ["his", "cat", "Lucy"]),
                (["Burton"], ["loves"], ["animals", "and", "cats"])
            ],
        ),
        (
            "Burton",
            "love",
            (4, 6),
            [(["Burton"], ["loves"], ["his", "cats", "Rico", "and", "Isaac"])]
        ),
        ("Burton", "hate", None, []),
    ],
)
def test_semistructured_statements(sss_doc, entity, cue, fragment_len_range, exp):
    obs = list(
        extract.semistructured_statements(
            sss_doc, entity=entity, cue=cue, fragment_len_range=fragment_len_range
        )
    )
    assert all(
        hasattr(sss, attr)
        for sss in obs for attr in ["entity", "cue", "fragment"]
    )
    obs_text = [
        (
            [tok.text for tok in e],
            [tok.text for tok in c],
            [tok.text for tok in f]
        )
        for e, c, f in obs
    ]
    assert obs_text == exp


@pytest.mark.parametrize(
    "text, exp",
    [
        (
            "Burton said, \"I love cats!\"",
            [(["Burton"], ["said"], "\"I love cats!\"")],
        ),
        (
            "\"We love cats!\" reply Burton and Nick.",
            [(["Burton", "Nick"], ["reply"], "\"We love cats!\"")],
        ),
        (
            "Burton explained from a podium. \"I love cats,\" he said.",
            [(["he"], ["said"], "\"I love cats,\"")],
        ),
        (
            "\"I love cats!\" insists Burton. \"I absolutely do.\"",
            [
                (["Burton"], ["insists"], "\"I love cats!\""),
                (["Burton"], ["insists"], "\"I absolutely do.\"")
            ],
        ),
        (
            "\"Some people say otherwise,\" he conceded.",
            [(["he"], ["conceded"], "\"Some people say otherwise,\"")],
        ),
        (
            "Burton claims that his favorite book is \"One Hundred Years of Solitude\".",
            [],
        ),
        (
            "Burton thinks that cats are \"cuties\".",
            [],
        ),
    ]
)
def test_direct_quotations(en_nlp, text, exp):
    obs = list(extract.direct_quotations(en_nlp(text)))
    assert all(
        hasattr(dq, attr)
        for dq in obs for attr in ["speaker", "cue", "content"]
    )
    obs_text = [
        (
            [tok.text for tok in speaker],
            [tok.text for tok in cue],
            content.text
        )
        for speaker, cue, content in obs
    ]
    assert obs_text == exp


@pytest.mark.parametrize(
    "text, exp",
    [
        (
            "\"Me encantan los gatos\", dijo Burtón.",
            [(["Burtón"], ["dijo"], "\"Me encantan los gatos\"")],
        ),
        (
            "«Me encantan los gatos», afirmó Burtón.",
            [(["Burtón"], ["afirmó"], "«Me encantan los gatos»")],
        ),
    ]
)
def test_direct_quotations_spanish(es_nlp, text, exp):
    obs = extract.direct_quotations(es_nlp(text))
    obs_text = [
        (
            [tok.text for tok in speaker],
            [tok.text for tok in cue],
            content.text
        )
        for speaker, cue, content in obs
    ]
    assert obs_text == exp
