import logging

from py_jftech import autowired, component, get_config

from api import AssetOptimize, PortfoliosChecker, Datum, Navs, DatumType

logger = logging.getLogger(__name__)


@component(bean_name='checker')
class DefaultPortfoliosChecker(PortfoliosChecker):

    @autowired
    def __init__(self, asset: AssetOptimize = None, navs: Navs = None, datum: Datum = None):
        self._asset = asset
        self._navs = navs
        self._datum = datum
        self._config = get_config(__name__)

    def check(self, day=None, portfolios: dict = None):
        if not self._config.get('switch'):
            return portfolios
        funds = self._datum.get_datums(type=DatumType.FUND)
        company = {f"{fund['id']}": fund['companyType'] for fund in funds}
        customType = {f"{fund['id']}": fund['customType'] for fund in funds}
        companies = set(company[key] for key in portfolios.keys())
        # 同时出现全部是ft或美盛基金的情况
        if len(companies) == 1:
            # step1:	检查原始投组的customType。检查顺序用列表呈现，依序进行
            priority = self._config.get('custom-type-priority')
            for p in priority:
                # 找出对应优先级序列的基金列表
                keys = [key for key in portfolios.keys() if customType[key] == p]
                # 若存在匹配值则执行后跳出循环
                if len(keys) > 0:
                    # 选取非同公司的、风险等级小于等于原基金的 基金
                    min_risk = min(fund['risk'] for fund in funds if str(fund['id']) in keys)
                    ids = [fund['id'] for fund in funds if fund['companyType'] != list(companies)[0] and
                           fund['risk'] <= min_risk]
                    if len(ids) == 0:
                        continue
                    best = self.find_highest_score(ids, day)
                    # 若刚好有一个匹配，直接替换
                    if len(keys) == 1:
                        portfolios[best] = portfolios[keys[0]]
                        # 删除原始键
                        del portfolios[keys[0]]
                    else:
                        # 算分，把分低的替换掉
                        scores = self.do_score(keys, day)
                        weight_scores = {key: scores[key] * portfolios[key] for key in keys}
                        lowest = min(scores, key=lambda k: weight_scores[k])
                        portfolios[best] = portfolios[lowest]
                        # 删除原始键
                        del portfolios[lowest]
                    break
        return portfolios

    def do_score(self, ids, day):
        optimize = self._asset.find_optimize(fund_ids=ids, day=day)
        scores = optimize[1].to_dict()
        id_score = {}
        for k, v in scores.items():
            id_score[f'{ids[k]}'] = v
        return id_score

    def find_highest_score(self, ids, day):
        optimize = self._asset.find_optimize(fund_ids=ids, day=day)
        return optimize[0][0]
