import enum
from collections import Counter
from datetime import date, datetime, timezone
from decimal import Decimal
from uuid import uuid4

import pytest
from hypothesis import assume, given
from hypothesis.strategies import decimals

from faust.utils.json import JSONEncoder, str_to_decimal


@given(decimals())
def test_str_to_decimal_decimals(x):
    assume(x.is_finite())
    assert str_to_decimal(str(x)) == x


def test_str_to_decimal_None():
    assert str_to_decimal(None) is None


def test_str():
    d1 = Decimal("3.3333433434343434343434343434343")
    assert str_to_decimal(str(d1)) == d1


def test_maxlen():
    s = "3." + "34" * 1000
    with pytest.raises(ValueError):
        str_to_decimal(s)


def test_NaN():
    with pytest.raises(ValueError):
        str_to_decimal("NaN")


def test_Inf():
    with pytest.raises(ValueError):
        str_to_decimal("Inf")


def test_negative_Inf():
    with pytest.raises(ValueError):
        str_to_decimal("-Inf")


def test_sNaN():
    with pytest.raises(ValueError):
        str_to_decimal("sNaN")


class Flags(enum.Enum):
    X = "Xval"
    Y = "Yval"


class CanJson:
    def __json__(self):
        return "yes"


def test_JSONEncoder():
    encoder = JSONEncoder()
    assert encoder.default(date(2016, 3, 2))
    assert encoder.default(datetime.utcnow())
    assert encoder.default(datetime.now(timezone.utc))
    assert encoder.default(Counter([("foo", 3), ("bar", 4)]))
    assert encoder.default(uuid4())
    assert encoder.default(frozenset({1, 2, 3})) == [1, 2, 3]
    assert encoder.default(Flags.X) == "Xval"
    assert encoder.default(Flags.Y) == "Yval"
    assert encoder.default({1, 2, 3}) == [1, 2, 3]
    assert encoder.default(CanJson()) == "yes"
    with pytest.raises(TypeError):
        encoder.default(object())
