#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2018/5/24 0024 14:12
# @Author  : Hadrianl
# @File    : utils.py
# @Contact   : 137150224@qq.com


from requests_futures.sessions import FuturesSession
import logging
import sys
import base64
import datetime
import hashlib
import hmac
from ecdsa import SigningKey
import json
import urllib
import urllib.parse
import urllib.request
import requests
from functools import wraps
import time
import zmq

_format = "%(asctime)-15s [%(levelname)s] [%(name)s] %(message)s"
_datefmt = "%Y/%m/%d %H:%M:%S"
_level = logging.INFO

handlers = [
    logging.StreamHandler(sys.stdout),
    logging.FileHandler('huobi.log')
]

logging.basicConfig(
    format=_format, datefmt=_datefmt, level=_level, handlers=handlers)
logging.addLevelName(60, 'WeChatLog')
logger = logging.getLogger('HuoBi')

SYMBOL = {'ethbtc', 'ltcbtc', 'etcbtc', 'bchbtc'}
PERIOD = {
    '1min', '5min', '15min', '30min', '60min', '1day', '1mon', '1week', '1year'
}
DEPTH = {
    0: 'step0',
    1: 'step1',
    2: 'step2',
    3: 'step3',
    4: 'step4',
    5: 'step5'
}

DerivativesDEPTH = {
    0: 'step0',
    1: 'step1',
    2: 'step2',
    3: 'step3',
    4: 'step4',
    5: 'step5',
    6: 'step6',
    7: 'step7',
    8: 'step8',
    9: 'step9',
    10: 'step10',
    11: 'step11',
}

class Depth:
    Step0 = 'step0'
    Step1 = 'step1'
    Step2 = 'step2'
    Step3 = 'step3'
    Step4 = 'step4'
    Step5 = 'step5'

class OrderType:
    BuyMarket = 'buy-market'  # '市价买'
    SellMarket = 'sell-market'  # '市价卖'
    BuyLimit = 'buy-limit' #'限价买'
    SellLimit = 'sell-limit' # '限价卖'
    BuyIoc = 'buy-ioc'  #'IOC买单'
    SellIoc = 'sell-ioc'  #'IOC卖单

class OrserStatus:
    PreSumitted = 'pre-submitted' # '准备提交'
    Submitted = 'submitted' # '已提交'
    PartialFilled = 'partial-filled' # '部分成交'
    PartialCanceled = 'partial-canceled'  # '部分成交撤销'
    Filled = 'filled'  # '完全成交'
    Canceled = 'canceled'  # '已撤销'


ORDER_TYPE = {
    'buy-market': '市价买',
    'sell-market': '市价卖',
    'buy-limit': '限价买',
    'sell-limit': '限价卖',
    'buy-ioc': 'IOC买单',
    'sell-ioc': 'IOC卖单',
    'buy-limit-maker': '限价买入做市',
    'sell-limit-maker': '限价卖出做市'
}
ORDER_STATES = {
    'pre-submitted': '准备提交',
    'submitted': '已提交',
    'partial-filled': '部分成交',
    'partial-canceled': '部分成交撤销',
    'filled': '完全成交',
    'canceled': '已撤销'
}

ORDER_SOURCE = {
'spot-web':	'现货 Web 交易单',
'spot-api':	'现货 Api 交易单',
'spot-app':	'现货 App 交易单',
'margin-web': '借贷 Web 交易单',
'margin-api': '借贷 Api 交易单',
'margin-app': '借贷 App 交易单',
'fl-sys': '借贷强制平仓单(爆仓单)'
}




ACCESS_KEY = ""
SECRET_KEY = ""
PRIVATE_KEY = ""

ETF_SWAP_CODE = {200: '正常',
                 10404: '基金代码不正确或不存在',
                 13403: '账户余额不足',
                 13404: '基金调整中,不能换入换出',
                 13405: '因配置项问题基金不可换入换出',
                 13406: '非API调用,请求被拒绝',
                 13410: 'API签名错误',
                 13500: '系统错误',
                 13601: '调仓期:暂停换入换出',
                 13603: '其他原因:暂停换入和换出',
                 13604: '暂停换入',
                 13605: '暂停换出',
                 13606: '换入或换出的基金份额超过规定范围'}

zmq_ctx = zmq.Context()
async_session = FuturesSession(max_workers=8)

# API 请求地址
DEFAULT_URL = 'https://api.huobi.br.com'
DEFAULT_DM_URL = 'https://api.hbdm.com'
MARKET_URL = 'https://api.huobi.br.com'
TRADE_URL = 'https://api.huobi.br.com'

ACCOUNT_ID = None


def setKey(access_key, secret_key):
    global ACCESS_KEY, SECRET_KEY, PRIVATE_KEY
    ACCESS_KEY = access_key
    SECRET_KEY = secret_key

def setUrl(market_url, trade_url):
    global MARKET_URL, TRADE_URL
    MARKET_URL = market_url
    TRADE_URL = trade_url

def createSign(pParams, method, host_url, request_path, secret_key):
    """
    from 火币demo, 构造签名
    :param pParams:
    :param method:
    :param host_url:
    :param request_path:
    :param secret_key:
    :return:
    """
    sorted_params = sorted(pParams.items(), key=lambda d: d[0], reverse=False)
    encode_params = urllib.parse.urlencode(sorted_params)
    payload = [method, host_url, request_path, encode_params]
    payload = '\n'.join(payload)
    payload = payload.encode(encoding='UTF8')
    secret_key = secret_key.encode(encoding='UTF8')

    digest = hmac.new(secret_key, payload, digestmod=hashlib.sha256).digest()
    signature = base64.b64encode(digest)
    signature = signature.decode()
    return signature

def createPrivateSign(secret_sign, private_key):
    signingkey = SigningKey.from_pem(private_key, hashfunc=hashlib.sha256)
    secret_sign = secret_sign.encode(encoding='UTF8')

    privateSignature = signingkey.sign(secret_sign)
    privateSignature = base64.b64encode(privateSignature)
    return privateSignature

def http_get_request(url, params, add_to_headers=None, _async=False):
    """
    from 火币demo, get方法
    :param url:
    :param params:
    :param add_to_headers:
    :return:
    """
    headers = {
        'Content-type':
        'application/x-www-form-urlencoded',
        'User-Agent':
        'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.71 Safari/537.36',
    }
    if add_to_headers:
        headers.update(add_to_headers)
    postdata = urllib.parse.urlencode(params)
    if _async:
        response = async_session.get(url, params=postdata, headers=headers, timeout=5)
        return response
    else:
        try:
            response = requests.get(url, postdata, headers=headers, timeout=5)
            if response.status_code == 200:
                return response.json()
            else:
                logger.debug(
                    f'<GET>error_code:{response.status_code}  reason:{response.reason} detail:{response.text}')
                return
        except Exception as e:
            logger.exception(f'<GET>httpGet failed, detail is:{response.text},{e}')
            return


def http_post_request(url, params, add_to_headers=None, _async=False):
    """
    from 火币demo, post方法
    :param url:
    :param params:
    :param add_to_headers:
    :return:
    """
    headers = {
        "Accept": "application/json",
        'Content-Type': 'application/json'
    }
    if add_to_headers:
        headers.update(add_to_headers)
    postdata = json.dumps(params)
    if _async:
        response = async_session.post(url, postdata, headers=headers, timeout=10)
        return response
    else:
        try:
            response = requests.post(url, postdata, headers=headers, timeout=10)
            if response.status_code == 200:
                return response.json()
            else:
                logger.debug(f'<POST>error_code:{response.status_code}  reason:{response.reason} detail:{response.text}')
                return
        except Exception as e:
            logger.exception(
                f'<POST>httpPost failed, detail is:{response.text},{e}')
            return


def api_key_get(params, request_path, _async=False, url=None):
    """
    from 火币demo, 构造get请求并调用get方法
    :param params:
    :param request_path:
    :return:
    """
    method = 'GET'
    timestamp = datetime.datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%S')
    params.update({
        'AccessKeyId': ACCESS_KEY,
        'SignatureMethod': 'HmacSHA256',
        'SignatureVersion': '2',
        'Timestamp': timestamp
    })

    host_url = DEFAULT_URL if url is None else url
    host_name = urllib.parse.urlparse(host_url).hostname.lower()
    secret_sign = createSign(params, method, host_name, request_path,
                                     SECRET_KEY)
    params['Signature'] = secret_sign

    url = host_url + request_path
    return http_get_request(url, params, _async=_async)


def api_key_post(params, request_path, _async=False, url=None):
    """
    from 火币demo, 构造post请求并调用post方法
    :param params:
    :param request_path:
    :return:
    """
    method = 'POST'
    timestamp = datetime.datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%S')
    params_to_sign = {
        'AccessKeyId': ACCESS_KEY,
        'SignatureMethod': 'HmacSHA256',
        'SignatureVersion': '2',
        'Timestamp': timestamp
    }

    host_url = DEFAULT_URL if url is None else url
    host_name = urllib.parse.urlparse(host_url).hostname.lower()
    secret_sign = createSign(params_to_sign, method, host_name,
                                             request_path, SECRET_KEY)
    params_to_sign['Signature'] = secret_sign

    url = ''.join([host_url, request_path , '?', urllib.parse.urlencode(params_to_sign)])
    return http_post_request(url, params, _async=_async)


def handler_profiler(filename=None):
    """
    handler的性能测试装饰器
    :param filename:
    :return:
    """
    if filename == None:
        f = sys.stdout
    else:
        f = open(filename, 'w')
    def _callfunc(handle):
        @wraps(handle)
        def func(self, topic, msg):
            t0 = time.time()
            handle(self, topic, msg)
            t1 = time.time()
            print(f'{self.name}-handle运行时间:{t1 - t0}s', file=f)

        return func
    return _callfunc

class Singleton(type):
    _instances = {}
    def __call__(cls, *args, **kwargs):
        if cls not in cls._instances:
            cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
        return cls._instances[cls]