from collections import namedtuple

from venom import Empty
from venom import Message
from venom.converter import Converter
from venom.fields import Int32, String
from venom.rpc import Service, rpc
from venom.rpc.method import HTTPVerb, MethodDescriptor
from venom.rpc.stub import Stub
from venom.rpc.test_utils import AioTestCase


class MethodTestCase(AioTestCase):
    async def test_method_override(self):
        Snake = namedtuple('Snake', ('name', 'size'))

        class SnakeMessage(Message):
            name = String()
            size = Int32()

        class SnakeConverter(Converter):
            wire = SnakeMessage
            python = Snake

            def resolve(self, message: SnakeMessage) -> Snake:
                return Snake(message.name, message.size)

            def format(self, value: Snake) -> SnakeMessage:
                return SnakeMessage(name=value.name, size=value.size)

        class SnakeStub(Stub):
            @rpc(SnakeMessage, SnakeMessage)
            def grow(self): pass

        self.assertEqual(set(SnakeStub.__methods__.keys()), {"grow"})
        self.assertEqual(SnakeStub.__methods__['grow'].request, SnakeMessage)
        self.assertEqual(SnakeStub.__methods__['grow'].response, SnakeMessage)

        # TODO test without stub (auto-generated request message)
        class SnakeService(Service):
            class Meta:
                converters = [SnakeConverter()]
                stub = SnakeStub

            @rpc
            def grow(self, request: Snake) -> Snake:
                return Snake(name=request.name, size=request.size + 1)

        self.assertEqual(await SnakeService().grow(SnakeMessage('snek', 2)), SnakeMessage('snek', 3))
        self.assertEqual(await SnakeService.grow.invoke(SnakeService(), SnakeMessage(name='snek', size=2)),
                         SnakeMessage(name='snek', size=3))
        self.assertEqual(await SnakeService.grow.invoke(SnakeService(), SnakeMessage(name='snek')),
                         SnakeMessage(name='snek', size=1))

    def test_method_http(self):
        class FooService(Service):
            pass

        self.assertEqual(MethodDescriptor(Empty, Empty).prepare(FooService(), 'bar').http_path, '/foo/bar')
        self.assertEqual(MethodDescriptor(Empty, Empty).prepare(FooService(), 'foo').http_method, HTTPVerb.POST)
        self.assertEqual(MethodDescriptor(Empty, Empty,
                                          http_path='./bar').prepare(FooService(), 'foo').http_path, '/foo/bar')

        self.assertEqual(MethodDescriptor(Empty, Empty, http_method=HTTPVerb.POST).http_method, HTTPVerb.POST)
        self.assertEqual(MethodDescriptor(Empty, Empty, http_method=HTTPVerb.DELETE).http_method, HTTPVerb.DELETE)

    def test_method_http_rule_params(self):
        class Snake(Message):
            id = Int32()
            name = String()
            size = Int32()

        class FooService(Service):
            pass

        self.assertEqual(MethodDescriptor(Empty, Empty)
                         .prepare(FooService(), 'foo')
                         .http_path_parameters(), set())
        self.assertEqual(MethodDescriptor(Snake, Snake, http_path='./{id}')
                         .prepare(FooService(), 'foo')
                         .http_path_parameters(), {'id'})
        self.assertEqual(MethodDescriptor(Snake, Snake, http_path='./{name}/{id}')
                         .prepare(FooService(), 'foo')
                         .http_path_parameters(), {'id', 'name'})

@@ -27,16 +27,46 @@
 medtuple
+%0Afrom unittest import SkipTest
 %0A%0Afrom v
@@ -109,16 +109,99 @@
 Message%0A
+from venom.common import Value, BoolValue%0Afrom venom.common.types import JSONValue%0A
 from ven
@@ -3605,12 +3605,344 @@
 ', 'name'%7D)%0A
+%0A    @SkipTest%0A    async def test_json_method(self):%0A        class FooService(Service):%0A            @rpc%0A            def get_json(self) -%3E JSONValue:%0A                return %7B%22foo%22: True%7D%0A%0A        self.assertEqual(await FooService.get_json.invoke(FooService(), Empty()),%0A                         Value(bool_value=BoolValue(True)))%0A%0A%0A

add JSON output test