import pytest

import ubelt as ub


def test_auto_dict() -> None:
    auto = ub.AutoDict()
    assert 0 not in auto
    auto[0][10][100] = None
    assert 0 in auto
    assert isinstance(auto[0], ub.AutoDict)


def test_auto_dict_to_dict() -> None:
    from ubelt.util_dict import AutoDict

    auto = AutoDict()
    auto[1] = 1
    auto['n1'] = AutoDict()
    auto['n1']['n2'] = AutoDict()
    auto['n1']['n2'][2] = 2
    auto['n1']['n2'][3] = 3
    auto['dict'] = {}
    auto['dict']['n3'] = AutoDict()
    auto['dict']['n3']['n4'] = AutoDict()
    print('auto = {!r}'.format(auto))
    static = auto.to_dict()
    print('static = {!r}'.format(static))
    assert not isinstance(static, AutoDict), '{}'.format(type(static))
    assert not isinstance(static['n1'], AutoDict), '{}'.format(
        type(static['n1'])
    )
    assert not isinstance(static['n1']['n2'], AutoDict)
    assert isinstance(static['dict']['n3'], AutoDict)
    assert isinstance(static['dict']['n3']['n4'], AutoDict)


def test_auto_dict_ordered() -> None:
    # To Dict should respect ordering
    from ubelt.util_dict import AutoDict, AutoOrderedDict

    auto = AutoOrderedDict()
    auto[0][3] = 3
    auto[0][2] = 2
    auto[0][1] = 1
    auto[0][4] = AutoDict()
    assert isinstance(auto, AutoDict)
    print('auto = {!r}'.format(auto))
    static = auto.to_dict()
    print('static = {!r}'.format(static))
    assert not isinstance(static, AutoDict), 'bad cast {}'.format(type(static))
    assert not isinstance(static[0][4], AutoDict), 'bad cast {}'.format(
        type(static[0][4])
    )
    assert list(auto[0].values())[0:3] == [3, 2, 1], 'maintain order'


def test_dzip_errors() -> None:
    with pytest.raises(TypeError):
        ub.dzip([1], 2)  # type: ignore
    with pytest.raises(TypeError):
        ub.dzip(1, [2])  # type: ignore
    with pytest.raises(ValueError):
        ub.dzip([1, 2, 3], [])
    with pytest.raises(ValueError):
        ub.dzip([], [4, 5, 6])
    with pytest.raises(ValueError):
        ub.dzip([1, 2, 3], [4, 5])


def test_group_items_callable() -> None:
    pairs = [
        ('ham', 'protein'),
        ('jam', 'fruit'),
        ('spam', 'protein'),
        ('eggs', 'protein'),
        ('cheese', 'dairy'),
        ('banana', 'fruit'),
    ]
    items, groupids = zip(*pairs)
    lut = dict(zip(items, groupids))

    result1 = ub.group_items(items, groupids)
    result2 = ub.group_items(items, lut.__getitem__)

    result1 = ub.map_values(lambda x: set(x), result1)  # type: ignore[arg-type,return-value]
    result2 = ub.map_values(lambda x: set(x), result2)  # type: ignore[arg-type,return-value]
    assert result1 == result2


def test_dict_hist_ordered() -> None:
    import random
    import string

    import ubelt as ub

    rng = random.Random(0)
    items = [rng.choice(string.ascii_letters) for _ in range(100)]
    # Ensure that the ordered=True bug is fixed
    a = sorted(ub.dict_hist(items, ordered=True).items())
    b = sorted(ub.dict_hist(items, ordered=False).items())
    assert a == b


def test_dict_subset_iterable() -> None:
    """
    CommandLine:
        xdoctest -m ~/code/ubelt/tests/test_dict.py test_dict_subset_iterable
    """
    # There was a bug in 0.7.0 where iterable keys would be exhausted too soon
    keys_list = list(range(10))
    dict_ = {k: k for k in keys_list}
    got = ub.dict_subset(dict_, iter(keys_list))
    assert dict(got) == dict_


def test_dict_subset_default() -> None:
    """
    Test dict_subset with default value for missing keys.
    """
    dict_ = {'a': 1, 'b': 2}
    keys = ['a', 'c', 'b']
    got = ub.dict_subset(dict_, keys, default=0)
    expected = {'a': 1, 'c': 0, 'b': 2}
    assert dict(got) == expected


# def _benchmark_groupid_sorted():
#     import random
#     import ubelt as ub

#     ydata = ub.ddict(list)
#     xdata = []

#     ti = ub.Timerit(100, bestof=10, verbose=True)

#     num = 10
#     for gamma in [0.01, .1, .5]:
#         for num in [10, 100, 1000, 10000, 100000]:
#             items = [random.random() for _ in range(num)]
#             groupids = [random.randint(0, int(num ** gamma)) for _ in range(num)]

#             xdata.append(num)

#             for timer in ti.reset(label='sort_g{}'.format(gamma)):
#                 with timer:
#                     ub.group_items(items, groupids, sorted_=True)
#             ydata[ti.label].append(ti.min())

#             for timer in ti.reset(label='nosort_g{}'.format(gamma)):
#                 with timer:
#                     ub.group_items(items, groupids, sorted_=False)
#             ydata[ti.label].append(ti.min())

#     ydata = ub.odict(sorted(ydata.items(), key=lambda t: t[1][-1])[::-1])
#     import netharn as nh
#     nh.util.autompl()
#     nh.util.multi_plot(xdata, ydata)
