# -*- coding: utf-8 -*-
import random

import webapp2
from webapp2 import BaseRoute, RedirectHandler, Request, Route, Router

import test_base


class TestRoute(test_base.BaseTestCase):
    def test_no_variable(self):
        route = Route(r'/hello', None)
        route, args, kwargs = route.match(Request.blank('/hello'))
        self.assertEqual(kwargs, {})
        url = route.build(Request.blank('/'), (), {})
        self.assertEqual(url, '/hello')

        route = Route(r'/hello/world/', None)
        route, args, kwargs = route.match(Request.blank('/hello/world/'))
        self.assertEqual(kwargs, {})
        url = route.build(Request.blank('/'), (), {})
        self.assertEqual(url, '/hello/world/')

    def test_repetition_operator(self):
        route = Route(r'/<:\d>', None)
        self.assertEqual(route.match(Request.blank('/1')), (route, ('1',), {}))
        self.assertEqual(route.match(Request.blank('/2')), (route, ('2',), {}))

        route = Route(r'/<:\d{2,3}>', None)
        self.assertEqual(route.match(Request.blank('/11')), (route, ('11',), {}))
        self.assertEqual(route.match(Request.blank('/111')), (route, ('111',), {}))
        self.assertEqual(route.match(Request.blank('/1111')), None)

    def test_unnamed_variable(self):
        route = Route(r'/<:\d{4}>', None)
        self.assertEqual(route.match(Request.blank('/2010')), (route, ('2010',), {}))
        self.assertEqual(route.match(Request.blank('/aaaa')), None)

        route = Route(r'/<:\d{2}>.<:\d{2}>', None)
        self.assertEqual(route.match(Request.blank('/98.99')), (route, ('98', '99'), {}))
        self.assertEqual(route.match(Request.blank('/aa.aa')), None)

        route = Route(r'/<:\d{2}>.<:\d{2}>/<foo>', None)
        self.assertEqual(route.match(Request.blank('/98.99/test')), (route, ('98', '99'), {'foo': 'test'}))
        self.assertEqual(route.match(Request.blank('/aa.aa/test')), None)

    def test_simple_variable(self):
        route = Route(r'/<foo>', None)
        self.assertEqual(route.match(Request.blank('/bar')), (route, (), {'foo': 'bar'}))
        url = route.build(Request.blank('/'), (), dict(foo='baz'))
        self.assertEqual(url, '/baz')

    def test_expr_variable(self):
        route = Route(r'/<year:\d{4}>', None)
        self.assertEqual(route.match(Request.blank('/bar')), None)
        self.assertEqual(route.match(Request.blank('/2010')), (route, (), {'year': '2010'}))
        self.assertEqual(route.match(Request.blank('/1900')), (route, (), {'year': '1900'}))
        url = route.build(Request.blank('/'), (), dict(year='2010'))
        self.assertEqual(url, '/2010')

    def test_expr_variable2(self):
        route = Route(r'/<year:\d{4}>/foo/', None)
        url = route.build(Request.blank('/'), (), dict(year='2010'))
        self.assertEqual(url, '/2010/foo/')

    def test_build_missing_argument(self):
        route = Route(r'/<:\d{4}>', None)
        self.assertRaises(KeyError, route.build, Request.blank('/'), (), {})

        route = Route(r'/<:\d{4}>/<:\d{2}>', None)
        self.assertRaises(KeyError, route.build, Request.blank('/'), (2010,), {})

    def test_build_invalid_argument(self):
        route = Route(r'/<:\d{4}>', None)
        self.assertRaises(ValueError, route.build, Request.blank('/'), ('20100',), {})

    def test_build_invalid_argument2(self):
        route = Route(r'/<:\d{4}>', None)
        self.assertRaises(ValueError, route.build, Request.blank('/'), ('201a',), {})

    def test_build_missing_keyword(self):
        route = Route(r'/<year:\d{4}>', None)
        self.assertRaises(KeyError, route.build, Request.blank('/'), (), {})

    def test_build_missing_keyword2(self):
        route = Route(r'/<year:\d{4}>/<month:\d{2}>', None)
        self.assertRaises(KeyError, route.build, Request.blank('/'), (), dict(year='2010'))

    def test_build_invalid_keyword(self):
        route = Route(r'/<year:\d{4}>', None)
        self.assertRaises(ValueError, route.build, Request.blank('/'), (), dict(year='20100'))

    def test_build_invalid_keyword2(self):
        route = Route(r'/<year:\d{4}>', None)
        self.assertRaises(ValueError, route.build, Request.blank('/'), (), dict(year='201a'))

    def test_build_with_unnamed_variable(self):
        route = Route(r'/<:\d{4}>/<month:\d{2}>', None)

        url = route.build(Request.blank('/'), (2010,), dict(month=10))
        self.assertEqual(url, '/2010/10')

        url = route.build(Request.blank('/'), ('1999',), dict(month='07'))
        self.assertEqual(url, '/1999/07')

    def test_build_default_keyword(self):
        route = Route(r'/<year:\d{4}>/<month:\d{2}>', None,
            defaults={'month': 10})
        url = route.build(Request.blank('/'), (), dict(year='2010'))
        self.assertEqual(url, '/2010/10')

        route = Route(r'/<year:\d{4}>/<month:\d{2}>', None,
            defaults={'year': 1900})
        url = route.build(Request.blank('/'), (), dict(month='07'))
        self.assertEqual(url, '/1900/07')

    def test_build_extra_keyword(self):
        route = Route(r'/<year:\d{4}>', None)
        url = route.build(Request.blank('/'), (), dict(year='2010', foo='bar'))
        self.assertEqual(url, '/2010?foo=bar')
        # Arguments are sorted.
        url = route.build(Request.blank('/'), (), dict(year='2010', foo='bar', baz='ding'))
        self.assertEqual(url, '/2010?baz=ding&foo=bar')

    def test_build_extra_positional_keyword(self):
        route = Route(r'/<year:\d{4}>/<:\d{2}>', None)

        url = route.build(Request.blank('/'), ('08', 'i-should-be-ignored', 'me-too'), dict(year='2010', foo='bar'))
        self.assertEqual(url, '/2010/08?foo=bar')

        url = route.build(Request.blank('/'), ('08', 'i-should-be-ignored', 'me-too'), dict(year='2010', foo='bar', baz='ding'))
        self.assertEqual(url, '/2010/08?baz=ding&foo=bar')

    def test_build_int_keyword(self):
        route = Route(r'/<year:\d{4}>', None)
        url = route.build(Request.blank('/'), (), dict(year=2010))
        self.assertEqual(url, '/2010')

    def test_build_int_variable(self):
        route = Route(r'/<:\d{4}>', None)
        url = route.build(Request.blank('/'), (2010,), {})
        self.assertEqual(url, '/2010')

    def test_router_build_error(self):
        router = Router(None)
        router.add(Route('/<year:\d{4}>', None, name='year-page'))

        url = router.build(Request.blank('/'), 'year-page', (), dict(year='2010'))
        self.assertEqual(url, '/2010')

        self.assertRaises(KeyError, router.build, Request.blank('/'), 'i-dont-exist', (), dict(year='2010'))

    def test_reverse_template(self):
        route = Route('/foo', None)
        # Access route.regex just to set the lazy properties.
        regex = route.regex
        self.assertEqual(route.reverse_template, '/foo')

        route = Route('/foo/<bar>', None)
        # Access route.regex just to set the lazy properties.
        regex = route.regex
        self.assertEqual(route.reverse_template, '/foo/%(bar)s')

        route = Route('/foo/<bar>/<baz:\d>', None)
        # Access route.regex just to set the lazy properties.
        regex = route.regex
        self.assertEqual(route.reverse_template, '/foo/%(bar)s/%(baz)s')

    def test_invalid_template(self):
        # To break it:
        # <>foo:><bar<:baz>
        route = Route('/<foo/<:bar', None)
        # Access route.regex just to set the lazy properties.
        regex = route.regex
        self.assertEqual(route.reverse_template, '/<foo/<:bar')

    def test_build_full_without_request(self):
        router = Router(None)
        router.add(Route(r'/hello', None, name='hello'))
        self.assertRaises(AttributeError, router.build, None, 'hello', (), dict(_full=True))
        self.assertRaises(AttributeError, router.build, None, 'hello', (), dict(_scheme='https'))

    def test_positions(self):
        template = '/<:\d+>' * 98
        args = tuple(str(i) for i in range(98))
        url_res = '/' + '/'.join(args)

        route = Route(template, None)
        self.assertEqual(route.match(Request.blank(url_res)), (route, args, {}))
        url = route.build(Request.blank('/'), args, {})
        self.assertEqual(url_res, url)

        args = [str(i) for i in range(1000)]
        random.shuffle(args)
        args = tuple(args[:98])
        url_res = '/' + '/'.join(args)
        self.assertEqual(route.match(Request.blank(url_res)), (route, args, {}))
        url = route.build(Request.blank('/'), args, {})
        self.assertEqual(url_res, url)

    def test_build_only_without_name(self):
        self.assertRaises(ValueError, Route, r'/<foo>', None, build_only=True)

    def test_route_repr(self):
        self.assertEqual(Route(r'/<foo>', None).__repr__(),
            "<Route('/<foo>', None, name=None, defaults={}, build_only=False)>")
        self.assertEqual(Route(r'/<foo>', None, name='bar', defaults={'baz': 'ding'}, build_only=True).__repr__(),
            "<Route('/<foo>', None, name='bar', defaults={'baz': 'ding'}, build_only=True)>")

        self.assertEqual(str(Route(r'/<foo>', None)),
            "<Route('/<foo>', None, name=None, defaults={}, build_only=False)>")
        self.assertEqual(str(Route(r'/<foo>', None, name='bar', defaults={'baz': 'ding'}, build_only=True)),
            "<Route('/<foo>', None, name='bar', defaults={'baz': 'ding'}, build_only=True)>")

    def test_router_repr(self):
        router = Router(None)
        router.add(Route(r'/hello', None, name='hello', build_only=True))
        router.add(Route(r'/world', None))

        self.assertEqual(router.__repr__(), "<Router([<Route('/world', None, name=None, defaults={}, build_only=False)>, <Route('/hello', None, name='hello', defaults={}, build_only=True)>])>")

    def test_base_route(self):
        route = BaseRoute('foo', 'bar')
        self.assertRaises(NotImplementedError, route.match, None)

    def test_set_matcher(self):
        req = Request.blank('/')
        def custom_matcher(router, request):
            self.assertEqual(request, req)

        router = Router(None)
        router.set_matcher(custom_matcher)
        router.match(req)

    def test_set_builder(self):
        req = Request.blank('/')
        def custom_builder(router, request, name, args, kwargs):
            self.assertEqual(request, req)
            return 'http://www.google.com'

        router = Router(None)
        router.set_builder(custom_builder)
        res = router.build(req, '', (), {})
        self.assertEqual(res, 'http://www.google.com')

    def test_set_adapter(self):
        def custom_adapter(router, handler):
            class MyAdapter(webapp2.BaseHandlerAdapter):
                def __call__(self, request, response):
                    response.write('hello my adapter')

            return MyAdapter(handler)

        def myhandler(request, *args, **kwargs):
            return webapp2.Response('hello')

        app = webapp2.WSGIApplication([('/', myhandler)])
        app.router.set_adapter(custom_adapter)

        rsp = app.get_response('/')
        self.assertEqual(rsp.status_int, 200)
        self.assertEqual(rsp.body, 'hello my adapter')

    def test_methods(self):
        route = Route(r'/', methods=['GET', 'POST'])
        router = Router([route])
        req = Request.blank('/')
        req.method = 'GET'
        self.assertTrue(router.match(req) is not None)
        req.method = 'POST'
        self.assertTrue(router.match(req) is not None)
        req.method = 'PUT'
        self.assertRaises(webapp2.exc.HTTPMethodNotAllowed, router.match, req)

    def test_schemes(self):
        route = Route(r'/', schemes=['http'])
        req = Request.blank('http://mydomain.com/')
        self.assertTrue(route.match(req) is not None)
        req = Request.blank('https://mydomain.com/')
        self.assertTrue(route.match(req) is None)

        route = Route(r'/', schemes=['https'])
        req = Request.blank('https://mydomain.com/')
        self.assertTrue(route.match(req) is not None)
        req = Request.blank('http://mydomain.com/')
        self.assertTrue(route.match(req) is None)


class TestSimpleRoute(test_base.BaseTestCase):
    def test_no_variable(self):
        router = webapp2.Router([(r'/', 'my_handler')])

        matched_route, args, kwargs = router.match(webapp2.Request.blank('/'))
        self.assertEqual(args, ())
        self.assertEqual(kwargs, {})

    def test_simple_variables(self):
        router = webapp2.Router([(r'/(\d{4})/(\d{2})', 'my_handler')])

        matched_route, args, kwargs = router.match(webapp2.Request.blank('/2007/10'))
        self.assertEqual(args, ('2007', '10'))
        self.assertEqual(kwargs, {})

    def test_build(self):
        route = webapp2.SimpleRoute('/', None)
        self.assertRaises(NotImplementedError, route.build, None, None, None)

    def test_route_repr(self):
        self.assertEqual(webapp2.SimpleRoute(r'/<foo>', None).__repr__(), "<SimpleRoute('/<foo>', None)>")
        self.assertEqual(str(webapp2.SimpleRoute(r'/<foo>', None)), "<SimpleRoute('/<foo>', None)>")


if __name__ == '__main__':
    test_base.main()