# Copyright 2019 Ram Rachum and collaborators.
# This program is distributed under the MIT license.

import io
import re
import abc

from python_toolbox import caching
from python_toolbox import sys_tools
from python_toolbox import temp_file_tools

import pysnooper

from .utils import (assert_output, VariableEntry, CallEntry, LineEntry,
                    ReturnEntry, OpcodeEntry, ExceptionEntry)


def test_string_io():
    string_io = io.StringIO()
    @pysnooper.snoop(string_io)
    def my_function(foo):
        x = 7
        y = 8
        return y + x
    result = my_function('baba')
    assert result == 15
    output = string_io.getvalue()
    assert_output(
        output,
        (
            VariableEntry('foo', value_regex="u?'baba'"),
            CallEntry('def my_function(foo):'),
            LineEntry('x = 7'),
            VariableEntry('x', '7'),
            LineEntry('y = 8'),
            VariableEntry('y', '8'),
            LineEntry('return y + x'),
            ReturnEntry('return y + x'),
        )
    )

def test_variables():

    class Foo(object):
        def __init__(self):
            self.x = 2

        def square(self):
            self.x **= 2

    @pysnooper.snoop(variables=('foo.x', 're'))
    def my_function():
        foo = Foo()
        for i in range(2):
            foo.square()

    with sys_tools.OutputCapturer(stdout=False,
                                  stderr=True) as output_capturer:
        result = my_function()
    assert result is None
    output = output_capturer.string_io.getvalue()
    assert_output(
        output,
        (
            VariableEntry(),
            VariableEntry(),
            CallEntry('def my_function():'),
            LineEntry('foo = Foo()'),
            VariableEntry(),
            VariableEntry(),
            LineEntry(),
            VariableEntry('i', '0'),
            LineEntry(),
            VariableEntry('foo.x', '4'),
            LineEntry(),
            VariableEntry('i', '1'),
            LineEntry(),
            VariableEntry('foo.x', '16'),
            LineEntry(),
            ReturnEntry(),
        )
    )

def test_depth():
    string_io = io.StringIO()

    def f4(x4):
        result4 = x4 * 2
        return result4

    def f3(x3):
        result3 = f4(x3)
        return result3

    def f2(x2):
        result2 = f3(x2)
        return result2

    @pysnooper.snoop(string_io, depth=3)
    def f1(x1):
        result1 = f2(x1)
        return result1

    result = f1(10)
    assert result == 20
    output = string_io.getvalue()
    assert_output(
        output,
        (
            VariableEntry(),
            VariableEntry(),
            CallEntry('def f1(x1):'),
            LineEntry(),

            VariableEntry(),
            VariableEntry(),
            CallEntry('def f2(x2):'),
            LineEntry(),

            VariableEntry(),
            VariableEntry(),
            CallEntry('def f3(x3):'),
            LineEntry(),

            VariableEntry(),
            LineEntry(),
            ReturnEntry(),

            VariableEntry(),
            LineEntry(),
            ReturnEntry(),

            VariableEntry(),
            LineEntry(),
            ReturnEntry(),
        )
    )


def test_method_and_prefix():

    class Baz(object):
        def __init__(self):
            self.x = 2

        @pysnooper.snoop(variables=('self.x'), prefix='ZZZ')
        def square(self):
            foo = 7
            self.x **= 2
            return self

    baz = Baz()

    with sys_tools.OutputCapturer(stdout=False,
                                  stderr=True) as output_capturer:
        result = baz.square()
    assert result is baz
    assert result.x == 4
    output = output_capturer.string_io.getvalue()
    assert_output(
        output,
        (
            VariableEntry(),
            CallEntry('def square(self):'),
            LineEntry('foo = 7'),
            VariableEntry('foo', '7'),
            LineEntry('self.x **= 2'),
            LineEntry(),
            ReturnEntry(),
        ),
        prefix='ZZZ'
    )

def test_file_output():

    with temp_file_tools.create_temp_folder(prefix='pysnooper') as folder:
        path = folder / 'foo.log'
        @pysnooper.snoop(str(path))
        def my_function(foo):
            x = 7
            y = 8
            return y + x
        result = my_function('baba')
        assert result == 15
        output = path.open().read()
        assert_output(
            output,
            (
                VariableEntry('foo', value_regex="u?'baba'"),
                CallEntry('def my_function(foo):'),
                LineEntry('x = 7'),
                VariableEntry('x', '7'),
                LineEntry('y = 8'),
                VariableEntry('y', '8'),
                LineEntry('return y + x'),
                ReturnEntry('return y + x'),
            )
        )


@@ -4862,12 +4862,880 @@
 %0A        )%0A%0A
+def test_function_name_finding():%0A    string_io = io.StringIO()%0A    @pysnooper.snoop(string_io,%0A    depth = 2%0A    # Multiline decorator%0A    )%0A    def my_function(foo):%0A        x = lambda bar: 7%0A        y = 8%0A        return y + x(foo)%0A    result = my_function('baba')%0A    assert result == 15%0A    output = string_io.getvalue()%0A    assert_output(%0A        output,%0A        (%0A            VariableEntry('foo', value_regex=%22u?'baba'%22),%0A            CallEntry('def my_function(foo):'),%0A            LineEntry(),%0A            VariableEntry(),%0A            LineEntry(),%0A            VariableEntry(),%0A            LineEntry(),%0A            # inside lambda%0A            VariableEntry('bar', value_regex=%22u?'baba'%22),%0A            CallEntry('x = lambda bar: 7'),%0A            LineEntry(),%0A            ReturnEntry(),%0A            # back in my_function%0A            ReturnEntry(),%0A        )%0A    )%0A

Add test-case for multiline decorator and lambda