# -*- encoding: utf-8 -*-
from __future__ import print_function

try:  # python 2
    from httplib import HTTPConnection
    from urllib import urlencode
except ImportError:
    from http.client import HTTPConnection
    from urllib.parse import urlencode

import unittest
import json
# imports below are for signing use
from base64 import b64encode
from hashlib import sha1
import hmac
import time
from pprint import pprint


class Request(object):
    '''http request

    Use canonical url's.
    You can specify a ip url and a Host header in headers,
    such that you don't have to modify hosts file to test a service.

    e.g.:
    >>> q = Request('http://127.0.0.1:8080/search', {'q': 'apple'}, {'Host': 'www.google.com'})
    >>> response = q.get()
    >>> print(response.read(), response.getheaders())
    '''

    def __init__(self, url, data=None, headers=None):
        self.url = url
        self.data = data and urlencode(data)
        self.headers = headers or {}
        self.host, self.port, self.path = self.parse_url()

    def parse_url(self):
        proto = 'http://'
        if not self.url.startswith(proto):
            raise ValueError('invalid url: %s' % self.url)
        url = self.url[len(proto):]
        try:
            pos = url.index('/')
            host = url[:pos]
            path = url[pos:]
        except ValueError:
            host = url
            path = '/'
        if host.find(':') > 0:
            host, port = host.split(':', 1)
            port = int(port)
        else:
            port = 80
        return host, port, path

    def request(self, verb='GET'):
        conn = HTTPConnection(self.host, self.port)
        if self.port == 80:
            host = self.host
        else:
            host = '%s:%d' % (self.host, self.port)
        self.headers.setdefault('Host', host)
        conn.request(verb, self.path, self.data, self.headers)
        return conn.getresponse()

    def get(self):
        return self.request('GET')

    def post(self):
        return self.request('POST')


class Auth(object):
    '''Use Auth issues authorizated http requests'''

    def __init__(self, id='mtct', key='b592d268ee78ae63a566b800913c1d48'):
        self.id = id
        self.key = key

    def _q(self, url, verb, data=None, headers=None):
        sign = self._sign_header(url, verb)
        headers = headers or {}
        headers.update(sign)
        request = Request(url, data, headers)
        method = getattr(request, verb.lower())
        response = method()
        content = response.read()
        try:
            data = json.loads(content.decode('utf8'))
            return data
        except ValueError:
            return content

    def get(self, url, data=None, headers=None):
        return self._q(url, 'GET', data, headers)

    def post(self, url, data=None, headers=None):
        return self._q(url, 'POST', data, headers)

    def _sign_header(self, url, verb='GET', date=None):
        if date is None:
            date = time.strftime('%a, %d %b %Y %H:%M:%S GMT', time.gmtime())
        elif isinstance(date, time.struct_time):
            date = time.strftime('%a, %d %b %Y %H:%M:%S GMT', date)
        elif not isinstance(date, str):
            raise ValueError("Invalid date")
        schema = 'https://' if url.startswith('https:/') else 'http://'
        try:
            url.index(schema)
            pos = url.find('/', len(schema))
        except ValueError:
            pos = url.find('/')
        path = url[pos:]
        unsigned = '%s %s\n%s' % (verb, path, date)
        h = hmac.new(self.key.encode('utf8'), unsigned.encode('ascii'), sha1)
        signed = b64encode(h.digest())
        auth = 'MWS %s:%s' % (self.id, signed.decode('utf8'))
        return {b'Date': date.encode('utf8'),
                b'Authorization': auth.encode('utf8')}


class OrgunitApiTest(unittest.TestCase):
    def __init__(self,
                 server,
                 auth,
                 url_base=None,
                 headers=None,
                 *args,
                 **kwargs):
        super(OrgunitApiTest, self).__init__(*args, **kwargs)
        self.url_base = url_base
        self.auth = auth
        self.additional_headers = headers
        self.server = server

    def get(self, api):
        return self.auth.get(self.url_base + api,
                             headers=self.additional_headers)

    def post(self, api):
        return self.auth.post(
            self.url_base + api, headers=self.additional_headers)

    def test_id(self):
        '''获得行政单元列表接口 - id

        http://wiki.sankuai.com/pages/viewpage.action?pageId=60459578
        '''
        ok = self.get('orgunit/id/10,20,82')
        self.assertIn('data', ok,
                      '%s: response should contain data' % self.server)

        err = self.get('orgunit/id/9000')
        self.assertIn('error', err,
                      '%s: response should contain error field' % self.server)

    def test_allwithoutspecial(self):
        url = 'orgunit/allwithoutspecial'
        ok = self.get(url)
        self.assertIn('data', ok, '%s: %s' % (self.server, url))

    def test_show(self):
        url = 'orgunit/show'
        ok = self.get(url)
        self.assertIn('data', ok, '%s: %s' % (self.server, url))

    def test_all(self):
        url = 'orgunit/all'
        ok = self.get(url)
        self.assertIn('data', ok, '%s: %s' % (self.server, url))

    def test_provunit(self):
        '''查询省份和行政单元对应关系接口 - provunit

        http://wiki.sankuai.com/pages/viewpage.action?pageId=60459649
        '''
        url = 'orgunit/provunit'
        ok = self.get('orgunit/provunit')
        # with open('provunit.api.log', 'wt') as f:
        #     pprint(ok['data'], f)
        self.assertIn('data', ok, '%s: %s' % (self.server, ok))

    # @unittest.skip('y')
    def test_cityunit(self):
        '''查询城市和行政单元对应关系接口 - cityunit

        http://wiki.sankuai.com/pages/viewpage.action?pageId=60459681
        '''
        url = 'orgunit/cityunit'
        ok = self.get('orgunit/cityunit')
        self.assertIn('data', ok, '%s: %s' % (self.server, url))
        while ok['data']:
            key, val = ok['data'].popitem()
            if (val.get('id', None) is not None and val.get('name')):
                # and len(val.get('units', '')) > 0):
                continue
            pprint(val)
            self.fail('illegal data')

    def test_contactcity(self):
        '''查询联络点对应上级城市 - contactcity

        bla~~~4
        '''
        ok = self.get('orgunit/contactcity')
        self.assertIn('data', ok, self.server)

    # @unittest.skip('y')
    def test_citycontact(self):
        ret = self.get('orgunit/citycontact')
        self.assertIn('data', ret, self.server)

    def test_orgunitid(self):
        ret = self.get('location/orgunitid/1')
        self.assertIn('data', ret, self.server)
        self.assertEqual(0, ret['data']['orgunitId'], self.server)
        ret = self.get('location/orgunitid/659002')
        self.assertIn('data', ret, self.server)
        self.assertNotEqual(0, ret['data']['orgunitId'], self.server)

    def test_location(self):
        api = 'orgunit/location/40,50'
        ret = self.get(api)
        self.assertIn('data', ret, '%s: %s' % (self.server, api))


def getTestSuite(*servers):
    auth = Auth()
    names = []
    for attr in dir(OrgunitApiTest):
        if attr.startswith('test_'):
            names.append(attr)
    suite = unittest.TestSuite()
    conf = {
        'dev': {
            'urlbase': 'http://www.xuxiangbase.dev.sankuai.com/api/'
        },
        'online': {
            'urlbase': 'http://base-in.sankuai.com/api/',
        },
        'cos16': {
            'urlbase': 'http://10.64.13.243:8088/api/',
            'headers': {'Host': 'base-in.sankuai.com'},
        },
        'cos17': {
            'urlbase': 'http://10.64.13.242:8088/api/',
            'headers': {'Host': 'base-in.sankuai.com'},
        },
        'base01': {
            'urlbase': 'http://10.64.13.160:8088/api/',
            'headers': {'Host': 'base-in.sankuai.com'},
        },
        'base02': {
            'urlbase': 'http://10.64.14.172:8088/api/',
            'headers': {'Host': 'base-in.sankuai.com'},
        },
    }
    for srv in servers:
        if srv not in conf:
            print('invalid server option:', srv)
            raise SystemExit(1)
        cfg = conf[srv]
        for n in names:
            suite.addTest(
                OrgunitApiTest(srv, auth, cfg['urlbase'], cfg.get('headers',
                                                                  None), n))
    return suite


if '__main__' == __name__:
    import argparse
    parser = argparse.ArgumentParser(description='run my cute test')
    parser.add_argument(
        'servers',
        metavar='servers',
        type=str,
        nargs='+',
        help='servers to test: dev online cos16 cos17 base01 base02')
    opts = parser.parse_args()
    suite = getTestSuite(*opts.servers)
    unittest.TextTestRunner().run(suite)
