# coding=utf-8
import json
from datetime import *
import datetime as time
from django.db import IntegrityError
from django.db import transaction
from django.db.models import Q
from api.models import ShowCity, ShowRegion, Region, IndexFunctionCity, IndexFunctionRegion
from api.models.slide import Slide, SmallImage, ButtonGadget, ButtonSideslip, FeedOperateV2
from api.models.feed import IndexFeed
from api.models import SideSkidCategory, Tag
from api.tool.log_tool import logging_exception
from ..datatables import SlideDT, SmallimageDT
from hera.queries.sidesikidcategory import SideSkidCategoryDQ
from rpc.exceptions import RPCNotFoundException, RPCIntegrityError
from rpc.exceptions import GaiaRPCFaultException
from rpc.decorators import bind_context, bind
from rpc.tool.dict_mixin import to_dict
from rpc.tool.log_tool import info_logger
from gm_types.gaia import SLIDE_POSITION, GADGET_PAGE_TYPE, SLIDE_USER_TYPE, AppConfigValueType, FUNCTION_VERSION
from gm_types.error import ERROR
from hera.utils import model_edit, m2m_save
from ..queries.gmhome import SlideDQ, SmallImageDQ
from ..queries.gmhome import IndexFunctionDQ
from api.models.slide import TabConfigDeploy, IndextabConfig, DisplaySite, SlideToDisplaySite
from api.models import IndexFunction
from rpc.cache import gadget_cache
from hera.cache.app_config import APPConfigCache

slide_pre = 'hera/slide'
smallimage_pre = 'hera/smallimage'
uri_pre = 'hera/gmhome'
uri_city = 'hera/slide_city'
uri_sideskid = 'hera/sideskid'
uri_function = 'hera/index_function'


@bind_context(slide_pre + '/query')
def slide_query(ctx, options):
    dqobj = SlideDQ()
    return dqobj.process(**options)


@bind_context(slide_pre + '/list')
def slide_datatable(request, req_data, vers):
    dtobj = SlideDT(Slide)
    return dtobj.process(req_data)


@bind_context(slide_pre + '/get_visible')
def get_slide_visible(ctx, position=SLIDE_POSITION.FIRST):
    return {'visible': Slide.get_visible(position=position)}


@bind_context(slide_pre + '/set_visible')
def set_slide_visible(ctx, visible=True, position=SLIDE_POSITION.FIRST):
    Slide.set_visible(position=position, visible=visible)


@bind_context(slide_pre + '/get')
def slide_detail(ctx, slide_id, options=None):
    try:
        slide = Slide.objects.get(id=slide_id)
    except:
        raise RPCNotFoundException
    if options is None:
        options = {
            'fields': None,
            'excludes': None,
            'expands': None,
        }
    data = to_dict(slide, **options)
    data['region'] = list(slide.showregion.all().values_list('region_id', flat=True))
    data['display_types'] = list(SlideToDisplaySite.objects.filter(slide_id=slide_id).values_list(
        'display_site__display_type', flat=True
    ))
    data['display_types'] = [int(i) for i in data['display_types']]
    if data['question']:
        data['question_id'] = data['question']['id']
        data['question'] = data['question']['id']
    if data['answer']:
        data['answer_id'] = data['answer']['id']
        data['answer'] = data['answer']['id']
    return data


@transaction.atomic()
@bind_context(slide_pre + '/edit')
def slide_edit(ctx, slide_id=None, slide_info=None):
    if slide_info is None:
        return None

    slide_info['service_id'] = slide_info.pop('service') or None
    slide_info['tag_id'] = slide_info.pop('tag') or None
    slide_info['special_id'] = slide_info.pop('special') or None
    slide_info['zone_single_id'] = slide_info.pop('zone_single') or None
    slide_info['question'] = slide_info.pop('question_id') or None
    slide_info['answer'] = slide_info.pop('answer_id') or None
    slide_info['content_polymer_id'] = slide_info.pop('content_polymer_id') or None
    for key in [
        'diary_id', 'topic_id', 'activity_id', 'zhiboconfig_id', 'column_id',
        'new_seckill_id', 'seckill_polymer_id', 'visualpage_id', 'promotion_id'
    ]:
        slide_info[key] = slide_info.pop(key, None) or None
    region = slide_info.pop('region')
    display_types = slide_info.pop('display_types', [])
    if 'cities' in slide_info.keys():
        slide_info.pop('cities')

    weight = slide_info.get('weight', 0)
    if not weight:
        slide_info['weight'] = 1
    if slide_id is None:
        try:
            slide = Slide.objects.create(**slide_info)
        except IntegrityError:
            logging_exception()
            raise RPCIntegrityError
    else:
        try:
            slide = Slide.objects.get(id=slide_id)
            for k, v in slide_info.iteritems():
                setattr(slide, k, v)
            slide.save()
        except:
            info_logger.info(__import__('traceback').format_exc())
            raise RPCNotFoundException
    # 轮播图展示位置绑定 7760
    display_site_ids = DisplaySite.objects.filter(
        display_type__in=display_types).values_list('id', flat=True)
    if display_site_ids:
        SlideToDisplaySite.objects.filter(slide_id=slide.id).delete()
        SlideToDisplaySite.objects.bulk_create([
            SlideToDisplaySite(slide_id=slide.id, display_site_id=item)
            for item in display_site_ids]
        )
    new_region = set(Region.objects.filter(id__in=region))
    old_region = set(Region.objects.filter(showregion__in=slide.showregion.all()))
    for region in (new_region - old_region):
        ShowRegion.objects.get_or_create(slide_id=slide.id, region=region)
    ShowRegion.objects.filter(slide_id=slide.id, region__in=(old_region-new_region)).delete()
    return slide.id


@bind_context(slide_pre + '/replication_slide')
def replication_slide(ctx, slide_id):
    """复制轮播图"""
    new_slide = None
    slide = Slide.objects.get(id=slide_id)
    display_site_ids = SlideToDisplaySite.objects.filter(
        slide_id=slide.id).values_list('display_site_id', flat=True)
    region_ids = ShowRegion.objects.filter(
        slide_id=slide.id).values_list('region_id', flat=True)
    with transaction.atomic():
        try:
            new_slide_data = to_dict(slide,
                    fields=[
                        "slide_type",
                        "service_type",
                        "slide_layout",
                        "slide_img",
                        "new_slide_img",
                        "app_new_slide_img",
                        "slide_banner_img",
                        "color",
                        "slide_img_big",
                        "slide_img1",
                        "topic_id",
                        "service_id",
                        "diary_id",
                        "zone_single_id",
                        "url",
                        "ordering",
                        "created_time",
                        "effect_time",
                        "end_time",
                        "is_online",
                        "tag_id",
                        "special_id",
                        "activity_id",
                        "position",
                        "title",
                        "is_show_info",
                        "waterfalls_pic",
                        "user_id",
                        "zhiboconfig_id",
                        "not_show_title",
                        "show_as_diary",
                        "column_id",
                        "content_polymer_id",
                        "question",
                        "answer",
                        "location",
                        "user_type",
                        "payment_type",
                        "new_seckill_id",
                        "seckill_polymer_id",
                        "visualpage_id",
                        "version",
                        "promotion_id",
                        "event_type",
                        "weight",
                        "device_type"
                    ])
            new_slide = Slide.objects.create(**new_slide_data)
            stod_objs = [SlideToDisplaySite(slide=new_slide, display_site_id=display_site_id)
                         for display_site_id in display_site_ids]
            SlideToDisplaySite.objects.bulk_create(stod_objs)

            re_objs = [ShowRegion(slide=new_slide, region_id=region_id)
                         for region_id in region_ids]
            ShowRegion.objects.bulk_create(re_objs)
        except Exception as e:
            logging_exception()
            raise e

    return {'id': new_slide.id}


@bind_context(slide_pre + '/delete')
def slide_delete(ctx, slide_id):
    try:
        Slide.objects.get(pk=slide_id).delete()
    except:
        return None

    return slide_id


@bind_context(slide_pre + '/listupdate')
def slide_listupdate(ctx, items):
    info = []
    for obj in items:
        slide = Slide.objects.get(pk=obj['key'])
        slide.is_online = obj['is_online']
        slide.ordering = obj['rank']
        weight = obj.get('weight')
        if weight:
            slide.weight = weight
        slide.save()
        info.append(obj['key'])
    return info

@transaction.atomic()
@bind_context(slide_pre + '/replication')
def slide_replication(ctx, slide_id=None):
    #品类聚合banner复制
    with transaction.atomic():
        old_slide = Slide.objects.get(id=slide_id)
        slide = Slide.objects.create(
            slide_type=old_slide.slide_type,
            service_type=old_slide.service_type,
            slide_layout=old_slide.slide_layout,
            slide_img=old_slide.slide_img,
            new_slide_img=old_slide.new_slide_img,
            app_new_slide_img=old_slide.app_new_slide_img,
            slide_banner_img=old_slide.slide_banner_img,
            color=old_slide.color,
            slide_img_big=old_slide.slide_img_big,
            slide_img1=old_slide.slide_img1,
            topic_id=old_slide.topic_id,
            service=old_slide.service,
            diary_id=old_slide.diary_id,
            zone_single=old_slide.zone_single,
            url=old_slide.url,
            ordering=old_slide.ordering,
            effect_time=old_slide.effect_time,
            end_time=old_slide.end_time,
            is_online=old_slide.is_online,
            tag=old_slide.tag,
            special=old_slide.special,
            activity_id=old_slide.activity_id,
            position=old_slide.position,
            title=old_slide.title,
            is_show_info=old_slide.is_show_info,
            waterfalls_pic=old_slide.waterfalls_pic,
            user=old_slide.user,
            zhiboconfig_id=old_slide.zhiboconfig_id,
            not_show_title=old_slide.not_show_title,
            show_as_diary=old_slide.show_as_diary,
            column_id=old_slide.column_id,
            content_polymer_id=old_slide.content_polymer_id,
            question=old_slide.question,
            answer=old_slide.answer,
            location=old_slide.location,
            user_type=old_slide.user_type,
            payment_type=old_slide.payment_type,
            new_seckill_id=old_slide.new_seckill_id,
            seckill_polymer_id=old_slide.seckill_polymer_id,
            device_type=old_slide.device_type
        )
        display_site_ids = SlideToDisplaySite.objects.filter(slide_id=slide_id).values_list('display_site_id', flat=True)
        region_ids = ShowRegion.objects.filter(slide_id=slide_id).values_list('region_id', flat=True)
        city_ids = ShowCity.objects.filter(slide_id=slide_id).values_list('city_id', flat=True)

        if display_site_ids:
            SlideToDisplaySite.objects.bulk_create([
                SlideToDisplaySite(slide_id=slide.id, display_site_id=display_site_id)
                for display_site_id in display_site_ids]
            )
        if region_ids:
            ShowRegion.objects.bulk_create([
                ShowRegion(slide_id=slide.id, region_id=region_id)
                for region_id in region_ids]
            )
        if city_ids:
            ShowCity.objects.bulk_create([
                ShowCity(slide_id=slide.id, city_id=city_id)
                for city_id in city_ids]
            )
    return {'id': slide.id}


@bind_context(smallimage_pre + '/query')
def smallimage_query(ctx, options):
    dqobj = SmallImageDQ()
    return dqobj.process(**options)


@bind_context(smallimage_pre + '/list')
def smallimage_datatable(request, req_data):
    dtobj = SmallimageDT(SmallImage)
    return dtobj.process(req_data)


@bind_context(smallimage_pre + '/get')
def smallimage_detail(ctx, smallimage_id, options=None):
    try:
        smallimage = SmallImage.objects.get(id=smallimage_id)
    except:
        raise RPCNotFoundException
    if options is None:
        options = {
            'fields': None,
            'excludes': None,
            'expands': None,
        }
    data = to_dict(smallimage, **options)
    return data


@bind_context(smallimage_pre + '/edit')
def smallimage_edit(ctx, smallimage_id=None, smallimage_info=None):
    if smallimage_info is None:
        return None

    smallimage_info['service_id'] = smallimage_info.pop('service')
    smallimage_info['special_id'] = smallimage_info.pop('special')
    smallimage_info['tag_id'] = smallimage_info.pop('tag')
    for key in ['diary_id', 'topic_id', 'activity_id']:
        smallimage_info[key] = smallimage_info.pop(key) or None

    if 'cities' in smallimage_info.keys():
        smallimage_info.pop('cities')
    if smallimage_id is None:
        try:
            smallimage = SmallImage.objects.create(**smallimage_info)
        except IntegrityError:
            raise RPCIntegrityError
    else:
        try:
            smallimage = SmallImage.objects.get(id=smallimage_id)
            for k, v in smallimage_info.iteritems():
                setattr(smallimage, k, v)
            smallimage.save()
        except:
            info_logger.info(__import__('traceback').format_exc())
            raise RPCNotFoundException

    return smallimage.id


@bind_context(smallimage_pre + '/listupdate')
def smallimage_listupdate(ctx, items):
    info = []
    for obj in items:
        smallimage = SmallImage.objects.get(pk=obj['key'])
        smallimage.is_online = obj['is_online']
        smallimage.ordering = obj['rank']
        smallimage.save()
        info.append(obj['key'])
    return info


@bind_context(smallimage_pre + '/get_visible')
def get_slide_visible(ctx):
    return SmallImage.get_visible()


@bind_context(smallimage_pre + '/set_visible')
def set_slide_visible(ctx, visible=True):
    SmallImage.set_visible(visible=visible)


@bind_context(uri_pre + '/get_button')
def get_button_layout(ctx):
    return ButtonGadget.get_buttons(page_type=GADGET_PAGE_TYPE.INDEX)


@bind_context(uri_pre + '/set_button')
def set_button_layout(ctx, layout_info):
    ButtonGadget.set_buttons(
        page_type=GADGET_PAGE_TYPE.INDEX,
        button_list=layout_info['button_list'],
        button_list_visible=layout_info['button_list_visible'])


@bind_context(uri_pre + '/sideslip/get_button')
def get_button_sideslip(ctx):
    return ButtonSideslip.get_buttons(page_type=0)


@bind_context(uri_pre + '/sideslip/set_button')
def set_button_sideslip(ctx, sideslip_info):
    ButtonSideslip.set_buttons(
        page_type=0,
        button_list=sideslip_info['button_list'])


@bind_context(uri_city + '/edit')
def edit_slide_city(ctx, slide_id, cities_info):
    if slide_id is None:
        return None
    old_cities = set(ShowCity.objects.filter(slide=slide_id))
    new_cities = set(ShowCity.objects.get_or_create(city_id=item,
                                                    slide_id=slide_id
                                                    )[0] for item in cities_info)
    for item in (old_cities - new_cities):
        item.delete()


@bind_context(uri_city + '/get')
def get_slide_city(ctx, slide_id):
    if slide_id is None:
        return None
    cities = ShowCity.objects.filter(slide=slide_id)
    list_cities = []
    for city in cities:
        list_cities.append(city.city_id)
    return list_cities


@bind_context(uri_sideskid + '/get')
def sideskidcategory_detail(ctx, obj_id, options=None):
    try:
        obj = SideSkidCategory.objects.get(id=obj_id)
    except:
        raise RPCNotFoundException
    if options is None:
        options = {
            'fields': None,
            'excludes': None,
            'expands': None,
        }
    data = to_dict(obj, **options)
    data['polymer_id'] = obj.polymer_id
    return data


@bind_context(uri_sideskid + '/edit')
def sideskidcategory_edit(ctx, obj_id=None, obj_info=None):
    if obj_info is None:
        return None
    tag_ids = obj_info.pop('tags', [])
    if obj_info['is_online'] and obj_info['ordering'] is not None \
            and SideSkidCategory.objects.exclude(id=obj_id).filter(ordering=obj_info['ordering'], is_online=True):
        raise GaiaRPCFaultException(error=1, message='这个位置已有其它分类', data=None)
    obj = model_edit(SideSkidCategory, obj_id, obj_info)
    # process tags related
    m2m_save(Tag, obj.tags, tag_ids)

    return {'id': obj.id}


@bind_context(uri_sideskid + '/query')
def sideskidcategory_query(ctx, options):
    dqobj = SideSkidCategoryDQ()
    return dqobj.process(**options)


@bind_context(uri_pre + '/get_tag_config')
def set_tag_icon(ctx, req_source=None):
    rkey = 'tabconfigdeploy{}'.format(req_source or '')
    return TabConfigDeploy.get_deploy(rkey)


@bind_context(uri_pre + '/set_tag_config')
def set_tag_icon(ctx, tab_config, req_source=None):
    rkey = 'tabconfigdeploy{}'.format(req_source or '')
    TabConfigDeploy.set_deploy(tab_config, rkey)


@bind_context(uri_pre + '/get_indextab_config')
def set_tag_icon(ctx):
    return IndextabConfig.get()


@bind_context(uri_pre + '/set_indextab_config')
def set_tag_icon(ctx, tab_config):
    IndextabConfig.set(tab_config)


@bind_context(uri_function + '/query')
def function_query(ctx, options):
    dqobj = IndexFunctionDQ()
    filters = options.get("filters", [])
    filters.append({
        "field": "version",
        "value": FUNCTION_VERSION.V1,
        "regex": False,
    })
    options["filters"] = filters
    return dqobj.process(**options)


@bind_context(uri_function + '/query/v2')
def function_query(ctx, options):
    dqobj = IndexFunctionDQ()
    filters = options.get("filters", [])
    filters.append({
        "field": "version",
        "value": FUNCTION_VERSION.V2,
        "regex": False,
    })
    options["filters"] = filters
    return dqobj.process(**options)


@bind_context(uri_function + '/get_visible')
def get_function_visible(ctx, version=FUNCTION_VERSION.V1):
    return {'visible': IndexFunction.get_visible(version=version)}


@bind_context(uri_function + '/set_visible')
def set_function_visible(ctx, visible=True, version=FUNCTION_VERSION.V1):
    IndexFunction.set_visible(visible=visible, version=version)


@bind_context(uri_function + '/edit')
def index_function_edit(ctx, id=None, data_info=None, version=FUNCTION_VERSION.V1):

    if version == FUNCTION_VERSION.V2 and data_info['is_online']:
        start_time = data_info["start_time"]
        end_time = data_info.get("end_time", None)
        check_time(end_time, start_time, version, id)
    cities_info = data_info.pop('cities')
    regions_info = data_info.pop('region')
    if id is None:
        data_info["version"] = version
        obj = IndexFunction.objects.create(**data_info)
    else:
        obj = IndexFunction.objects.get(id=id)
        for k, v in data_info.iteritems():
            setattr(obj, k, v)
        obj.save()
    #城市和大区的更新
    old_cities = set(IndexFunctionCity.objects.filter(indexfunction=obj))
    new_cities = set(IndexFunctionCity.objects.get_or_create(city_id=item, indexfunction_id=obj.id)[0] for item in cities_info)
    for item in (old_cities - new_cities):
        item.delete()
    old_region = set(IndexFunctionRegion.objects.filter(indexfunction=obj))
    new_region = set(IndexFunctionRegion.objects.get_or_create(region_id=item, indexfunction_id=obj.id)[0] for item in regions_info)
    for item in (old_region - new_region):
        item.delete()
    return obj.id


@bind_context(uri_function + '/get_index_function')
def index_function_edit(ctx, index_id=None, version=None):
    """
    返回首页功能区指定配置项
    hera/index_function/get_index_function
    """
    if not index_id:
        index_id = 2
    obj = IndexFunction.objects.filter(id=index_id)  # 老用户配置项
    return json.loads(obj[0].card_data)[3] if obj else None  # 第四个配置项为新人一元秒杀


def check_time(end_time, start_time, version, id):

    # TODO: 根据版本检测时间冲突
    return

    exclude = Q(id=id) if id else Q()
    ids = IndexFunction.objects.filter(
        Q(is_online=True, version=version) & Q(end_time__isnull=True)
    ).exclude(exclude).values_list("id", flat=True)

    if ids:
        pass

    elif not end_time:
        ids = IndexFunction.objects.filter(
            Q(is_online=True, version=version) &
            Q(end_time__gt=start_time, start_time__lt=start_time)
        ).exclude(exclude).values_list("id", flat=True)

    else:
        ids = IndexFunction.objects.filter(
            Q(is_online=True, version=version) &
            (Q(end_time__gt=start_time, start_time__lt=start_time) | Q(end_time__gt=end_time,
                                                                         start_time__lt=end_time))
        ).exclude(exclude).values_list("id", flat=True)

    if ids:
        ids = ", ".join(map(lambda i: str(i), ids))
        raise GaiaRPCFaultException(error=1, message='当前配置与配置ID为：{0} 的时间冲突'.format(ids), data=None)


@bind('doctor/ssc/list')
def ssc_list():
    result = [{
        'id': ssc.id,
        'name': ssc.name
    } for ssc in SideSkidCategory.objects.filter(is_online=True)]
    return result


@bind(uri_sideskid + '/ssc/list')
def ssc_list():
    result = [{
        'id': ssc.id,
        'name': ssc.name
    } for ssc in SideSkidCategory.objects.filter(is_online=True)]
    return result


@bind(uri_sideskid + '/getname_byid')
def ssc_get_namebyid(ssc_id):
    ssc = SideSkidCategory.objects.get(id=ssc_id)
    return {'id': ssc.id, 'name': ssc.name}

@transaction.atomic()
@bind_context(slide_pre + '/feedoperatev2_replication')
def feedoperatev2_replication(ctx, feedoperatev2_id=None):
    #feeds流插入复制
    with transaction.atomic():
        old_feedoperatev2 = FeedOperateV2.objects.get(id=feedoperatev2_id)
        feedoperatev2 = FeedOperateV2.objects.create(
            name=old_feedoperatev2.name,
            location=old_feedoperatev2.location,
            tab_id=old_feedoperatev2.tab_id,
            image=old_feedoperatev2.image,
            service_type=old_feedoperatev2.service_type,
            show_type=old_feedoperatev2.show_type,
            card_type=old_feedoperatev2.card_type,
            card_id=old_feedoperatev2.card_id,
            user_type=old_feedoperatev2.user_type,
            payment_type=old_feedoperatev2.payment_type,
            is_online=old_feedoperatev2.is_online,
            rank=old_feedoperatev2.rank,
            width=old_feedoperatev2.width,
            height=old_feedoperatev2.height,
            start_time=old_feedoperatev2.start_time,
            end_time=old_feedoperatev2.end_time,
            version=old_feedoperatev2.version,
        )
        feedoperatev2.regions = list(old_feedoperatev2.regions.values_list('id', flat=True))
        feedoperatev2.cities = list(old_feedoperatev2.cities.values_list('id', flat=True))
    return {'id': feedoperatev2.id}



@bind('hera/index_publish/get')
def get_index_publish():
    app_config = APPConfigCache(gadget_cache, 'index_publish')
    res = app_config.get_config_from_cache()
    res = json.loads(res or "{}")
    if not res:
        res = {}
    return res


@bind('hera/index_publish/set')
def set_index_publish(config_info):
    config_info = json.dumps(config_info)
    app_config = APPConfigCache(gadget_cache, 'index_publish')
    app_config.update_app_config(config_info, AppConfigValueType.TYPE_JSON, '首页发布配置')
    app_config.set_config_cache(config_info)


@bind('hera/first_card_config/get')
def get_index_card_one():
    app_config = APPConfigCache(gadget_cache, 'index_first_card')
    res = app_config.get_config_from_cache()
    res = json.loads(res or "{}")
    if not res:
        res = {}
    return res


@bind('hera/first_card_config/set')
def set_index_card_one(config_info):
    config_info = json.dumps(config_info)
    app_config = APPConfigCache(gadget_cache, 'index_first_card')
    app_config.update_app_config(config_info, AppConfigValueType.TYPE_JSON, '首页1号位配置')
    app_config.set_config_cache(config_info)
