#!/usr/bin/env python
# -*- coding:utf-8 -*-
#
#   Author  :   RobertDing
#   E-mail  :   robertdingx@gmail.com
#   Date    :   18/01/31 10:29:01
#   Desc    :   wiki backend 接口
#

from __future__ import unicode_literals, absolute_import, print_function

import itertools
from collections import (
    namedtuple,
    defaultdict,
)
from operator import itemgetter

from rpc.decorators import bind
from rpc.tool.dict_mixin import to_dict

from gm_types.wiki.enum import PRODUCT_TYPE, PRODUCT_PARAMS, PRODUCT_IMAGE_TYPE
from gm_types.wiki.enum import ATTESTATION_TYPE

from api.models.wordrel import WordRel
from wiki.models.directory import Directory
from wiki.models.product import Product, ProductRelationKeyWords, ProductParams
from wiki.models.collect import (
    Collect,
    CollectTag,
    CollectItem,
    CollectTagV3,
)
from wiki.models.brand import Brand, BrandRelationKeyWords
from wiki.models.item import (
    Item,
    ItemTag,
    ItemTagV3,
    ItemRelationKeyWords,
    ItemAfterKnow,
)

from wiki.logices.product import product_cover_image, product_brand_info
from wiki.logices.item import similar_collect, format_item_info
from wiki.logices.directory import directory_cell_info
from wiki.logices.item import ContrastData


@bind('gaia/wiki/directory')
def directory_info():
    result = []
    for x in Directory.objects.filter(is_online=True).select_related('collect', 'brand', 'product'):
        data = {
            'directory_type': x.dirctory_type,
            'model_type': x.model_type,
            'rank': x.rank,
        }
        if not directory_cell_info(x):
            continue
        data.update(directory_cell_info(x))
        result.append(data)
    return result


@bind('gaia/wiki/collect')
def collect_info(collect_id):
    collect = Collect.objects.get(id=collect_id, is_online=True)
    return {
        'name': collect.name,
        'description': collect.description,

        'similar_collections': [{
            'id': x.id, 'name': x.name
        } for x in similar_collect(collect)],

        'items': [{
            'id': x.item.id,
            'name': x.item.name,
            'description': x.item.description,
            'effect': x.item.effect,
            'fit_people': x.item.fit_people,
            'price': x.item.price,
            'is_hot': x.item.is_hot,
        } for x in collect.items.filter(is_online=True).order_by('rank')]
    }


@bind('gaia/wiki/brand')
def brand_info(brand_id):
    brand = Brand.objects.get(id=brand_id, is_online=True)
    return {
        'name': brand.name,
        'description': brand.description,
        'theory': brand.theory,
        'company': brand.company,
        'market_time': brand.market_time,
        'image_url': brand.image_url,

        'drugs': [{
            'id': x.id,
            'name': x.name,
            'is_cfda': x.attestations.filter(attestation_type=ATTESTATION_TYPE.CFDA, is_online=True).exists(),
            'description': x.description,
            'special': x.special,
            'image_url': product_cover_image(x),
            'is_hot': x.is_hot
        } for x in brand.products.filter(is_online=True)]
    }


@bind('gaia/wiki/item')
def item_info(item_id):
    item = Item.objects.get(id=item_id, is_online=True)
    return format_item_info(item)


@bind('gaia/wiki/product')
def product_info(product_id):
    product = Product.objects.get(id=product_id, is_online=True)
    return {
        'id': product.id,
        'name': product.name,
        'other_name': product.other_name,
        'product_type': product.product_type,
        'origin': product.origin,
        'description': product.description,
        'special': product.special,
        'theory': product.theory,
        'effect': product.effect,
        'application_range': product.application_range,
        'advantage': product.advantage,
        'disadvantage': product.disadvantage,
        'after_know': product.after_know,
        'safe_tips': product.safe_tips,
        'verify_url': product.verify_url,
        'clinical': product.clinical,
        'ingredient': product.ingredient,
        'treatment_method': product.treatment_method,
        'treatment_time': product.treatment_time,
        'treatment_num': product.treatment_num,
        'treatment_doctor': product.treatment_doctor,
        'maintain_time': product.maintain_time,
        'anesthesia_method': product.anesthesia_method,
        'ache_level': product.ache_level,
        'price': product.price,
        'hospitalized': product.hospitalized,
        'recover_time': product.recover_time,
        'is_hot': product.is_hot,
        'is_online': product.is_online,

        'brand': product_brand_info(product),
        'similar_item': [{
            'id': x.id, 'name': x.name
        } for x in product.similar_items.filter(is_online=True)],

        'similar_product': [{
            'id': x.id, 'name': x.name
        } for x in product.similar_product.filter(is_online=True) if x.id != product.id],

        'params': [{
            'params_type': x.params_type,
            'params_message': x.params_message,
            'params_name': PRODUCT_PARAMS.getDesc(x.params_type)
        } for x in product.params.all()],

        'attestation': [{
            'type': x.attestation_type,
            'image_url': x.attestation_image,
        } for x in product.attestations.filter(is_online=True)],
        'images': [{
            'image_url': x.image_url,
        } for x in product.images.filter(is_online=True, image_type=PRODUCT_IMAGE_TYPE.PRODUCT)],
        'verify_image': [{
            'image_url': x.image_url,
            'info': x.info
        } for x in product.images.filter(is_online=True, image_type=PRODUCT_IMAGE_TYPE.VERRIFY)],
    }


@bind('gaia/wiki/contrast')
def contrast_info(type, items):
    return ContrastData(type, items).data()


@bind('gaia/wiki/search')
def search_wiki(query='', count=3):
    if not query or count < 1:
        return []

    product_qs = Product.objects.filter(
        is_online=True, product_type__in=[
            PRODUCT_TYPE.DRUG,
            PRODUCT_TYPE.INSTRUMENT,
            PRODUCT_TYPE.MATERIALS
        ], name__contains=query
    )
    if product_qs.__len__() > 1:
        product_qs = []

    item_qs = Item.objects.filter(
        is_online=True, name__contains=query
    )
    if item_qs.__len__() > 1:
        item_qs = []

    brand_qs = Brand.objects.filter(
        is_online=True, name__contains=query
    )
    if brand_qs.__len__() > 1:
        brand_qs = []

    collect_qs = Collect.objects.filter(
        is_online=True, name__contains=query
    )
    if collect_qs.__len__() > 1:
        collect_qs = []

    result = []
    haved = set()
    # define iteration priority
    for o in itertools.chain(collect_qs, item_qs, product_qs, brand_qs):
        if o.name in haved:
            continue
        item = {
            'type': o.__class__.__name__.lower(),
            'id': o.id,
            'name': o.name,
            'description': o.description
        }
        result.append(item)
        count -= 1
        if not count:
            break

        if o.__class__.__name__.lower() == 'collect':
            haved.add(o.name)

    return result


@bind('api/wiki/search_by_tag')
def get_wiki_by_tags(tags=[]):

    if not tags:
        return {}

    tag_ids = [tag.get('id') for tag in tags]
    wiki_items = ItemTag.objects.filter(tag_id__in=tag_ids, item__is_online=True). \
                                 select_related('item')

    collect_ids = CollectTag.objects.filter(tag_id__in=tag_ids).values_list('collect_id', flat=True)
    wiki_collect = CollectItem.objects.select_related('item').\
                                       filter(collect_id__in=collect_ids, is_online=True)


    result = []
    for wiki in wiki_items:
        data = {
            'id': wiki.item.id,
            'name': wiki.item.name,
            'description': wiki.item.description,
            'icon': wiki.item.icon,
            'other_name': wiki.item.other_name,
            'is_hot': wiki.item.is_hot,
        }
        result.append(data)

    for wiki in wiki_collect:
        data = {
            'id': wiki.item.id,
            'name': wiki.item.name,
            'description': wiki.item.description,
            'icon': wiki.item.icon,
            'other_name': wiki.item.other_name,
            'is_hot': wiki.item.is_hot,
        }
        result.append(data)

    return {
        'data': result
    }


@bind("api/wiki/search_by_tag_v2")
def get_wiki_by_tags_v2(old_tag_ids=[], tag_v3_ids=[]):
    """
    通过新老标签获取百科
    有新标签 按照新标签来，新标签没关联百科则按照老标签来
    :param old_tag_ids: [] 老标签id列表
    :param tag_v3_ids: []  新标签id列表
    :return:
    """
    result = {
        "data": []
    }

    if not any([old_tag_ids, tag_v3_ids]):
        return result

    v3_relation_items = []
    v1_relation_items = []

    if tag_v3_ids:  # 有新标签
        relation_tag_v3_wiki_ids = set()
        v3_item_ids = set(ItemTagV3.objects.filter(
            tag_v3_id__in=tag_v3_ids
        ).values_list("item_id", flat=True))
        relation_tag_v3_wiki_ids.update(v3_item_ids)

        v3_collect_ids = set(CollectTagV3.objects.filter(
            tag_v3_id__in=tag_v3_ids
        ).values_list("collect_id", flat=True))
        v3_collect_item_ids = set(CollectItem.objects.filter(
            collect_id__in=v3_collect_ids,
            is_online=True
        ).values_list("item_id", flat=True))
        relation_tag_v3_wiki_ids.update(v3_collect_item_ids)

        v3_relation_items = Item.objects.filter(
            pk__in=relation_tag_v3_wiki_ids,
            is_online=True
        )

    if old_tag_ids and not v3_relation_items:  # 有标签 同时新标签未找到关联数据
        relation_tag_wiki_ids = set()
        v1_item_ids = set(ItemTag.objects.filter(
            tag_id__in=old_tag_ids
        ).values_list("item_id", flat=True))
        relation_tag_wiki_ids.update(v1_item_ids)

        v1_collect_ids = set(CollectTag.objects.filter(
            tag_id__in=tag_v3_ids
        ).values_list("collect_id", flat=True))
        v1_collect_item_ids = set(CollectItem.objects.filter(
            collect_id__in=v1_collect_ids,
            is_online=True
        ).values_list("item_id", flat=True))
        relation_tag_wiki_ids.update(v1_collect_item_ids)

        v1_relation_items = Item.objects.filter(
            pk__in=relation_tag_wiki_ids,
            is_online=True
        )

    wiki_items = v3_relation_items or v1_relation_items
    for wiki_item in wiki_items:
        data = {
            'id': wiki_item.id,
            'name': wiki_item.name,
            'description': wiki_item.description,
            'icon': wiki_item.icon,
            'other_name': wiki_item.other_name,
            'is_hot': wiki_item.is_hot,
        }
        result["data"].append(data)

    return result


'''
param:
    count limit default is 4
    wiki_product_ids
    wiki_item_ids
    wiki_brand_ids
    wiki_collect_ids
'''


@bind('api/wiki/search_list')
def search_list_wiki(count=4, ids=None):
    if not isinstance(ids, dict):
        return []
    product_qs = list()
    item_qs = list()
    brand_qs = list()
    collect_qs = list()

    wiki_product_ids = ids.get('wiki_product_ids')
    wiki_item_ids = ids.get('wiki_item_ids')
    wiki_brand_ids = ids.get('wiki_brand_ids')
    wiki_collect_ids = ids.get('wiki_collect_ids')

    if wiki_product_ids and isinstance(wiki_product_ids, list):
        product_qs = Product.objects.filter(
            id__in=wiki_product_ids,
            is_online=True, product_type__in=[
                PRODUCT_TYPE.DRUG,
                PRODUCT_TYPE.INSTRUMENT,
                PRODUCT_TYPE.MATERIALS
            ]
        )
    if wiki_item_ids and isinstance(wiki_item_ids, list):

        item_qs = Item.objects.filter(
            id__in=wiki_item_ids,
            is_online=True
        )
    if wiki_brand_ids and isinstance(wiki_brand_ids, list):
        brand_qs = Brand.objects.filter(
            id__in=wiki_brand_ids,
            is_online=True
        )
    if wiki_collect_ids and isinstance(wiki_collect_ids, list):
        collect_qs = Collect.objects.filter(
            id__in=wiki_collect_ids,
            is_online=True
        )

    result = list()
    for o in itertools.chain(collect_qs, item_qs, product_qs, brand_qs):
        item = {
            'type': o.__class__.__name__.lower(),
            'id': o.id,
            'name': o.name,
            'description': o.description,
            'price': getattr(o, "price", ''),
            'recover_time': getattr(o, 'recover_time', ''),
            'product_type': getattr(o, 'product_type', ''),  # 只有 Product 才有，用于区分药品、材料、仪器
        }
        detail_info = {}
        if hasattr(o, 'detail_info'):
           detail_info = o.detail_info()
        item.update(detail_info)
        result.append(item)
        count -= 1
        if not count:
            break

    return result


@bind("api/wiki/search_list_v2")
def search_list_wiki_v2(ids=None, count=10):
    """
    优化百科列表接口。
    add date 2019.11.14
    wiki: http://wiki.wanmeizhensuo.com/pages/viewpage.action?pageId=28911841
    产品基本上是把详情页的东西搬过来判断在卡片的展示逻辑。无语了……
    先这么写吧，后期在看有没有优化空间
    :param ids:
    :return:
    """
    if not isinstance(ids, dict):
        return []

    product_qs, item_qs, brand_qs, collect_qs = [], [], [], []
    wiki_polymer_dic = {}

    wiki_product_ids = ids.get('wiki_product_ids')
    wiki_item_ids = ids.get('wiki_item_ids')
    wiki_brand_ids = ids.get('wiki_brand_ids')
    wiki_collect_ids = ids.get('wiki_collect_ids')

    if wiki_product_ids and isinstance(wiki_product_ids, list):

        product_qs = Product.objects.filter(
            id__in=wiki_product_ids,
            is_online=True, product_type__in=[
                PRODUCT_TYPE.DRUG,
                PRODUCT_TYPE.INSTRUMENT,
                PRODUCT_TYPE.MATERIALS
            ]
        )
        # 参数
        product_params = ProductParams.objects.filter(
            product_id__in=wiki_product_ids
        ).values("product_id", "params_type", "params_message")

        product_params_dic = defaultdict(list)
        for item in product_params:
            _p_id = item["product_id"]
            _param_type = item["params_type"]
            product_params_dic[_p_id].append({
                "params_type": _param_type,
                "params_message": item["params_message"],
                "params_name": PRODUCT_PARAMS.getDesc(_param_type)
            })

        _type = Product.__name__.lower()
        for p in product_qs:
            _id = p.id
            _data = to_dict(p, excludes=["soyoung_id", "create_time", "similar_sy_product", "similar_product"])
            _data.update({
                "params": product_params_dic.get(_id, []),
                "similar_product": p.get_similar_products(),
            })
            wiki_polymer_dic[(_id, _type)] = _data

    if wiki_item_ids and isinstance(wiki_item_ids, list):
        item_qs = Item.objects.filter(
            id__in=wiki_item_ids,
            is_online=True
        )

        item_after_knows_dic = defaultdict(list)
        item_after_knows = ItemAfterKnow.objects.filter(
            item_id__in=wiki_item_ids,
            is_online=True
        ).values("item_id", "title", "rank", "recover_tips", "care_method")

        for item in item_after_knows:
            _item_id = item["item_id"]
            item_after_knows_dic[_item_id].append({
                "title": item["title"],
                "rank": item["rank"],
                "recover_tips": item["recover_tips"],
                "care_method": item["care_method"],
            })

        _type = Item.__name__.lower()
        for wiki_item in item_qs:
            _item_id = wiki_item.id
            _data = to_dict(
                wiki_item,
                excludes=["soyoung_id", "create_time", "similar_sy_item", "related_sy_drug", "similar_item", "related_product"]
            )
            _data.update({
                "after_know": sorted(item_after_knows_dic.get(_item_id, []), key=lambda x: x["rank"]),
                "similar_item": [s.detail_info(extra=False) for s in wiki_item.valid_similar_items()]
            })
            wiki_polymer_dic[(_item_id, _type)] = _data

    if wiki_brand_ids and isinstance(wiki_brand_ids, list):
        brand_qs = Brand.objects.filter(
            id__in=wiki_brand_ids,
            is_online=True
        )

        # 旗下产品
        brand_products_dic = defaultdict(list)
        products = Product.objects.filter(brand_id__in=wiki_brand_ids, is_online=True)
        for p in products:
            _data = {
                "id": p.id,
                "name": p.name,
                "description": p.description,
                "special": p.special,
                # "image_url": product_cover_image(p),  # 图先注释
            }
            brand_products_dic[p.brand_id].append(_data)

        _type = Brand.__name__.lower()
        for brand_wiki in brand_qs:
            _brand_id = brand_wiki.id
            _data = to_dict(brand_wiki, excludes=["create_time", "soyoung_id"])
            _data.update({
                "drugs": brand_products_dic.get(brand_wiki.id, []),
            })
            wiki_polymer_dic[(_brand_id, _type)] = _data

    if wiki_collect_ids and isinstance(wiki_collect_ids, list):
        collect_qs = Collect.objects.filter(
            id__in=wiki_collect_ids,
            is_online=True
        )

        # 聚合项目
        collect_items = CollectItem.objects.filter(
            collect_id__in=wiki_collect_ids,
            is_online=True
        ).values("collect_id", "item_id", "rank")

        item_ids = [c_item["item_id"] for c_item in collect_items]
        items_info = Item.objects.filter(pk__in=item_ids, is_online=True).values(
            "id", "name", "description", "effect", "price", "is_hot", "fit_people"
        )
        item_infos_dic = {item["id"]: dict(item) for item in items_info}

        # 聚合项目信息
        collect_items_dic = defaultdict(list)
        for collect_item in collect_items:
            _collect_id = collect_item["collect_id"]
            _item_id = collect_item["item_id"]
            _rank = collect_item.get("rank", 999)

            _item_info = item_infos_dic.get(_item_id, {})
            if _item_info:
                _item_info.update({
                    "c_item_rank": _rank,  # 把排序移过来
                })
                collect_items_dic[_collect_id].append(_item_info)

        _type = Collect.__name__.lower()
        for collect in collect_qs:
            _collect_id = collect.id
            _data = to_dict(collect, excludes=["create_time", "is_online"])
            _data.update({
                "items": sorted(collect_items_dic.get(_collect_id, []), key=lambda x: x.get("c_item_rank", 999)),
                "similar_collections": [{  # 同类产品
                    'id': x.id,
                    'name': x.name
                } for x in similar_collect(collect)],
            })
            wiki_polymer_dic[(_collect_id, _type)] = _data

    result = []
    for o in itertools.chain(collect_qs, item_qs, product_qs, brand_qs):
        item = {
            'type': o.__class__.__name__.lower(),
            'id': o.id,
            'name': o.name,
            'description': o.description,
            'price': getattr(o, "price", ''),
            'recover_time': getattr(o, 'recover_time', ''),
            'product_type': getattr(o, 'product_type', ''),  # 只有 Product 才有，用于区分药品、材料、仪器
        }
        item.update(wiki_polymer_dic.get((item["id"], item["type"]), {}))

        result.append(item)
        count -= 1

        if not count:
            break

    return result


@bind("api/wiki/relation_keywords")
def get_wiki_relation_keywords(wiki_id, wiki_type):
    """
    通过百科id和类型，获取对应的标签数据
    :param wiki_id:
    :param wiki_type:
    :return:
    """
    _nt = namedtuple("WIKI_MODEL_TYPE", ["ITEM", "BRAND", "PRODUCT"])
    WIKI_MODEL_TYPE = _nt(ITEM='item', BRAND="brand", PRODUCT='product')

    result = {
        "relation_keywords": [],
    }

    filters_kw = {
        "is_effect": True,
    }

    if wiki_type == WIKI_MODEL_TYPE.ITEM:
        _model = ItemRelationKeyWords
        _wiki_param = 'item_id'

    elif wiki_type == WIKI_MODEL_TYPE.BRAND:
        _model = BrandRelationKeyWords
        _wiki_param = 'brand_id'

    elif wiki_type == WIKI_MODEL_TYPE.PRODUCT:
        _model = ProductRelationKeyWords
        _wiki_param = 'product_id'
    else:
        _model = None
        _wiki_param = None

    if _model and _wiki_param:
        filters_kw.update({_wiki_param: wiki_id})
        _data = _model.objects.filter(**filters_kw).values("wordrel_id", "category")

        wordrel_ids = set(map(itemgetter("wordrel_id"), _data))
        wordrel_data = dict(WordRel.objects.filter(pk__in=wordrel_ids).values_list("id", "keyword"))

        for item in _data:
            _wordrel_id = item.get("wordrel_id", 0)
            item["keyword"] = wordrel_data.get(_wordrel_id, "")

        result["relation_keywords"] = list(_data)

    return result
