# -*- coding: utf-8 -*-
import json
import os
from json import JSONDecodeError

import re
import requests

from easytrader.log import log
from easytrader.webtrader import NotLoginError, TradeError
from easytrader.webtrader import WebTrader
import easytrader.xqtrader


class XueQiuClient(WebTrader):
    config_path = os.path.dirname(easytrader.xqtrader.__file__) + '/config/xq.json'

    def __init__(self, **kwargs):
        super(XueQiuClient, self).__init__()
        headers = {
            'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:32.0) Gecko/20100101 Firefox/32.0',
            'Host': 'xueqiu.com',
            'Pragma': 'no-cache',
            'Connection': 'keep-alive',
            'Accept': '*/*',
            'Accept-Encoding': 'gzip,deflate,sdch',
            'Cache-Control': 'no-cache',
            'Referer': 'http://xueqiu.com/P/ZH003694',
            'X-Requested-With': 'XMLHttpRequest',
            'Accept-Language': 'zh-CN,zh;q=0.8'
        }
        self.session = requests.Session()
        self.session.headers.update(headers)
        self.account_config = None
        self.config.update({
            "create_cubes_url": "https://xueqiu.com/cubes/create.json",
            "get_token_url": "https://xueqiu.com/service/csrf",
            "get_cubes_list": "https://xueqiu.com/v4/stock/portfolio/stocks.json",
            "get_cubes_detail": "https://xueqiu.com/cubes/quote.json",
        })

    def autologin(self, **kwargs):
        """
        重写自动登录方法
        避免重试导致的帐号封停
        :return:
        """
        self.login()

    def login(self, throw=False):
        """
        登录
        :param throw:
        :return:
        """
        login_status, result = self.post_login_data()
        if login_status is False and throw:
            raise NotLoginError(result)
        log.debug('login status: %s' % result)
        return login_status

    def _prepare_account(self, user='', password='', **kwargs):
        """
        转换参数到登录所需的字典格式
        :param user: 雪球邮箱(邮箱手机二选一)
        :param password: 雪球密码
        :param account: 雪球手机号(邮箱手机二选一)
        :param portfolio_market: 交易市场, 可选['cn', 'us', 'hk'] 默认 'cn'
        :return:
        """
        if 'portfolio_market' not in kwargs:
            kwargs['portfolio_market'] = 'cn'
        if 'account' not in kwargs:
            kwargs['account'] = ''
        self.account_config = {
            'username': user,
            'account': kwargs['account'],
            'password': password,
            'portfolio_market': kwargs['portfolio_market']
        }

    def post_login_data(self):
        login_post_data = {
            'username': self.account_config.get('username', ''),
            'areacode': '86',
            'telephone': self.account_config['account'],
            'remember_me': '0',
            'password': self.account_config['password']
        }
        login_response = self.session.post(self.config['login_api'], data=login_post_data)
        login_status = login_response.json()
        if 'error_description' in login_status:
            return False, login_status['error_description']
        return True, "SUCCESS"

    def __search_stock_info(self, code):
        """
        通过雪球的接口获取股票详细信息
        :param code: 股票代码 000001

        :return: 查询到的股票
        {"code":"SZ000651","name":"格力电器","enName":null,"hasexist":null,"flag":1,"type":null,"current":45.46,"chg":0.6,"percent":"1.34","stock_id":1001306,"ind_id":100007,"ind_name":"家用电器","ind_color":"#82b952","textname":"格力电器(SZ000651)","segment_name":"家用电器","weight":30,"url":"/S/SZ000651","proactive":true,"price":"45.46"}
        ** flag : 未上市(0)、正常(1)、停牌(2)、涨跌停(3)、退市(4)
        """
        data = {
            'code': str(code),
            'size': '300',
            'key': '47bce5c74f',
            'market': self.account_config['portfolio_market'],
        }
        r = self.session.get(self.config['search_stock_url'], params=data)
        stocks = json.loads(r.text)
        stocks = stocks['stocks']
        stock = None
        if len(stocks) > 0:
            stock = stocks[0]
        return stock

    def __get_create_cube_token(self):
        """获取创建组合时需要的token信息
        :return: token
        """
        response = self.session.get(self.config["get_token_url"], params={
            "api": "/cubes/create.json"
        })
        try:
            token_response = json.loads(response.text)
        except JSONDecodeError:
            raise TradeError("解析创建组合的token信息失败: %s" % response.text)
        if 'token' not in token_response:
            raise TradeError("获取创建组合的token信息失败: %s" % response.text)
        return token_response['token']

    @staticmethod
    def get_cube_name(cube_prefix, stock_code):
        return "%s%s" % (cube_prefix, stock_code)

    def create_cube(self, stock_code, weight, cube_prefix="SC", description="", market='cn'):
        """创建组合, 并设置股票初始买入的百分比
        ** 组合名称默认格式为前缀 + 股票代码
        :param stock_code: str 股票代码
        :param weight: int 初始仓位的百分比, 0 - 100 之间的整数
        :param cube_prefix: 组合名字的前缀
        :param description: 组合描述信息
        :param market: 市场范围, cn: 沪深, us: 美股, hk: 港股
        :return: (是否创建成功, 组合代码, 组合名称)
        """
        cube_name = self.get_cube_name(cube_prefix, stock_code)
        stock = self.__search_stock_info(stock_code)
        if stock is None:
            raise TradeError(u"没有查询要操作的股票信息")
        if stock['flag'] != 1:
            raise TradeError(u"未上市、停牌、涨跌停、退市的股票无法操作。")
        holdings = [
            {
                "code": stock['code'],
                "name": stock['name'],
                "enName": stock['enName'],
                "hasexist": stock['hasexist'],
                "flag": stock['flag'],
                "type": stock['type'],
                "current": stock['current'],
                "chg": stock['chg'],
                "percent": str(stock['percent']),
                "stock_id": stock['stock_id'],
                "ind_id": stock['ind_id'],
                "ind_name": stock['ind_name'],
                "ind_color": stock['ind_color'],
                "textname": "%s(%s)" % (stock['name'], stock['code']),
                "segment_name": stock['ind_name'],
                "weight": weight,
                "url": "/S/" + stock['code'],
                "proactive": True,
                "price": str(stock['current'])
            }
        ]

        create_cube_data = {
            "name": cube_name,
            "cash": 100 - weight,
            "description": description,
            "market": market,
            "holdings": json.dumps(holdings),
            "session_token": self.__get_create_cube_token(),
        }
        try:
            cube_res = self.session.post(self.config['create_cubes_url'], data=create_cube_data)
        except Exception as e:
            log.warn('创建组合%s失败: %s ' % (cube_name, e))
            return (False, None, None)
        else:
            log.debug('创建组合%s: 持仓比例%d' % (cube_name, weight))
            cube_res_status = json.loads(cube_res.text)
            if 'error_description' in cube_res_status.keys() and cube_res.status_code != 200:
                log.error('创建组合错误: %s, error_no: %s, error_info: %s' % (
                    cube_res_status['error_description'],
                    cube_res_status['error_code'],
                    cube_res_status['error_description'],
                ))
                if cube_res_status['error_code'] == '20912':
                    log.error("组合名称: %s 不符合要求, 请尝试换一个组合名称,组合名称只能是中文,英文,数字(测试时发现某些情况下可以包含下划线)" % cube_name)
                return (False, None, None)
            log.debug('创建组合成功 %s: 持仓比例%d, 创建信息: \n%s' % (
                cube_name, weight, json.dumps(cube_res_status, ensure_ascii=False, indent=4)))
            return (True, cube_res_status['symbol'], cube_name)

    def get_cubes_list(self, type=4):
        """获取组合详情,默认获取自选组合
        :param type: 组合名字的前缀, 1: 全部组合。 4: 我的组合。 5: 只看沪深组合。 6: 只看美股。7: 只看港股
        :return: 组合列表
        """
        response = self.session.get(self.config['get_cubes_list'], params={
            "category": 1,
            "type": type
        })
        try:
            cubes_response = json.loads(response.text)
        except JSONDecodeError:
            log.warning(response.text)
            raise TradeError("解析组合列表失败: %s" % response.text)
        if 'stocks' not in cubes_response:
            log.warning(cubes_response)
            raise TradeError("获取组合信息失败: %s" % response.text)
        cubes_code_list = [cube['code'] for cube in cubes_response['stocks']]

        response = self.session.get(self.config['get_cubes_detail'], params={
            "code": ','.join(cubes_code_list),
            "return_hasexist": False,
        })
        try:
            cubes_detail_response = json.loads(response.text)
        except JSONDecodeError:
            raise TradeError("解析组合详情失败: %s" % response.text)
        if 'stocks' not in cubes_response:
            raise TradeError("获取组合信息失败: %s" % response.text)
        return cubes_detail_response

    def get_portfolio_info(self, portfolio_code):
        """
        获取组合信息
        :return: 字典
        """
        url = self.config['portfolio_url'] + portfolio_code
        html = self.__get_html(url)
        match_info = re.search(r'(?<=SNB.cubeInfo = ).*(?=;\n)', html)
        if match_info is None:
            raise Exception('cant get portfolio info, portfolio html : {}'.format(html))
        try:
            portfolio_info = json.loads(match_info.group())
        except Exception as e:
            raise Exception('get portfolio info error: {}'.format(e))
        return portfolio_info

    def __get_html(self, url):
        return self.session.get(url).text

if __name__ == '__main__':
    from backtradercn.settings import settings as conf

    client = XueQiuClient()
    client.prepare(account=conf.XQ_ACCOUNT, password=conf.XQ_PASSWORD, portfolio_market=conf.XQ_PORTFOLIO_MARKET)
    # 创建股票代码为000651的组合
    response = client.create_cube(stock_code="000651", weight=5, cube_prefix=conf.XQ_CUBES_PREFIX)
    print(response)
    # 获取自定义组合信息
    cubes_list = client.get_cubes_list()
    print(cubes_list)