# coding=utf-8


import itertools

from api.models import Tag, ItemWiki, Service, TagRelation
from api.models import TAG_TYPE
from rpc.tool.error_code import CODES, gen


def get_tag_by_id(id):
    if not id:
        raise gen(CODES.PARAMS_INCOMPLETE)

    try:
        tag = Tag.objects.get(pk=id)
    except Tag.DoesNotExist:
        raise gen(CODES.TAG_NOT_FOUND)

    return tag


def get_tags_by_ids(ids):
    tags = Tag.objects.filter(id__in=ids, is_online=True)
    return tags


def get_service_tags(service_id):
    try:
        s = Service.objects.get(pk=service_id)
    except:
        return []

    return s.get_tags()


def _get_level23_tags(tag, with_level_3, is_online=True, ordering=False):
    tags = []

    tag_info = {'name': tag.name, 'tag_id': tag.id, 'ordering': tag.ordering}
    if tag.tag_type == TAG_TYPE.BODY_PART_SUB_ITEM and with_level_3:
        tag_info['subtags'] = []
        for t in tag.child_tags_with_condition_is_online(is_online, ordering=ordering):
            tag_info['subtags'].extend(_get_level23_tags(t, with_level_3, is_online, ordering=ordering))

    if not is_online or (is_online and tag.is_online):
        tags.append(tag_info)
    if ordering:
        tags.sort(key=lambda o: (o['ordering'], o['tag_id']))
    return tags


def get_wiki_by_tag(tag, is_online=True):
    """get wiki tags by tag object.

    NOTE:
        only body part and sub body part item tag has WIKIs.
    """
    if not tag.is_online: return

    tags = []
    if tag.tag_type == TAG_TYPE.BODY_PART:
        for t in tag.child_tags_with_condition_is_online(is_online):
            for st in t.child_tags_with_condition_is_online(is_online):
                wiki = ItemWiki.get_wiki_by_tagid(st.id)
                if not wiki or (is_online and not wiki.is_online): continue
                tags.append({'wiki_name': st.name, 'tag_id': st.id, 'wiki_id': wiki.id})

    elif tag.tag_type == TAG_TYPE.BODY_PART_SUB_ITEM:
        for t in tag.child_tags_with_condition_is_online(is_online):
            wiki = ItemWiki.get_wiki_by_tagid(t.id)
            if not wiki or (is_online and not wiki.is_online): continue
            tags.append({'wiki_name': t.name, 'tag_id': t.id, 'wiki_id': wiki.id})

    return tags


def get_bodypart_tags(with_level_3, is_online=True, ordering=False):
    """
    相似接口: get_bodypart_tags_without_empty_container
    """
    if ordering:
        bodypart_tags = Tag.objects.filter(tag_type=TAG_TYPE.BODY_PART).order_by('ordering', 'id')
    else:
        bodypart_tags = Tag.objects.filter(tag_type=TAG_TYPE.BODY_PART).order_by('id')
    result = []

    for tag in bodypart_tags:
        if is_online and not tag.is_online: continue

        t = {'name': tag.name, 'tag_id': tag.id, 'subtags': []}

        for c in tag.child_tags_with_condition_is_online(is_online, ordering=ordering):
            t['subtags'].extend(_get_level23_tags(c, with_level_3, is_online, ordering=ordering))

        t['subtags'].sort(key=lambda o: (o['ordering'], o['tag_id']))

        result.append(t)

    return result


def get_bodypart_tags_with_tag_filter(permitted_tag_id_list):
    """
    接口数据格式参考: get_bodypart_tags
    """

    # 二级标签相关
    # {{{
    bodypart_subitem_fromchild_qs = TagRelation.objects.filter(
        parent__is_online=True,
        parent__tag_type=TAG_TYPE.BODY_PART_SUB_ITEM,
        child__is_online=True,
        child__tag_type=TAG_TYPE.ITEM_WIKI,
    )
    bodypart_subitem_fromchild_id_set = frozenset(
        rel.parent_id
        for rel in bodypart_subitem_fromchild_qs
        if rel.child_id in permitted_tag_id_list
    )

    bodypart_subitem_fromself_id_set = frozenset(
        Tag.objects.filter(tag_type=TAG_TYPE.BODY_PART_SUB_ITEM).values_list('id', flat=True)
    ).intersection(permitted_tag_id_list)

    bodypart_subitem_id_set = bodypart_subitem_fromchild_id_set | bodypart_subitem_fromself_id_set
    # }}}

    # 一级标签相关
    # {{{
    relation_12_fromchild = TagRelation.objects.filter(
        parent__is_online=True,
        parent__tag_type=TAG_TYPE.BODY_PART,
        child_id__in=bodypart_subitem_id_set,
    ).values('parent_id', 'child_id')

    bodypart_fromself_id_set = frozenset(
        Tag.objects.filter(tag_type=TAG_TYPE.BODY_PART).values_list('id', flat=True)
    ).intersection(permitted_tag_id_list)

    relation_12 = sorted(itertools.chain(
        relation_12_fromchild,
        (dict(parent_id=bodypart_id, child_id=None) for bodypart_id in bodypart_fromself_id_set)
    ))
    # }}}

    # 获取 Tag 名字
    # {{{
    tag_id_set = frozenset(itertools.chain(
        (
            r['parent_id']
            for r in relation_12
        ),
        (
            r['child_id']
            for r in relation_12
            if r['child_id'] is not None
        ),
    ))
    tags = Tag.objects.filter(pk__in=tag_id_set).values('id', 'name')
    tag_map = {
        tag['id']: tag
        for tag in tags
    }
    # }}}

    # 汇总
    # {{{
    result = []
    result_map = {}

    for r12 in relation_12:
        bodypart_id = r12['parent_id']
        bodypart_subitem_id = r12['child_id']

        if bodypart_id not in result_map:
            bodypart_tag = tag_map[bodypart_id]
            t = {'name': bodypart_tag['name'], 'tag_id': bodypart_tag['id'], 'subtags': []}
            result_map[bodypart_id] = t
            result.append(t)
        else:
            t = result_map[bodypart_id]

        if bodypart_subitem_id is None:
            continue

        subitem_list = t['subtags']

        subitem_tag = tag_map[bodypart_subitem_id]
        subitem_info = {'name': subitem_tag['name'], 'tag_id': subitem_tag['id']}

        subitem_list.append(subitem_info)
    # }}}

    return result


def get_province_tags(with_city_tag=False, is_online=True):
    result = []
    province_tags = Tag.objects.filter(tag_type=TAG_TYPE.PROVINCE)
    if is_online:
        province_tags = province_tags.filter(is_online=True)

    for pt in province_tags:
        data = {'name': pt.name, 'tag_id': pt.id}
        if with_city_tag:
            data['subtags'] = get_city_tags(pt.id, is_online)
        result.append(data)

    return result


def get_city_tags(province_tag_id=None, is_online=True):
    if province_tag_id:
        province_tag = Tag.objects.get(pk=province_tag_id)
        tags = province_tag.child_tags_with_condition_is_online(is_online)

    else:
        tags = Tag.objects.filter(tag_type=TAG_TYPE.CITY)
        if is_online:
            tags = tags.filter(is_online=True)

    result = []
    for tag in tags:
        result.append({'tag_id': tag.id, 'name': tag.name})

    return result


def get_all_tag_types():
    tagtypes = []
    for id, name in TAG_TYPE:
        tag_type = {"id":id, "name":name}
        tagtypes.append(tag_type)
    return tagtypes


def get_page_tags(page, each_page_amount, query, category):
    offset = (page-1) * each_page_amount
    limit = offset + each_page_amount

    tags = Tag.objects.all()

    if category:
        tags = tags.filter(tag_type=category)
    if query:
        tags = tags.filter(name__contains=query)
    tags = tags[offset:limit]

    tag_info_list = []
    for tag in tags:
        tag_info = {
            "id":  tag.id,
            "tagname": tag.name,
            "tagtype": TAG_TYPE.getDesc(tag.tag_type),
        }
        tag_info_list.append(tag_info)
    return tag_info_list


def get_page_info(page, each_page_amount, query, category):
    tags = Tag.objects.all()
    if category:
        tags = tags.filter(tag_type=category)
    if query:
        tags = tags.filter(name__contains=query)

    tags_count = tags.count()

    f = lambda a, b: (a-1)/b + 1
    max_page_number = f(tags_count, each_page_amount)
    # 防止用户请求错误页码
    page = 1 if page < 1 else page
    page = max_page_number if page > max_page_number else page
    has_prev = page > 1
    hsa_next = page < max_page_number
    return {
        "current": page,
        "has_prev": has_prev,
        "has_next": hsa_next,
        "max": max_page_number,
        "range": [p+1 for p in range(max_page_number)],
    }


def get_tags_by_type(types):
    tags = Tag.objects.filter(tag_type__in=types).values_list('name', flat=True)
    return list(tags)
