#!/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
from wiki.models.brand import Brand, BrandRelationKeyWords
from wiki.models.item import Item, ItemTag, 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
    }


'''
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
