import pytest
import headerparser
from headerparser import HeaderParser


def test_additional() -> None:
    parser = HeaderParser()
    parser.add_field("Foo")
    parser.add_field("Bar")
    parser.add_additional()
    msg = parser.parse("Foo: red\nBar: green\nBaz: blue\n")
    assert dict(msg) == {"Foo": "red", "Bar": "green", "Baz": "blue"}
    assert msg.body is None


def test_many_additional() -> None:
    parser = HeaderParser()
    parser.add_field("Foo")
    parser.add_field("Bar")
    parser.add_additional()
    msg = parser.parse(
        "Foo: red\nBar: green\nBaz: blue\nQUUX: purple\nglarch: orange\n"
    )
    assert dict(msg) == {
        "Foo": "red",
        "Bar": "green",
        "Baz": "blue",
        "QUUX": "purple",
        "glarch": "orange",
    }
    assert msg.body is None


def test_intermixed_additional() -> None:
    parser = HeaderParser()
    parser.add_field("Foo")
    parser.add_field("Bar")
    parser.add_additional()
    msg = parser.parse(
        "QUUX: purple\nBar: green\nglarch: orange\nFoo: red\nBaz: blue\n"
    )
    assert dict(msg) == {
        "Foo": "red",
        "Bar": "green",
        "Baz": "blue",
        "QUUX": "purple",
        "glarch": "orange",
    }
    assert msg.body is None


def test_additional_only() -> None:
    parser = HeaderParser()
    parser.add_additional()
    msg = parser.parse("Foo: red\nBar: green\nBaz: blue\n")
    assert dict(msg) == {"Foo": "red", "Bar": "green", "Baz": "blue"}
    assert msg.body is None


def test_dest_additional() -> None:
    parser = HeaderParser()
    parser.add_field("Foo", dest="dest")
    parser.add_field("Bar")
    with pytest.raises(ValueError) as excinfo:
        parser.add_additional()
    assert "add_additional and `dest` are mutually exclusive" in str(excinfo.value)


def test_additional_dest() -> None:
    parser = HeaderParser()
    parser.add_additional()
    parser.add_field("Foo")
    with pytest.raises(ValueError) as excinfo:
        parser.add_field("Bar", dest="dest")
    assert "add_additional and `dest` are mutually exclusive" in str(excinfo.value)


def test_additional_bad_named_multiple() -> None:
    parser = HeaderParser()
    parser.add_field("Foo")
    parser.add_additional()
    with pytest.raises(headerparser.DuplicateFieldError) as excinfo:
        parser.parse("Foo: red\nFOO: magenta\nBar: green\n")
    assert str(excinfo.value) == "Header field 'Foo' occurs more than once"
    assert excinfo.value.name == "Foo"


def test_additional_named_multiple() -> None:
    parser = HeaderParser()
    parser.add_field("Foo", multiple=True)
    parser.add_additional()
    msg = parser.parse("Foo: red\nFOO: magenta\nBar: green\n")
    assert dict(msg) == {"Foo": ["red", "magenta"], "Bar": "green"}
    assert msg.body is None


def test_additional_bad_multiple() -> None:
    parser = HeaderParser()
    parser.add_field("Foo")
    parser.add_additional()
    with pytest.raises(headerparser.DuplicateFieldError) as excinfo:
        parser.parse("Foo: red\nBar: green\nBar: lime\n")
    assert str(excinfo.value) == "Header field 'Bar' occurs more than once"
    assert excinfo.value.name == "Bar"


def test_additional_bad_multiple_cases() -> None:
    parser = HeaderParser()
    parser.add_field("Foo")
    parser.add_additional()
    with pytest.raises(headerparser.DuplicateFieldError) as excinfo:
        parser.parse("Foo: red\nBar: green\nBAR: lime\n")
    assert str(excinfo.value) == "Header field 'BAR' occurs more than once"
    assert excinfo.value.name == "BAR"


def test_multiple_additional() -> None:
    parser = HeaderParser()
    parser.add_field("Foo")
    parser.add_additional(multiple=True)
    msg = parser.parse("Foo: red\nBar: green\nBAR: lime\n")
    assert dict(msg) == {"Foo": "red", "Bar": ["green", "lime"]}
    assert msg.body is None


def test_one_multiple_additional() -> None:
    parser = HeaderParser()
    parser.add_field("Foo")
    parser.add_additional(multiple=True)
    msg = parser.parse("Foo: red\nBAR: lime\n")
    assert dict(msg) == {"Foo": "red", "BAR": ["lime"]}
    assert msg.body is None


def test_multiple_additional_bad_named_multiple() -> None:
    parser = HeaderParser()
    parser.add_field("Foo")
    parser.add_field("Bar")
    parser.add_additional(multiple=True)
    with pytest.raises(headerparser.DuplicateFieldError) as excinfo:
        parser.parse("Foo: red\nBar: green\nBaz: blue\nFOO: magenta\n")
    assert str(excinfo.value) == "Header field 'Foo' occurs more than once"
    assert excinfo.value.name == "Foo"


def test_additional_missing_named() -> None:
    parser = HeaderParser()
    parser.add_field("Foo")
    parser.add_field("Bar")
    parser.add_additional()
    msg = parser.parse("Baz: blue\nQUUX: purple\nglarch: orange\n")
    assert dict(msg) == {"Baz": "blue", "QUUX": "purple", "glarch": "orange"}
    assert msg.body is None


def test_additional_missing_required_named() -> None:
    parser = HeaderParser()
    parser.add_field("Foo", required=True)
    parser.add_field("Bar")
    parser.add_additional()
    with pytest.raises(headerparser.MissingFieldError) as excinfo:
        parser.parse("Baz: blue\nQUUX: purple\nglarch: orange\n")
    assert str(excinfo.value) == "Required header field 'Foo' is not present"
    assert excinfo.value.name == "Foo"


def test_missing_additional() -> None:
    parser = HeaderParser()
    parser.add_field("Foo")
    parser.add_field("Bar")
    parser.add_additional()
    msg = parser.parse("Foo: red\nBar: green\n")
    assert dict(msg) == {"Foo": "red", "Bar": "green"}
    assert msg.body is None


def test_additional_type() -> None:
    parser = HeaderParser()
    parser.add_field("Foo")
    parser.add_field("Bar")
    parser.add_additional(type=int)
    msg = parser.parse("Foo: 1\nBar: 2\nBaz: 3\n")
    assert dict(msg) == {"Foo": "1", "Bar": "2", "Baz": 3}
    assert msg.body is None


def test_additional_bad_type() -> None:
    parser = HeaderParser()
    parser.add_field("Foo")
    parser.add_field("Bar")
    parser.add_additional(type=int)
    with pytest.raises(headerparser.FieldTypeError) as excinfo:
        parser.parse("Foo: 1\nBar: 2\nBaz: three\n")
    assert str(excinfo.value) == (
        "Error while parsing 'Baz': 'three': ValueError: "
        + str(excinfo.value.exc_value)
    )
    assert excinfo.value.name == "Baz"
    assert excinfo.value.value == "three"
    assert isinstance(excinfo.value.exc_value, ValueError)


def test_additional_choices() -> None:
    parser = HeaderParser()
    parser.add_field("Foo")
    parser.add_field("Bar")
    parser.add_additional(choices=["red", "green", "blue"])
    msg = parser.parse("Foo: mauve\nBar: red\nBaz: green\nQuux: blue\n")
    assert dict(msg) == {
        "Foo": "mauve",
        "Bar": "red",
        "Baz": "green",
        "Quux": "blue",
    }
    assert msg.body is None


def test_additional_bad_choices() -> None:
    parser = HeaderParser()
    parser.add_field("Foo")
    parser.add_field("Bar")
    parser.add_additional(choices=["red", "green", "blue"])
    with pytest.raises(headerparser.InvalidChoiceError) as excinfo:
        parser.parse("Foo: mauve\nBar: red\nBaz: green\nQuux: taupe\n")
    assert str(excinfo.value) == "'taupe' is not a valid choice for 'Quux'"
    assert excinfo.value.name == "Quux"
    assert excinfo.value.value == "taupe"


def test_additional_unfold() -> None:
    parser = HeaderParser()
    parser.add_field("Foo")
    parser.add_additional(unfold=True)
    msg = parser.parse(
        "Foo: This is\n"
        "   test\n"
        "  text.\n"
        "Bar: This is\n"
        "   test\n"
        "  text.\n"
    )
    assert dict(msg) == {
        "Foo": "This is\n   test\n  text.",
        "Bar": "This is test text.",
    }
    assert msg.body is None


def test_bad_additional_dest() -> None:
    parser = HeaderParser()
    parser.add_field("Foo")
    parser.add_field("Bar")
    with pytest.raises(TypeError):
        parser.add_additional(dest="somewhere")


def test_bad_additional_required() -> None:
    parser = HeaderParser()
    parser.add_field("Foo")
    parser.add_field("Bar")
    with pytest.raises(TypeError):
        parser.add_additional(required=True)


def test_bad_additional_default() -> None:
    parser = HeaderParser()
    parser.add_field("Foo")
    parser.add_field("Bar")
    with pytest.raises(TypeError):
        parser.add_additional(default="")


def test_additional_multiname() -> None:
    parser = HeaderParser()
    parser.add_field("Foo", "Oof")
    parser.add_field("Bar", "Baz")
    parser.add_additional()
    msg = parser.parse("Oof: red\nBar: green\nQuux: blue\n")
    assert dict(msg) == {"Foo": "red", "Bar": "green", "Quux": "blue"}
    assert msg.body is None


def test_additional_off() -> None:
    parser = HeaderParser()
    parser.add_field("Foo")
    parser.add_field("Bar")
    parser.add_additional(False)
    with pytest.raises(headerparser.UnknownFieldError) as excinfo:
        parser.parse("Foo: red\nBar: green\nBaz: blue\n")
    assert str(excinfo.value) == "Unknown header field 'Baz'"
    assert excinfo.value.name == "Baz"
