# -*- coding: utf-8 -*-
"""
    tests.local
    ~~~~~~~~~~~~~~~~~~~~~~~~

    Local and local proxy tests.

    :copyright: 2007 Pallets
    :license: BSD-3-Clause
"""
import copy
import time
from functools import partial
from threading import Thread

import pytest

from werkzeug import local


def test_basic_local():
    ns = local.Local()
    ns.foo = 0
    values = []

    def value_setter(idx):
        time.sleep(0.01 * idx)
        ns.foo = idx
        time.sleep(0.02)
        values.append(ns.foo)

    threads = [Thread(target=value_setter, args=(x,)) for x in [1, 2, 3]]
    for thread in threads:
        thread.start()
    time.sleep(0.2)
    assert sorted(values) == [1, 2, 3]

    def delfoo():
        del ns.foo

    delfoo()
    pytest.raises(AttributeError, lambda: ns.foo)
    pytest.raises(AttributeError, delfoo)

    local.release_local(ns)


def test_local_release():
    ns = local.Local()
    ns.foo = 42
    local.release_local(ns)
    assert not hasattr(ns, "foo")

    ls = local.LocalStack()
    ls.push(42)
    local.release_local(ls)
    assert ls.top is None


def test_local_proxy():
    foo = []
    ls = local.LocalProxy(lambda: foo)
    ls.append(42)
    ls.append(23)
    ls[1:] = [1, 2, 3]
    assert foo == [42, 1, 2, 3]
    assert repr(foo) == repr(ls)
    assert foo[0] == 42
    foo += [1]
    assert list(foo) == [42, 1, 2, 3, 1]


def test_local_proxy_operations_math():
    foo = 2
    ls = local.LocalProxy(lambda: foo)
    assert ls + 1 == 3
    assert 1 + ls == 3
    assert ls - 1 == 1
    assert 1 - ls == -1
    assert ls * 1 == 2
    assert 1 * ls == 2
    assert ls / 1 == 2
    assert 1.0 / ls == 0.5
    assert ls // 1.0 == 2.0
    assert 1.0 // ls == 0.0
    assert ls % 2 == 0
    assert 2 % ls == 0


def test_local_proxy_operations_strings():
    foo = "foo"
    ls = local.LocalProxy(lambda: foo)
    assert ls + "bar" == "foobar"
    assert "bar" + ls == "barfoo"
    assert ls * 2 == "foofoo"

    foo = "foo %s"
    assert ls % ("bar",) == "foo bar"


def test_local_stack():
    ident = local.get_ident()

    ls = local.LocalStack()
    assert ident not in ls._local.__storage__
    assert ls.top is None
    ls.push(42)
    assert ident in ls._local.__storage__
    assert ls.top == 42
    ls.push(23)
    assert ls.top == 23
    ls.pop()
    assert ls.top == 42
    ls.pop()
    assert ls.top is None
    assert ls.pop() is None
    assert ls.pop() is None

    proxy = ls()
    ls.push([1, 2])
    assert proxy == [1, 2]
    ls.push((1, 2))
    assert proxy == (1, 2)
    ls.pop()
    ls.pop()
    assert repr(proxy) == "<LocalProxy unbound>"

    assert ident not in ls._local.__storage__


def test_local_proxies_with_callables():
    foo = 42
    ls = local.LocalProxy(lambda: foo)
    assert ls == 42
    foo = [23]
    ls.append(42)
    assert ls == [23, 42]
    assert foo == [23, 42]


def test_custom_idents():
    ident = 0
    ns = local.Local()
    stack = local.LocalStack()
    local.LocalManager([ns, stack], ident_func=lambda: ident)

    ns.foo = 42
    stack.push({"foo": 42})
    ident = 1
    ns.foo = 23
    stack.push({"foo": 23})
    ident = 0
    assert ns.foo == 42
    assert stack.top["foo"] == 42
    stack.pop()
    assert stack.top is None
    ident = 1
    assert ns.foo == 23
    assert stack.top["foo"] == 23
    stack.pop()
    assert stack.top is None


def test_deepcopy_on_proxy():
    class Foo(object):
        attr = 42

        def __copy__(self):
            return self

        def __deepcopy__(self, memo):
            return self

    f = Foo()
    p = local.LocalProxy(lambda: f)
    assert p.attr == 42
    assert copy.deepcopy(p) is f
    assert copy.copy(p) is f

    a = []
    p2 = local.LocalProxy(lambda: [a])
    assert copy.copy(p2) == [a]
    assert copy.copy(p2)[0] is a

    assert copy.deepcopy(p2) == [a]
    assert copy.deepcopy(p2)[0] is not a


def test_local_proxy_wrapped_attribute():
    class SomeClassWithWrapped(object):
        __wrapped__ = "wrapped"

    def lookup_func():
        return 42

    partial_lookup_func = partial(lookup_func)

    proxy = local.LocalProxy(lookup_func)
    assert proxy.__wrapped__ is lookup_func

    partial_proxy = local.LocalProxy(partial_lookup_func)
    assert partial_proxy.__wrapped__ == partial_lookup_func

    ns = local.Local()
    ns.foo = SomeClassWithWrapped()
    ns.bar = 42

    assert ns("foo").__wrapped__ == "wrapped"
    pytest.raises(AttributeError, lambda: ns("bar").__wrapped__)
