import datetime
import decimal
import operator
import re

import pytest

import pydash as _

from . import helpers


parametrize = pytest.mark.parametrize


@parametrize(
    "value,other,expected",
    [("a", "a", True), (None, None, True), (None, "", False), (1, str(1), False)],
)
def test_eq(value, other, expected):
    assert _.eq(value, other) == expected
    assert _.eq_cmp(other)(value) == expected


@parametrize(
    "case,expected",
    [
        ((2, 1), True),
        ((2.5, 2.3), True),
        ((1, 2), False),
        ((2.3, 2.5), False),
        ((1, 1), False),
    ],
)
def test_gt(case, expected):
    assert _.gt(*case) == expected
    assert _.gt_cmp(case[1])(case[0]) == expected


@parametrize(
    "case,expected",
    [
        ((2, 1), True),
        ((2.5, 2.3), True),
        ((1, 2), False),
        ((2.3, 2.5), False),
        ((1, 1), True),
    ],
)
def test_gte(case, expected):
    assert _.gte(*case) == expected
    assert _.gte_cmp(case[1])(case[0]) == expected


@parametrize(
    "case,expected",
    [
        ((2, 1), False),
        ((2.5, 2.3), False),
        ((1, 2), True),
        ((2.3, 2.5), True),
        ((1, 1), False),
    ],
)
def test_lt(case, expected):
    assert _.lt(*case) == expected
    assert _.lt_cmp(case[1])(case[0]) == expected


@parametrize(
    "case,expected",
    [
        ((2, 1), False),
        ((2.5, 2.3), False),
        ((1, 2), True),
        ((2.3, 2.5), True),
        ((1, 1), True),
    ],
)
def test_lte(case, expected):
    assert _.lte(*case) == expected
    assert _.lte_cmp(case[1])(case[0]) == expected


@parametrize(
    "case,expected",
    [
        ((3, 2, 4), True),
        ((4, 8), True),
        ((4, 2), False),
        ((2, 2), False),
        ((1.2, 2), True),
        ((5.2, 4), False),
        (("", 5), False),
        ((2, ""), False),
        ((-1, -2, ""), True),
    ],
)
def test_in_range(case, expected):
    assert _.in_range(*case) == expected
    assert _.in_range_cmp(*case[1:])(case[0]) == expected


@parametrize(
    "case,expected",
    [
        ([], True),
        ({}, True),
        ("", True),
        (0, False),
        (True, False),
    ],
)
def test_is_associative(case, expected):
    assert _.is_associative(case) == expected


@parametrize(
    "case,expected",
    [
        ("", True),
        ("\n", True),
        (" ", True),
        ("a", False),
    ],
)
def test_is_blank(case, expected):
    assert _.is_blank(case) == expected


@parametrize(
    "case,expected",
    [
        (True, True),
        (False, True),
        (0, False),
        ("", False),
    ],
)
def test_is_boolean(case, expected):
    assert _.is_boolean(case) == expected


@parametrize(
    "case,expected",
    [
        (list, True),
        (dict, True),
        ([].append, True),
        ({}.update, True),
        (1, True),
        (True, True),
        (None, True),
        (datetime, False),
        (parametrize, False),
        ("a", False),
        (lambda: None, False),
    ],
)
def test_is_builtin(case, expected):
    assert _.is_builtin(case) == expected


@parametrize(
    "case,expected",
    [
        (datetime.date.today(), True),
        (datetime.datetime.today(), True),
        ("2014-01-01", False),
        ("2014-01-01T00:00:00", False),
        (1, False),
    ],
)
def test_is_date(case, expected):
    assert _.is_date(case) == expected


@parametrize(
    "case,expected",
    [
        ([3, 2, 1], True),
        ([6, 5, 5], True),
        (0, True),
        ([5, 4, 4, 3, 1], True),
        ([5, 4, 4, 5, 4, 3], False),
    ],
)
def test_is_decreasing(case, expected):
    assert _.is_decreasing(case) == expected


@parametrize(
    "case,expected",
    [({}, True), ([], False), (1, False), ("a", False), (iter([]), False), (iter({}), False)],
)
def test_is_dict(case, expected):
    assert _.is_dict(case) == expected


@parametrize(
    "case,expected",
    [
        (True, True),
        (0, True),
        (123.45, True),
        ("", True),
        ({}, True),
        ([], True),
        (False, True),
        (None, True),
        ({"a": 1}, False),
        ([1], False),
        ("Hello", False),
        (["Hello", "World"], False),
    ],
)
def test_is_empty(case, expected):
    assert _.is_empty(case) == expected


@parametrize(
    "case,expected",
    [
        ((1, 1), True),
        ((1, 2), False),
        (("1", "1"), True),
        (("1", "2"), False),
        (([1], {"a": 1}), False),
        (({"a": 1}, {"a": 1}), True),
        (({"a": 1}, {"b": 1}), False),
        (([1, 2, 3], [1, 2, 3]), True),
        (([1, 2, 3], [1, 2]), False),
        (([1, 2], [1, 2, 3]), False),
        (([1, 2, 3], [1, 2]), False),
        (([1, 2], [1, 2, 3]), False),
        ((["hello", "goodbye"], ["hi", "goodbye"]), False),
    ],
)
def test_is_equal(case, expected):
    assert _.is_equal(*case) == expected
    assert _.is_equal_cmp(*case[1:])(case[0]) == expected


@parametrize(
    "case,expected",
    [
        ((1, 1, None), True),
        ((1, 2, None), False),
        (("1", "1", None), True),
        (("1", "2", None), False),
        (([1], {"a": 1}, None), False),
        (([1], {"a": 1}, lambda a, b: True), True),
        (({"a": 1}, {"a": 1}, None), True),
        (({"a": 1}, {"b": 1}, lambda a, b: None if isinstance(a, dict) else True), False),
        (([1, 2, 3], [1, 2, 3], None), True),
        (([1, 2, 3], [1, 2], None), False),
        (([1, 2], [1, 2, 3], None), False),
        (([1, 2, 3], [1, 2], lambda a, b: None if isinstance(a, list) else True), False),
        (([1, 2], [1, 2, 3], lambda a, b: None if isinstance(a, list) else True), False),
        ((["hello", "goodbye"], ["hi", "goodbye"], helpers.is_equal_iteratee0), True),
    ],
)
def test_is_equal_with(case, expected):
    assert _.is_equal_with(*case) == expected
    assert _.is_equal_with_cmp(*case[1:])(case[0]) == expected


@parametrize("case,expected", [(Exception(), True), ({}, False), ([], False)])
def test_is_error(case, expected):
    assert _.is_error(case) == expected


@parametrize(
    "case,expected",
    [
        (2, True),
        (16, True),
        (1, False),
        (3.0, False),
        (3.5, False),
        (None, False),
    ],
)
def test_is_even(case, expected):
    assert _.is_even(case) == expected


@parametrize(
    "case,expected",
    [
        (1.0, True),
        (3.245, True),
        (1, False),
        (True, False),
        ("", False),
    ],
)
def test_is_float(case, expected):
    assert _.is_float(case) == expected


@parametrize(
    "case,expected",
    [
        (lambda x: x + 1, True),
        ("Hello, world!", False),
    ],
)
def test_is_function(case, expected):
    assert _.is_function(case) == expected


@parametrize(
    "case,expected",
    [
        ([1, 2, 3], True),
        ([5, 5, 6], True),
        (0, True),
        ([1, 2, 3, 4, 4, 5], True),
        ([1, 2, 3, 4, 4, 3], False),
    ],
)
def test_is_increasing(case, expected):
    assert _.is_increasing(case) == expected


@parametrize(
    "case,expected",
    [
        ([], True),
        ("", True),
        ((), True),
        ({}, False),
        (1, False),
        (None, False),
    ],
)
def test_is_indexed(case, expected):
    assert _.is_indexed(case) == expected


@parametrize(
    "case,expected",
    [
        ((1, int), True),
        ((1.0, float), True),
        (([], (list, str)), True),
        (([], dict), False),
        ((True, float), False),
    ],
)
def test_is_instance_of(case, expected):
    assert _.is_instance_of(*case) == expected
    assert _.is_instance_of_cmp(case[1])(case[0]) == expected


@parametrize(
    "case,expected",
    [
        (1, True),
        (2, True),
        (1.0, False),
        (True, False),
        (None, False),
        ([], False),
    ],
)
def test_is_integer(case, expected):
    assert _.is_integer(case) == expected


@parametrize(
    "case,expected",
    [
        ([], True),
        ({}, True),
        ((), True),
        ("a", True),
        (5, False),
        (None, False),
    ],
)
def test_is_iterable(case, expected):
    assert _.is_iterable(case) == expected


@parametrize(
    "case,expected",
    [
        ('{"one": 1, "two": {"three": "3"}, "four": [4]}', True),
        ({"one": 1, "two": {"three": "3"}, "four": [4]}, False),
        ("", False),
        (1, False),
        (True, False),
    ],
)
def test_is_json(case, expected):
    assert _.is_json(case) == expected


@parametrize(
    "case,expected",
    [
        ([1, 2, 3], True),
        ({}, False),
    ],
)
def test_is_list(case, expected):
    assert _.is_list(case) == expected


@parametrize(
    "case,expected",
    [
        (({"name": "fred", "age": 40}, {"age": 40}), True),
        (({"name": "fred", "age": 40}, {"age": 40, "active": True}), False),
        (([1, 2, 3], [1, 2]), True),
        (([1, 2, 3], [1, 2, 3, 4]), False),
        (({}, {}), True),
        (({"a": 1}, {}), True),
        (([], []), True),
        (([1], []), True),
    ],
)
def test_is_match(case, expected):
    assert _.is_match(*case) == expected
    assert _.is_match_cmp(case[1])(case[0]) == expected


@parametrize(
    "case,expected",
    [
        (([1, 2], [2, 4], lambda a, b: None if isinstance(a, list) else b == a + a), True),
        (([1, 2], [2, 4], lambda a, b, key: a == b == key), False),
        (([0, 1], [0, 1], lambda a, b, key: a == b == key), True),
    ],
)
def test_is_match_with(case, expected):
    assert _.is_match_with(*case) == expected
    assert _.is_match_with_cmp(*case[1:])(case[0]) == expected


@parametrize(
    "case,expected",
    [
        (([1, 2, 3], operator.le), True),
        (([3, 2, 1], operator.ge), True),
        (([1, 1, 2], operator.lt), False),
        (([3, 3, 2], operator.gt), False),
    ],
)
def test_is_monotone(case, expected):
    assert _.is_monotone(*case) == expected
    assert _.is_monotone_cmp(case[1])(case[0]) == expected


@parametrize(
    "case,expected",
    [
        (0, False),
        (123456789123456789123456789, False),
        (123.45, False),
        (decimal.Decimal(1), False),
        ("1", True),
    ],
)
def test_is_nan(case, expected):
    assert _.is_nan(case) == expected


@parametrize(
    "case,expected",
    [
        (-1, True),
        (-1.25, True),
        (-0.1, True),
        (0, False),
        (1, False),
        (True, False),
        (False, False),
    ],
)
def test_is_negative(case, expected):
    assert _.is_negative(case) == expected


@parametrize(
    "case,expected",
    [
        (None, True),
        (0, False),
    ],
)
def test_is_none(case, expected):
    assert _.is_none(case) == expected


@parametrize(
    "case,expected",
    [
        (0, True),
        (123456789123456789123456789, True),
        (123.45, True),
        (decimal.Decimal(1), True),
        ("1", False),
        (True, False),
        (False, False),
    ],
)
def test_is_number(case, expected):
    assert _.is_number(case) == expected


@parametrize(
    "case,expected",
    [({}, True), ([], True), (1, False), ("a", False), (iter([]), False), (iter({}), False)],
)
def test_is_object(case, expected):
    assert _.is_object(case) == expected


@parametrize(
    "case,expected",
    [
        (1, True),
        (3.0, True),
        (3.5, True),
        (2, False),
        (16, False),
        (None, False),
    ],
)
def test_is_odd(case, expected):
    assert _.is_odd(case) == expected


@parametrize(
    "case,expected",
    [(1, True), (1.25, True), (0.1, True), (-1, False), (0, False), (True, False), (False, False)],
)
def test_is_positive(case, expected):
    assert _.is_positive(case) == expected


@parametrize(
    "case,expected",
    [
        (re.compile(""), True),
        ("", False),
        ("Hello, world!", False),
        (1, False),
        ({}, False),
        ([], False),
        (None, False),
    ],
)
def test_is_reg_exp(case, expected):
    assert _.is_reg_exp(case) == expected


@parametrize("case,expected", [(set(), True), ([1, 2, 3], False)])
def test_is_set(case, expected):
    assert _.is_set(case) == expected


@parametrize(
    "case,expected",
    [
        ([1, 2, 3], False),
        ([3, 2, 1], True),
        ([1, 1, 2], False),
        ([3, 3, 2], False),
    ],
)
def test_is_strictly_decreasing(case, expected):
    assert _.is_strictly_decreasing(case) == expected


@parametrize(
    "case,expected",
    [
        ([1, 2, 3], True),
        ([3, 2, 1], False),
        ([1, 1, 2], False),
        ([3, 3, 2], False),
    ],
)
def test_is_strictly_increasing(case, expected):
    assert _.is_strictly_increasing(case) == expected


@parametrize(
    "case,expected",
    [("", True), ("Hello, world!", True), (1, False), ({}, False), ([], False), (None, False)],
)
def test_is_string(case, expected):
    assert _.is_string(case) == expected


@parametrize(
    "case,expected",
    [
        ((), True),
        ([], False),
        ({}, False),
    ],
)
def test_is_tuple(case, expected):
    assert _.is_tuple(case) == expected


@parametrize(
    "case,expected",
    [
        (0, True),
        (0.0, False),
        ("", False),
        (True, False),
        (False, False),
    ],
)
def test_is_zero(case, expected):
    assert _.is_zero(case) == expected
