# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
#      https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.

import re

from requests_mock import adapter
from . import base

ANY = adapter.ANY


class TestMatcher(base.TestCase):

    def match(self,
              target,
              url,
              matcher_method='GET',
              request_method='GET',
              complete_qs=False,
              headers=None,
              request_data=None,
              request_headers={},
              additional_matcher=None,
              real_http=False,
              case_sensitive=False):
        matcher = adapter._Matcher(matcher_method,
                                   target,
                                   [],
                                   complete_qs=complete_qs,
                                   additional_matcher=additional_matcher,
                                   request_headers=request_headers,
                                   real_http=real_http,
                                   case_sensitive=case_sensitive)
        request = adapter._RequestObjectProxy._create(request_method,
                                                      url,
                                                      headers,
                                                      data=request_data)
        return matcher._match(request)

    def assertMatch(self,
                    target=ANY,
                    url='http://example.com/requests-mock',
                    matcher_method='GET',
                    request_method='GET',
                    **kwargs):
        self.assertEqual(True,
                         self.match(target,
                                    url,
                                    matcher_method=matcher_method,
                                    request_method=request_method,
                                    **kwargs),
                         'Matcher %s %s failed to match %s %s' %
                         (matcher_method, target, request_method, url))

    def assertMatchBoth(self,
                        target=ANY,
                        url='http://example.com/requests-mock',
                        matcher_method='GET',
                        request_method='GET',
                        **kwargs):
        self.assertMatch(target,
                         url,
                         matcher_method=matcher_method,
                         request_method=request_method,
                         **kwargs)
        self.assertMatch(url,
                         target,
                         matcher_method=request_method,
                         request_method=matcher_method,
                         **kwargs)

    def assertNoMatch(self,
                      target=ANY,
                      url='http://example.com/requests-mock',
                      matcher_method='GET',
                      request_method='GET',
                      **kwargs):
        self.assertEqual(False,
                         self.match(target,
                                    url,
                                    matcher_method=matcher_method,
                                    request_method=request_method,
                                    **kwargs),
                         'Matcher %s %s unexpectedly matched %s %s' %
                         (matcher_method, target, request_method, url))

    def assertNoMatchBoth(self,
                          target=ANY,
                          url='http://example.com/requests-mock',
                          matcher_method='GET',
                          request_method='GET',
                          **kwargs):
        self.assertNoMatch(target,
                           url,
                           matcher_method=matcher_method,
                           request_method=request_method,
                           **kwargs)
        self.assertNoMatch(url,
                           target,
                           matcher_method=request_method,
                           request_method=matcher_method,
                           **kwargs)

    def assertMatchMethodBoth(self, matcher_method, request_method, **kwargs):
        url = 'http://www.test.com'

        self.assertMatchBoth(url,
                             url,
                             request_method=request_method,
                             matcher_method=matcher_method,
                             **kwargs)

    def assertNoMatchMethodBoth(self,
                                matcher_method,
                                request_method,
                                **kwargs):
        url = 'http://www.test.com'

        self.assertNoMatchBoth(url,
                               url,
                               request_method=request_method,
                               matcher_method=matcher_method,
                               **kwargs)

    def test_url_matching(self):
        self.assertMatchBoth('http://www.test.com',
                             'http://www.test.com')
        self.assertMatchBoth('http://www.test.com',
                             'http://www.test.com/')
        self.assertMatchBoth('http://www.test.com/abc',
                             'http://www.test.com/abc')
        self.assertMatchBoth('http://www.test.com:5000/abc',
                             'http://www.test.com:5000/abc')

        self.assertNoMatchBoth('https://www.test.com',
                               'http://www.test.com')
        self.assertNoMatchBoth('http://www.test.com/abc',
                               'http://www.test.com')
        self.assertNoMatchBoth('http://test.com',
                               'http://www.test.com')
        self.assertNoMatchBoth('http://test.com',
                               'http://www.test.com')
        self.assertNoMatchBoth('http://test.com/abc',
                               'http://www.test.com/abc/')
        self.assertNoMatchBoth('http://test.com/abc/',
                               'http://www.test.com/abc')
        self.assertNoMatchBoth('http://test.com:5000/abc/',
                               'http://www.test.com/abc')
        self.assertNoMatchBoth('http://test.com/abc/',
                               'http://www.test.com:5000/abc')

    def test_subset_match(self):
        self.assertMatch('/path', 'http://www.test.com/path')
        self.assertMatch('/path', 'http://www.test.com/path')
        self.assertMatch('//www.test.com/path', 'http://www.test.com/path')
        self.assertMatch('//www.test.com/path', 'https://www.test.com/path')

    def test_query_string(self):
        self.assertMatch('/path?a=1&b=2',
                         'http://www.test.com/path?a=1&b=2')
        self.assertMatch('/path?a=1',
                         'http://www.test.com/path?a=1&b=2',
                         complete_qs=False)
        self.assertNoMatch('/path?a=1',
                           'http://www.test.com/path?a=1&b=2',
                           complete_qs=True)
        self.assertNoMatch('/path?a=1&b=2',
                           'http://www.test.com/path?a=1')

    def test_query_empty_string(self):
        self.assertMatch('/path?a',
                         'http://www.test.com/path?a')
        self.assertMatch('/path?bob&paul',
                         'http://www.test.com/path?paul&bob')
        self.assertNoMatch('/path?bob',
                           'http://www.test.com/path?paul')
        self.assertNoMatch('/path?pual&bob',
                           'http://www.test.com/path?bob')

    def test_method_match(self):
        self.assertNoMatchMethodBoth('GET', 'POST')
        self.assertMatchMethodBoth('GET', 'get')
        self.assertMatchMethodBoth('GeT', 'geT')

    def test_match_ANY_url(self):
        self.assertMatch(ANY, 'http://anything')
        self.assertMatch(ANY, 'http://somethingelse')
        self.assertNoMatch(ANY, 'http://somethingelse', request_method='POST')

    def test_match_ANY_method(self):
        for m in ('GET', 'POST', 'HEAD', 'OPTION'):
            self.assertMatch('http://www.test.com',
                             'http://www.test.com',
                             matcher_method=ANY,
                             request_method=m)

        self.assertNoMatch('http://www.test.com',
                           'http://another',
                           matcher_method=ANY)

    def test_match_with_regex(self):
        r1 = re.compile('test.com/a')
        r2 = re.compile('/b/c')

        self.assertMatch(r1, 'http://mock.test.com/a/b')
        self.assertMatch(r1, 'http://test.com/a/')
        self.assertMatch(r1, 'mock://test.com/a/b')
        self.assertNoMatch(r1, 'mock://test.com/')

        self.assertMatch(r2, 'http://anything/a/b/c/d')
        self.assertMatch(r2, 'mock://anything/a/b/c/d')

    def test_match_with_headers(self):
        self.assertMatch('/path',
                         'http://www.test.com/path',
                         headers={'A': 'abc', 'b': 'def'},
                         request_headers={'a': 'abc'})

        self.assertMatch('/path',
                         'http://www.test.com/path',
                         headers={'A': 'abc', 'b': 'def'})

        self.assertNoMatch('/path',
                           'http://www.test.com/path',
                           headers={'A': 'abc', 'b': 'def'},
                           request_headers={'b': 'abc'})

        self.assertNoMatch('/path',
                           'http://www.test.com/path',
                           headers={'A': 'abc', 'b': 'def'},
                           request_headers={'c': 'ghi'})

        # headers should be key insensitive and value sensitive, we have no
        # choice here because they go into an insensitive dict.
        self.assertMatch('/path',
                         'http://www.test.com/path',
                         headers={'aBc': 'abc', 'DEF': 'def'},
                         request_headers={'abC': 'abc'})

        self.assertNoMatch('/path',
                           'http://www.test.com/path',
                           headers={'abc': 'aBC', 'DEF': 'def'},
                           request_headers={'abc': 'Abc'})

    def test_case_sensitive_ignored_for_netloc_and_protocol(self):
        for case_sensitive in (True, False):
            self.assertMatch('http://AbC.CoM',
                             'http://aBc.CoM',
                             case_sensitive=case_sensitive)

            self.assertMatch('htTP://abc.com',
                             'hTTp://abc.com',
                             case_sensitive=case_sensitive)

            self.assertMatch('htTP://aBC.cOm',
                             'hTTp://AbC.Com',
                             case_sensitive=case_sensitive)

    def assertSensitiveMatch(self, target, url, **kwargs):
        self.assertMatch(target, url, case_sensitive=False, **kwargs)
        self.assertNoMatch(target, url, case_sensitive=True, **kwargs)

    def test_case_sensitive_paths(self):
        self.assertSensitiveMatch('http://abc.com/pAtH', 'http://abc.com/path')
        self.assertSensitiveMatch('/pAtH', 'http://abc.com/path')

    def test_case_sensitive_query(self):
        self.assertSensitiveMatch('http://abc.com/path?abCD=efGH',
                                  'http://abc.com/path?abCd=eFGH')

        self.assertSensitiveMatch('http://abc.com/path?abcd=efGH',
                                  'http://abc.com/path?abcd=eFGH')

    def test_additional_matcher(self):

        def test_match_body(request):
            return 'hello' in request.text

        self.assertMatch(request_method='POST',
                         matcher_method='POST',
                         request_data='hello world',
                         additional_matcher=test_match_body)

        self.assertNoMatch(request_method='POST',
                           matcher_method='POST',
                           request_data='goodbye world',
                           additional_matcher=test_match_body)
