import pytest
from easyansi._core import validations as v


####################################################################################################
# build_error_msg
####################################################################################################


@pytest.mark.parametrize(
    "error_msg,error_value,expected_msg",
    [("Field 1 has failed", "badData", "Field 1 has failed: badData"),
     ("Field 2 has failed", 123, "Field 2 has failed: 123"),
     ("Field 3 is none", None, "Field 3 is none"),
     ("Field 4 is empty", "", "Field 4 is empty: "),
     (None, None, "None")
     ])
def test_build_error_msg_no_kwargs(error_msg, error_value, expected_msg):
    actual_msg = v.build_error_msg(error_msg, error_value)
    assert expected_msg == actual_msg


@pytest.mark.parametrize(
    "error_msg,error_value,expected_msg,kwargs",
    [("Field 1 has failed", "badData", "Field 1 has failed: badData, field: Name",
      {"field": "Name"}),
     ("Field 2 has failed", 123, "Field 2 has failed: 123, Expected: 456",
      {"Expected": 456}),
     ("Field 3 is none", None, "Field 3 is none, field: addr, isEmpty: False",
      {"field": "addr", "isEmpty": False}),
     ])
def test_build_error_msg_with_kwargs(error_msg, error_value, expected_msg, kwargs):
    actual_msg = v.build_error_msg(error_msg, error_value, **kwargs)
    assert expected_msg == actual_msg


####################################################################################################
# validate_is_integer
####################################################################################################


@pytest.mark.parametrize(
    "value,field",
    [(None, "Field1"), ("String", "Field2"), (123.45, "Field3")])
def test_validate_is_integer_non_integers(value, field):
    expected_msg = "Number is not an integer: " + str(value) + ", Field: " + field
    with pytest.raises(ValueError) as vl_error:
        v.validate_is_integer(value, field)
    assert expected_msg == str(vl_error.value)


@pytest.mark.parametrize(
    "value,field",
    [(0, "field0"), (-1, "field1"), (1, "field2"), (-50, "field50"), (75, "field75")])
def test_validate_is_integer_with_integers(value, field):
    # Test passes if no error is raised
    v.validate_is_integer(value, field)


####################################################################################################
# validate_is_string
####################################################################################################


@pytest.mark.parametrize(
    "value,field",
    [(None, "Field1"), (12, "Field2"), (123.45, "Field3")])
def test_validate_is_string_non_strings(value, field):
    expected_msg = "Text is not a string: " + str(value) + ", Field: " + field
    with pytest.raises(ValueError) as vl_error:
        v.validate_is_string(value, field)
    assert expected_msg == str(vl_error.value)


@pytest.mark.parametrize(
    "value,field",
    [(str(10), "field0"), ("A", "field1"), (str(12.34), "field2")])
def test_validate_is_string_with_strings(value, field):
    # Test passes if no error is raised
    v.validate_is_string(value, field)


####################################################################################################
# validate_is_not_empty_string
####################################################################################################


def test_validate_is_not_empty_string_invalid_type():
    value = 4
    field_name = "field0"
    with pytest.raises(ValueError) as vl_error:
        v.validate_is_not_empty_string(value, field_name)
    assert field_name in str(vl_error.value)


def test_validate_is_not_empty_string_empty_string():
    value = ""
    field_name = "field0"
    expected_msg = "Text is empty, Field: field0"
    with pytest.raises(ValueError) as vl_error:
        v.validate_is_not_empty_string(value, field_name)
    assert expected_msg == str(vl_error.value)


def test_validate_is_not_empty_string_empty_string():
    # Test passes if no exception is raised.
    value = "abc"
    field_name = "field0"
    v.validate_is_not_empty_string(value, field_name)


####################################################################################################
# validate_in_range
####################################################################################################


@pytest.mark.parametrize(
    "value,minimum,maximum,field,error_contains",
    [("A", 0, 10, "Field1", "Field1"),
     (5, "A", 10, "Field2", "Minimum Value"),
     (5, 0, "A", "Field3", "Maximum Value")])
def test_validate_in_range_invalid_parameters(value, minimum, maximum, field, error_contains):
    with pytest.raises(ValueError) as vl_error:
        v.validate_in_range(value, minimum, maximum, field)
    assert error_contains in str(vl_error.value)


def test_validate_in_range_minimum_greater_than_maximum():
    value = 5
    minimum = 10
    maximum = 3
    field = "Field1"
    expected_msg = "Minimum > Maximum, Field: Field1, Minimum: 10, Maximum: 3"
    with pytest.raises(ValueError) as vl_error:
        v.validate_in_range(value, minimum, maximum, field)
    assert expected_msg == str(vl_error.value)


@pytest.mark.parametrize(
    "value,minimum,maximum,field",
    [(11, 0, 10, "Field1"),
     (1, 2, 10, "Field2"),
     (-1, 0, 10, "Field3"),
     (-2, -5, -3, "Field4")])
def test_validate_in_range_out_of_range(value, minimum, maximum, field):
    expected_msg = ("Number out of range: " + str(value) + ", Field: " + field +
                    ", Minimum: " + str(minimum) + ", Maximum: " + str(maximum))
    with pytest.raises(ValueError) as vl_error:
        v.validate_in_range(value, minimum, maximum, field)
    assert expected_msg == str(vl_error.value)


@pytest.mark.parametrize(
    "value,minimum,maximum,field",
    [(5, 0, 10, "Field1"),
     (2, 2, 10, "Field2"),
     (10, 0, 10, "Field3"),
     (-4, -5, -3, "Field4")])
def test_validate_in_range_within_range(value, minimum, maximum, field):
    # Test passes if no error is raised
    v.validate_in_range(value, minimum, maximum, field)


####################################################################################################
# validate_at_least_minimum
####################################################################################################

@pytest.mark.parametrize(
    "value,minimum,field,error_contains",
    [("A", 1, "Field1", "Field1"),
     (1, "B", "Field2", "Minimum Value")])
def test_validate_at_least_minimum_invalid_parameters(value, minimum, field, error_contains):
    with pytest.raises(ValueError) as vl_error:
        v.validate_at_least_minimum(value, minimum, field)
    assert error_contains in str(vl_error.value)


@pytest.mark.parametrize(
    "value,minimum,field",
    [(4, 5, "Field1"),
     (-1, 0, "Field2"),
     (-6, -5, "Field3")])
def test_validate_at_least_minimum_below_minimum(value, minimum, field):
    expected_msg = ("Number < Minimum: " + str(value) + ", Field: " + field +
                    ", Minimum: " + str(minimum))
    with pytest.raises(ValueError) as vl_error:
        v.validate_at_least_minimum(value, minimum, field)
    assert expected_msg == str(vl_error.value)


@pytest.mark.parametrize(
    "value,minimum,field",
    [(6, 5, "Field1"),
     (1, 0, "Field2"),
     (0, 0, "Field4"),
     (-5, -5, "Field3")])
def test_validate_at_least_minimum_valid_values(value, minimum, field):
    # Test passes if no error is raised
    v.validate_at_least_minimum(value, minimum, field)


####################################################################################################
# validate_in_choices
####################################################################################################


def test_validate_in_choices_no_choices():
    value = "a"
    field = "field0"
    expected_msg = "No choices provided to choose from, Field: " + field
    with pytest.raises(ValueError) as vl_error:
        v.validate_in_choices(value, field)
    assert expected_msg == str(vl_error.value)


@pytest.mark.parametrize(
    "value,field_name,choice0,choice1,choice2",
    [(1, "field0", 2, 3, 5),
     ("b", "field1", "a", "bb", "c"),
     (False, "field2", True, True, True),
     (3.1, "field3", 1.23, 2.34, 3.14)])
def test_validate_in_choices_invalid_choice(value, field_name, choice0, choice1, choice2):
    choice_tuple = (choice0, choice1, choice2)
    expected_msg = ("Value not found in choices: " + str(value) +
                    ", Field: " + field_name + ", Choices: " + str(choice_tuple))
    with pytest.raises(ValueError) as vl_error:
        v.validate_in_choices(value, field_name, choice0, choice1, choice2)
    assert expected_msg == str(vl_error.value)


@pytest.mark.parametrize(
    "value,field_name,choice0,choice1,choice2",
    [(3, "field0", 2, 3, 5),
     ("a", "field1", "a", "bb", "c"),
     (True, "field2", True, False, True),
     (3.14, "field3", 1.23, 2.34, 3.14)])
def test_validate_in_choices_valid_choice(value, field_name, choice0, choice1, choice2):
    # Test passes if no error is raised
    v.validate_in_choices(value, field_name, choice0, choice1, choice2)
