import re
from test.common import (
    html_citation_refs,
    html_citations,
    html_footnote_refs,
    html_footnotes,
)
from typing import Set

import pytest


def citation_refs(output) -> Set[str]:
    return {match.group("label") for match in html_citation_refs().finditer(output)}


def citations(output) -> Set[str]:
    return {match.group("label") for match in html_citations().finditer(output)}


@pytest.mark.sphinx("html", testroot="bibliography_empty", freshenv=True)
def test_bibliography_empty(app, warning) -> None:
    app.build()
    assert not warning.getvalue()


@pytest.mark.sphinx("html", testroot="bibliography_header")
def test_bibliography_header(app, warning) -> None:
    app.build()
    assert not warning.getvalue()
    output = (app.outdir / "index.html").read_text(encoding="utf-8")
    assert "Regular Citations" in output
    assert "Footnote Citations" in output


@pytest.mark.sphinx(
    "pseudoxml",
    testroot="bibliography_empty",
    freshenv=True,
    confoverrides={
        "bibtex_bibliography_header": ".. rubric:: Regular Citations",
        "bibtex_footbibliography_header": ".. rubric:: Footnote Citations",
    },
)
def test_bibliography_empty_no_header(app, warning) -> None:
    app.build()
    assert not warning.getvalue()
    output = (app.outdir / "index.pseudoxml").read_text(encoding="utf-8")
    assert "Regular Citations" not in output
    assert "Footnote Citations" not in output
    assert "<rubric" not in output


@pytest.mark.sphinx("html", testroot="bibliography_style_default")
def test_bibliography_style_default(app, warning) -> None:
    app.build()
    assert not warning.getvalue()
    output = (app.outdir / "index.html").read_text()
    # ensure Man09 is cited with plain style and not with alpha style
    assert citation_refs(output) == citations(output) == {"1"}


@pytest.mark.sphinx("html", testroot="bibliography_style_label_1")
def test_bibliography_style_label_1(app, warning) -> None:
    app.build()
    assert not warning.getvalue()
    output = (app.outdir / "index.html").read_text()
    # the custom style uses keys as labels
    # citations
    assert len(html_citations(label="myfancybibtexkey").findall(output)) == 1
    assert len(html_citations(label="myotherfancybibtexkey").findall(output)) == 1
    assert len(html_citation_refs(label="myfancybibtexkey").findall(output)) == 1
    assert len(html_citation_refs(label="myotherfancybibtexkey").findall(output)) == 1


@pytest.mark.sphinx("html", testroot="bibliography_style_label_2")
def test_bibliography_style_label_2(app, warning) -> None:
    app.build()
    assert not warning.getvalue()
    output = (app.outdir / "index.html").read_text()
    assert len(html_citation_refs(label="APAa").findall(output)) == 1
    assert len(html_citation_refs(label="APAb").findall(output)) == 1


@pytest.mark.sphinx("html", testroot="bibliography_style_nowebref")
def test_bibliography_style_nowebref(app, warning) -> None:
    app.build()
    assert not warning.getvalue()
    output = (app.outdir / "index.html").read_text(encoding="utf-8")
    # the custom style suppresses web links
    assert "http://arxiv.org" not in output
    assert "http://dx.doi.org" not in output


@pytest.mark.sphinx("html", testroot="bibliography_bad_option")
def test_bibliography_bad_option(app, warning) -> None:
    app.build()
    assert 'unknown option: "thisisintentionallyinvalid"' in warning.getvalue()


# see issue 87
@pytest.mark.sphinx("html", testroot="bibliography_key_prefix")
def test_bibliography_key_prefix(app, warning) -> None:
    app.build()
    assert not warning.getvalue()
    output = (app.outdir / "doc0.html").read_text()
    assert citations(output) == citation_refs(output) == {"AMan09", "AEve03"}
    output = (app.outdir / "doc1.html").read_text()
    assert citations(output) == citation_refs(output) == {"BMan09"}


@pytest.mark.sphinx("html", testroot="bibliography_label_prefix_1")
def test_bibliography_label_prefix_1(app, warning) -> None:
    app.build()
    assert not warning.getvalue()
    output = (app.outdir / "doc1.html").read_text()
    assert citations(output) == citation_refs(output) == {"A1"}
    output = (app.outdir / "doc2.html").read_text()
    assert citations(output) == citation_refs(output) == {"B1"}


@pytest.mark.sphinx("html", testroot="bibliography_label_prefix_2")
def test_bibliography_label_prefix_2(app, warning) -> None:
    doc1_refs = {"AFM12", "ABlu83", "AGIH02", "AWS14"}
    doc1_cites = {"ABlu83", "AFM12", "AGIH02", "AWS14"}
    doc2_refs = {"BShi13"}
    doc2_cites = {"BShi13"}
    sum_refs = {"CMcMahonKM10", "CRMM11", "CRM09", "CMM03", "CHdJMD13", "AFM12"}
    sum_cites = {"CMcMahonKM10", "CRMM11", "CRM09", "CMM03", "CHdJMD13"}
    app.build()
    assert not warning.getvalue()
    output1 = (app.outdir / "doc1.html").read_text(encoding="utf-8-sig")
    # use <= instead of == as there are some extra reference nodes
    assert doc1_refs <= citation_refs(output1)
    assert doc1_cites == citations(output1)
    output2 = (app.outdir / "doc2.html").read_text(encoding="utf-8-sig")
    assert doc2_refs <= citation_refs(output2)
    assert doc2_cites == citations(output2)
    output3 = (app.outdir / "summary.html").read_text(encoding="utf-8-sig")
    assert sum_refs <= citation_refs(output3)
    assert sum_cites == citations(output3)
    # check citation reference from summary to doc1
    match1 = html_citations(label="AFM12").search(output1)
    match3 = html_citation_refs(label="AFM12").search(output3)
    assert match1
    assert match3
    assert match1.group("id_") == match3.group("refid")
    assert match3.group("refdoc") == "doc1.html"


# test order of bibliography entries when using an unsorted style
@pytest.mark.sphinx("html", testroot="bibliography_order_unsorted")
def test_bibliography_order_unsorted(app, warning) -> None:
    app.build()
    assert not warning.getvalue()
    output = (app.outdir / "index.html").read_text(encoding="utf-8-sig")
    assert re.search(
        r"<p>A\.\sFirst\. Test 1\.</p>.*<p>B\.\sSecond\. Test 2\.</p>",
        output,
        re.DOTALL,
    )


# see issue 187
@pytest.mark.sphinx("html", testroot="bibliography_multi_foot")
def test_bibliography_multi_foot(app, warning) -> None:
    app.build()
    assert not warning.getvalue()
    output = (app.outdir / "index.html").read_text(encoding="utf-8")
    assert output.count('<p class="rubric"') == 3
    assert len(re.findall(html_footnotes(id_="footcite-2009-mandel"), output)) == 1
    assert len(re.findall(html_footnotes(id_="footcite-2003-evensen"), output)) == 1
    assert len(re.findall(html_footnotes(id_="footcite-1986-lorenc"), output)) == 1
    assert (
        len(re.findall(html_footnote_refs(refid="footcite-2009-mandel"), output)) == 2
    )
    assert (
        len(re.findall(html_footnote_refs(refid="footcite-2003-evensen"), output)) == 1
    )
    assert (
        len(re.findall(html_footnote_refs(refid="footcite-1986-lorenc"), output)) == 1
    )


@pytest.mark.sphinx("html", testroot="bibliography_missing_field")
def test_bibliography_missing_field(app, warning) -> None:
    app.build()
    assert "missing year in testkey" in warning.getvalue()


@pytest.mark.sphinx("html", testroot="bibliography_content")
def test_bibliography_content(app, warning) -> None:
    app.build()
    assert not warning.getvalue()
    output1 = (app.outdir / "doc1.html").read_text()
    output2 = (app.outdir / "doc2.html").read_text()
    output3 = (app.outdir / "doc3.html").read_text()
    assert citation_refs(output1) == {"One", "Two"}
    assert citations(output1) == {"One", "Two", "Thr"}
    assert citation_refs(output2) == {"Fou", "Fiv"}
    assert citations(output2) == {"Fiv", "Six"}
    assert not citation_refs(output3)
    assert citations(output3) == {"Fou"}


@pytest.mark.sphinx("html", testroot="bibliography_bad_key")
def test_bibliography_bad_key(app, warning) -> None:
    app.build()
    assert 'could not find bibtex key "badkey"' in warning.getvalue()


def url(link: str) -> str:
    return f'<a class="reference external" href="{link}">{link}</a>'


@pytest.mark.sphinx("html", testroot="bibliography_url")
def test_bibliography_url(app, warning) -> None:
    app.build()
    assert not warning.getvalue()
    output = (app.outdir / "index.html").read_text(encoding="utf-8")
    match1 = html_citations(label="Een").search(output)
    match2 = html_citations(label="Twe").search(output)
    match3 = html_citations(label="Dri").search(output)
    match4 = html_citations(label="Vie").search(output)
    assert match1 is not None
    assert match2 is not None
    assert match3 is not None
    assert match4 is not None
    assert url("https://github.com/") in match1.group("text")
    assert "aaa" + url("https://google.com/") + "bbb" in match2.group("text")
    assert url("https://youtube.com/") in match3.group("text")
    assert "URL: " + url("https://wikipedia.org/") in match4.group("text")
    # check tooltips do not contain \url command
    match4 = html_citation_refs(label="Een").search(output)
    match5 = html_citation_refs(label="Twe").search(output)
    match6 = html_citation_refs(label="Dri").search(output)
    match7 = html_citation_refs(label="Vie").search(output)
    assert match4 is not None
    assert match5 is not None
    assert match6 is not None
    assert match7 is not None
    assert "\\url " not in match4.group("title")
    assert "\\url " not in match5.group("title")
    assert "\\url " not in match6.group("title")
    assert "\\url " not in match7.group("title")


@pytest.mark.sphinx("html", testroot="bibliography_custom_ids")
def test_bibliography_custom_ids(app, warning) -> None:
    app.build()
    assert not warning.getvalue()
    output = (app.outdir / "index.html").read_text(encoding="utf-8")
    assert ' id="bibliography-id-1"' in output
    assert ' id="bibliography-id-2"' in output
    assert ' id="footbibliography-id-1"' in output
    assert ' id="footbibliography-id-2"' in output
    match1 = html_citations(text=".*Evensen.*").search(output)
    match2 = html_citations(text=".*Mandel.*").search(output)
    match3 = html_citations(text=".*Lorenc.*").search(output)
    assert match1 is not None
    assert match2 is not None
    assert match3 is not None
    assert match1.group("id_") == "cite-id-1-2003-evensen"
    assert match2.group("id_") == "cite-id-1-2009-mandel"
    assert match3.group("id_") == "cite-id-2-1986-lorenc"
    match1 = html_footnotes(text=".*Evensen.*").search(output)
    match2 = html_footnotes(text=".*Mandel.*").search(output)
    match3 = html_footnotes(text=".*Lorenc.*").search(output)
    assert match1 is not None
    assert match2 is not None
    assert match3 is not None
    assert match1.group("id_") == "footcite-id-2003-evensen"
    assert match2.group("id_") == "footcite-id-2009-mandel"
    assert match3.group("id_") == "footcite-id-1986-lorenc"
