import pytest
import miska.dicts as dicts
from miska import missing


# --------------------------------------------------
# new()
# --------------------------------------------------

def test_new_excludes_and_updates():
    data = {"a": 1, "b": 2, "c": 3}

    result = dicts.new(data, "b", extra=100)

    assert result == {"a": 1, "c": 3, "extra": 100}
    # original must stay unchanged
    assert data == {"a": 1, "b": 2, "c": 3}


# --------------------------------------------------
# wipe()
# --------------------------------------------------

def test_wipe_single_key_copy():
    data = {"x": 1, "y": 2}

    result = dicts.wipe(data, key="x", copy=True)

    assert result == {"y": 2}
    # original untouched because copy=True
    assert data == {"x": 1, "y": 2}


def test_wipe_multiple_keys_copy():
    data = {"a": 1, "b": 2, "c": 3}

    result = dicts.wipe(data, keys=("a", "c"), copy=True)

    assert result == {"b": 2}
    assert data == {"a": 1, "b": 2, "c": 3}


def test_wipe_in_place():
    data = {"a": 1, "b": 2}

    result = dicts.wipe(data, key="a", copy=False)

    assert result is data
    assert data == {"b": 2}


def test_wipe_ignores_missing_sentinel():
    data = {"a": 1}

    result = dicts.wipe(data, key=missing.MISSING, copy=True)

    assert result == {"a": 1}


# --------------------------------------------------
# move()
# --------------------------------------------------

def test_move_copy():
    data = {"one": 1, "two": 2}

    result = dicts.move(
        data,
        key="one",
        new_key="uno",
        copy=True,
    )

    assert result == {"uno": 1, "two": 2}
    assert data == {"one": 1, "two": 2}


def test_move_in_place():
    data = {"a": 10}

    result = dicts.move(
        data,
        key="a",
        new_key="b",
        copy=False,
    )

    assert result is data
    assert data == {"b": 10}


# --------------------------------------------------
# rekey()
# --------------------------------------------------

def test_rekey_all_copy():
    data = {"a": 1, "b": 2}

    result = dicts.rekey(
        data,
        func=lambda k: k.upper(),
        copy=True,
    )

    assert result == {"A": 1, "B": 2}
    assert data == {"a": 1, "b": 2}


def test_rekey_specific_keys():
    data = {"a": 1, "b": 2, "c": 3}

    result = dicts.rekey(
        data,
        func=lambda k: k.upper(),
        keys=("b",),
        copy=True,
    )

    assert result == {"a": 1, "B": 2, "c": 3}


# --------------------------------------------------
# revalue()
# --------------------------------------------------

def test_revalue_all_copy():
    data = {"a": 2, "b": 3}

    result = dicts.revalue(
        data,
        func=lambda v: v * 5,
        copy=True,
    )

    assert result == {"a": 10, "b": 15}
    assert data == {"a": 2, "b": 3}


def test_revalue_specific_keys():
    data = {"a": 2, "b": 3}

    result = dicts.revalue(
        data,
        func=lambda v: v + 1,
        keys=("b",),
        copy=True,
    )

    assert result == {"a": 2, "b": 4}


# --------------------------------------------------
# dict_by()
# --------------------------------------------------

def test_dict_by_callable():
    items = [{"id": 1}, {"id": 2}]

    result = dicts.dict_by(
        items,
        key=lambda item: item["id"],
    )

    assert result == {
        1: {"id": 1},
        2: {"id": 2},
    }


def test_dict_by_empty():
    result = dicts.dict_by([], key=lambda x: x)

    assert result == {}
