# -*- coding: UTF-8 -*-
from django.db import transaction
from gm_types.gaia import WIKI_LEVEL


import json
from django.db.models import Q
from django.db import IntegrityError
from django.conf import settings

from api.tool.itemwiki_tool import ItemwikiHot
from hera.queries.itemwiki import ItemWikiDQ
from hera.queries.wiki_advertise import WikidetailAdverDQ
from rpc.tool.error_code import CODES, gen
from rpc.decorators import bind_context
from rpc.exceptions import RPCNotFoundException, RPCIntegrityError
from rpc.tool.datatables import to_dict
from rpc.tool.log_tool import info_logger
from api.models.itemwiki import  WikidetailAdver
from api.models.itemwiki import ItemWiki, OptimizeWiki, ConnectWiki, WikiStruct
from ..datatables import ItemWikiDT
from api.models.types import OPTIMIZE_WIKI_TYPE
from .talos.diary import diary_choices
from ..queries.itemwiki import ItemWikiDQ

uri_pre = 'hera/itemwiki'


@bind_context(uri_pre + '/get_hots')
def hots_get(ctx):
    hots = ItemwikiHot.get()
    return hots


@bind_context(uri_pre + '/set_hots')
def hots_set(ctx, hot_ids):
    ItemwikiHot.set(hot_ids)
    return gen(CODES.SUCCESS)


@bind_context(uri_pre + '/choices')
def itemwiki_choices(ctx, q='', page=1, num=30, initial=None):
    page = int(page)
    num = int(num)

    if initial is not None:
        if isinstance(initial, (list, tuple)):
            qry = Q(id__in=initial)
        else:
            qry = Q(id=initial)
    else:
        qry = Q(id__contains=q) | Q(item_name__contains=q)
    qry &= Q(is_online=True)
    query = ItemWiki.objects.using(settings.SLAVE_DB_NAME).filter(qry).order_by('-id')
    total_count = query.count()
    start_pos = (page - 1) * num
    start_pos = start_pos if start_pos >= 0 else 0
    results = [
        {
            'id': obj.id,
            'text': u'{}:{}'.format(obj.id, obj.item_name)
        } for obj in query[start_pos: start_pos + num]
        ]
    return {'total_count': total_count, 'results': results, 'page': page, 'num': num}


@bind_context(uri_pre + '/query')
def itemwiki_query(ctx, options):
    dqobj = ItemWikiDQ()
    return dqobj.process(**options)


@bind_context(uri_pre + '/list')
def itemwiki_datatable(ctx, req_data):
    dtobj = ItemWikiDT(ItemWiki)
    return dtobj.process(req_data, ['item_name', 'slogan'])


@bind_context(uri_pre + '/get')
def itemwiki_detail(ctx, itemwiki_id, options=None):
    try:
        itemwiki = ItemWiki.objects.get(id=itemwiki_id)
    except:
        raise RPCNotFoundException

    if options is None:
        options = {
            'fields': None,
            'excludes': None,
            'expands': None,
        }
    itemwiki_data = to_dict(itemwiki, **options)
    return itemwiki_data


@bind_context(uri_pre + '/edit')
def itemwiki_edit(ctx, itemwiki_id=None, itemwiki_info=None):
    if itemwiki_info is None:
        return None
    itemwiki_info['bodypart_subitem_id'] = itemwiki_info.pop('bodypart_subitem', None)
    itemwiki_info['tag_id'] = itemwiki_info.pop('tag', None)
    if itemwiki_id is None:
        try:
            itemwiki = ItemWiki.objects.create(**itemwiki_info)
        except IntegrityError:
            raise RPCIntegrityError
    else:
        try:
            itemwiki = ItemWiki.objects.get(id=itemwiki_id)
        except:
            info_logger.info(__import__('traceback').format_exc())
            raise RPCNotFoundException
        for k, v in itemwiki_info.iteritems():
            setattr(itemwiki, k, v)
        itemwiki.save()

    return itemwiki.id


@bind_context(uri_pre + '/update')
def update_itemwiki_status(ctx, wiki_id):
    try:
        wiki = ItemWiki.objects.get(id=wiki_id)
    except ItemWiki.DoesNotExist:
        raise RPCNotFoundException
    wiki.is_online = not wiki.is_online
    wiki.save()
    return wiki.id


#  ====================== new wiki ==================

@bind_context(uri_pre + '/new_detail')
def projectwiki_detail(ctx, projectwiki_id):
    """
    获取项目百科的详细信息
    """
    if projectwiki_id is None:
        return None
    projectwiki = ItemWiki.objects.get(pk=projectwiki_id)
    projectwiki_info = to_dict(projectwiki)
    return projectwiki_info


@bind_context(uri_pre + '/getby_tag')
def projectwiki_getbytag(ctx, tag_id):
    """
    根据tag获取该项目百科的wiki数目
    :param tag_id:
    :param ctx:
    """
    if tag_id is None:
        return 0
    count = ItemWiki.objects.filter(tag_id=tag_id).count()
    return count


@bind_context(uri_pre + '/new_edit')
def projectwiki_edit(ctx, projectwiki_info, projectwiki_id=None):
    """
     项目百科编辑
     :param projectwiki_id:
     :param projectwiki_info:
     :param ctx:
    """
    if projectwiki_info is None:
        return None
    if projectwiki_id is None:
        try:
            projectwiki = ItemWiki.objects.create(**projectwiki_info)
        except IntegrityError:
            raise RPCIntegrityError
    else:
        try:
            projectwiki = ItemWiki.objects.get(id=projectwiki_id)
        except:
            info_logger.info(__import__('traceback').format_exc())
            raise RPCNotFoundException
        for k, v in projectwiki_info.iteritems():
            setattr(projectwiki, k, v)
        projectwiki.save()
    return {"projectwiki_id": projectwiki.id}


@bind_context(uri_pre + '/new_query')
def projectwiki_query(ctx, options):
    dqobj = ItemWikiDQ()
    return dqobj.process(**options)


@bind_context(uri_pre + '/getby_wiki')
def wikiadver_getbytag(ctx, wiki_id):
    """
    根据wiki_id获取该项目百科的广告位数目
    :param wiki_id:
    :param ctx:
    """
    if wiki_id is None:
        return {'count': 0}
    count = WikidetailAdver.objects.filter(wiki_id=wiki_id).count()
    return {'count': count}


@bind_context(uri_pre + "/wikiadver_get")
def wikiadver_get(ctx, wikiadver_id, options=None):
    try:
        wikiadver = WikidetailAdver.objects.get(id=wikiadver_id)
    except:
        raise RPCNotFoundException
    if options is None:
        options = {
            'fields': None,
            'excludes': None,
            'expands': None,
        }
    wikiadver_data = to_dict(wikiadver, **options)
    return wikiadver_data


@bind_context(uri_pre + "/wikiadver_edit")
def wikiadver_edit(ctx, wikiadver_id=None, wikiadver_info=None):
    if wikiadver_info is None:
        return None

    wikiadver_info['wiki_id'] = wikiadver_info.pop('wiki')

    if wikiadver_id is None:
        try:
            wikiadver = WikidetailAdver.objects.create(**wikiadver_info)
        except IntegrityError:
            raise RPCIntegrityError
    else:
        try:
            wikiadver = WikidetailAdver.objects.get(id=wikiadver_id)
            for k, v in wikiadver_info.iteritems():
                setattr(wikiadver, k, v)
            wikiadver.save()
        except:
            info_logger.info(__import__('traceback').format_exc())
            raise RPCNotFoundException
    return {
        'state': True,
        'wikiadver_id': wikiadver.id
    }


@bind_context(uri_pre + "/ad_query")
def wikiadver_query(ctx, options):
    dqobj = WikidetailAdverDQ()
    return dqobj.process(**options)


@bind_context(uri_pre + '/struct_list')
def wiki_struct_list(ctx):
    """
    只返回第一个目录的所有,其他的目录只显示Id和wiki的名字
    """
    struct_first_list = WikiStruct.objects.filter(parent_id__isnull=True).order_by('sort')
    list_res = []
    if len(struct_first_list) > 0:
        list_res.append(get_struct_list(struct_first_list[0]))
        for struct_first in struct_first_list[1:len(struct_first_list)]:
            dict_first_res = {
                'id': struct_first.id, 'wiki_id': struct_first.wiki.id, 'name': struct_first.wiki.item_name
            }
            list_res.append(dict_first_res)
    return list_res


@bind_context(uri_pre + '/get_struct')
def get_wiki_struct(ctx, parent_id):
    """
    根据目录ID获取该目录的结构
    """
    if parent_id:
        struct = WikiStruct.objects.get(id=parent_id)
        content = []
        result = get_struct_list(struct)
        content.extend([item for item in get_content(struct)])
        content.append(u'目录')
        content.reverse()
        result['content_name'] = content

    else:
        result_content = WikiStruct.objects.filter(struct_level=WIKI_LEVEL.ONE).order_by('sort')
        result_item = [{
                           'id': item.id,
                           'wiki_id': item.wiki.id,
                           'name': item.wiki.item_name,
                       } for item in result_content]
        result = {
            'id': '',
            'name': u'目录',
            'wiki_id': '',
            'item': result_item,
            'content_name': [u'目录']
        }
    return result


@bind_context(uri_pre + '/edit_relation')
@transaction.atomic
def edit_relation(ctx, ids, parents_id=None):
    """
    编辑关系
    """
    if not parents_id:
        struct_level = WIKI_LEVEL.ONE

    else:
        parents = WikiStruct.objects.get(id=parents_id)
        if parents.struct_level == WIKI_LEVEL.ONE:
            struct_level = WIKI_LEVEL.TWO
        elif parents.struct_level == WIKI_LEVEL.TWO:
            struct_level = WIKI_LEVEL.THREE
        else:
            raise ValueError('error struct_level')

    old_ids = set(WikiStruct.objects.filter(struct_level=struct_level,
                                            parent_id=parents_id).values_list('wiki_id', flat=True))
    new_ids = set([long(key) for key, value in ids.items()])
    for key, value in ids.items():
        if long(key) in old_ids:
            wikistruct = WikiStruct.objects.get(wiki_id=key, parent_id=parents_id)
            wikistruct.sort = value
            wikistruct.save()
        else:
            WikiStruct.objects.create(wiki_id=key, parent_id=parents_id, struct_level=struct_level, sort=value)
    for item in (old_ids - new_ids):
        WikiStruct.objects.get(wiki_id=item, parent_id=parents_id).delete()


def get_struct_list(struct_first):
    """
    根据父类获取结构树

    """
    dict_first_res = {'id': struct_first.id, 'wiki_id': struct_first.wiki.id, 'name': struct_first.wiki.item_name}
    struct_second_list = WikiStruct.objects.filter(parent_id=struct_first.id).order_by('sort')
    res_second_list = []
    for struct_second in struct_second_list:
        dict_second_res = {'id': struct_second.id, 'wiki_id': struct_second.wiki.id,
                           'name': struct_second.wiki.item_name}
        struct_third_list = WikiStruct.objects.filter(parent_id=struct_second.id).order_by('sort')
        dict_second_res['item'] = [
            {'id': struct_third.id, 'wiki_id': struct_third.wiki.id, 'name': struct_third.wiki.item_name} for
            struct_third in
            struct_third_list]
        res_second_list.append(dict_second_res)
    dict_first_res['item'] = res_second_list
    return dict_first_res


def get_content(struct):
    """
    获取层级关系的content
    """
    while struct is not None:
        yield struct.wiki.item_name
        if struct.parent_id:
            struct = WikiStruct.objects.get(id=struct.parent_id)
        else:
            struct = None


@bind_context(uri_pre + '/optimize_query')
def optimizewiki_query(ctx, options):
    dqobj = ItemWikiDQ()
    return dqobj.process(**options)


@bind_context(uri_pre + '/get_optimize')
def get_optimize_wiki(ctx, itemwiki_id):
    try:
        itemwiki = ItemWiki.objects.get(id=itemwiki_id)
    except:
        return None

    connect_wikis_id = ConnectWiki.objects.filter(wiki_id=itemwiki_id)
    connect_wikis_ids = [obj.connectwiki_id for obj in connect_wikis_id]

    optimizewiki_list = OptimizeWiki.objects.filter(wiki_id=itemwiki_id, is_delete=False).order_by('type')
    optimizewiki_date = []
    diary_id = None
    for info in optimizewiki_list:
        info_date = to_dict(info)
        info_date['content'] = info.content
        if info_date['type'] == OPTIMIZE_WIKI_TYPE.FILES or info_date['type'] == OPTIMIZE_WIKI_TYPE.EXAMPLES:
            if info.content:
                info_date['content'] = json.loads(info.content)
                if info_date['type'] == OPTIMIZE_WIKI_TYPE.EXAMPLES:
                    diary_id = info_date['content']['diary_images']['diary_id']
        optimizewiki_date.append(info_date)
    data = {
        'item_name': itemwiki.item_name,
        'is_online': itemwiki.is_online,
        'image': itemwiki.image,
        'show_diary': itemwiki.show_diary,
        'show_service': itemwiki.show_service,
        'show_zone': itemwiki.show_zone,
        'optimizes': optimizewiki_date,
        'diary': diary_choices.func('', initial=diary_id)['results'] if diary_id else None,
        'connectwiki': itemwiki_choices.func('', initial=connect_wikis_ids)['results'],
        'image_head': itemwiki.image_head,
    }
    return data


@transaction.atomic
@bind_context(uri_pre + '/edit_optimize')
def edit_optimize(ctx, itemwiki_id=None, data=None):
    itemwiki_data = json.loads(data)
    optimizes = itemwiki_data.pop('optimizes')
    connect_wiki = itemwiki_data.pop('connectwiki')
    with transaction.atomic():
        try:
            item = ItemWiki.objects.get(id=itemwiki_id)
        except:
            item = ItemWiki.objects.create(**itemwiki_data)
        for k, v in itemwiki_data.iteritems():
            setattr(item, k, v)
        item.save()

        old_wikis = map(str, ConnectWiki.objects.filter(wiki_id=item.id).
                        values_list('connectwiki_id', flat=True))
        old_wikis = set(old_wikis)
        new_wikis = set(connect_wiki or [])
        for iw_obj in (new_wikis - old_wikis):
            ConnectWiki.objects.get_or_create(wiki_id=item.id, connectwiki_id=iw_obj)
        ConnectWiki.objects.filter(wiki_id=item.id,
                                   connectwiki_id__in=(old_wikis - new_wikis)).delete()
        OptimizeWiki.objects.filter(wiki_id=item.id).update(is_delete=True)
        for obj in optimizes:
            obj_id = obj.pop('id')
            obj.pop('wiki', None)
            obj['wiki_id'] = item.id
            if obj['type'] == OPTIMIZE_WIKI_TYPE.EXAMPLES or obj['type'] == OPTIMIZE_WIKI_TYPE.FILES:
                if obj['type'] == OPTIMIZE_WIKI_TYPE.FILES:
                    for k, v in obj['content'].iteritems():
                        setattr(item, k, v)
                        if k not in ['effect_duration_md']:
                            obj['content'][k] = str(v)
                    item.save()
                obj['content'] = json.dumps(obj['content'])

            try:
                optimizes = OptimizeWiki.objects.get(id=obj_id)
            except:
                optimizes = OptimizeWiki.objects.create(**obj)
            for k, v in obj.iteritems():
                setattr(optimizes, k, v)
            optimizes.save()
    return item.id
