# encoding=utf-8
from datetime import datetime
from django.db import IntegrityError, transaction
from gm_types.gaia import TAG_TYPE

from rpc.decorators import bind_context
from rpc.exceptions import RPCIntegrityError, RPCNotFoundException, GaiaRPCFaultException
from rpc.tool.dict_mixin import to_dict
from hera.queries.ranklist import RankListDQ
from api.models.ranklist import *
from rpc.all import get_rpc_remote_invoker

uri_pre = 'hera/ranklist'


@bind_context(uri_pre + '/query')
def ranklist_query(ctx, options):
    dtobj = RankListDQ()
    return dtobj.process(**options)


@bind_context(uri_pre + '/list_update')
def ranklist_listupdate(ctx, items):
    result = []
    for item in items:
        ranklist = RankList.objects.get(id=item['id'])
        ranklist.rank = item['rank']
        ranklist.is_online = item['is_online']
        ranklist.save()
        result.append(ranklist.id)
    return result


@bind_context(uri_pre + '/get')
def ranklist_detail(ctx, ranklist_id, options=None):
    try:
        ranklist = RankList.objects.get(id=ranklist_id)
    except:
        raise RPCNotFoundException
    if options is None:
        options = {
            'fields': None,
            'excludes': None,
            'expands': None,
        }
    data = to_dict(ranklist, **options)
    rankservices = RankListService.objects.filter(rank_list_id=ranklist.id)
    data['serviceinfo'] = []
    for rankservice in rankservices:
        service_data = {}
        service = rankservice.service
        service_data['id'] = rankservice.service_id
        service_data['name'] = service.name
        service_data['is_online'] = '上线' if service.is_online else '下线'
        service_data['doctor_id'] = service.doctor_id
        service_data['doctor_name'] = service.doctor.name
        service_data['hospital_name'] = service.doctor.hospital.name
        service_data['message'] = rankservice.message
        service_data['rank'] = rankservice.rank
        data['serviceinfo'].append(service_data)
    return data


@transaction.atomic
@bind_context(uri_pre + '/edit')
def ranklist_edit(ctx, ranklist_id=None, ranklist_info=None):
    ranklist_info['special_id'] = ranklist_info.pop('special')
    service_info = ranklist_info.pop('service_info')
    try:
        with transaction.atomic():
            if ranklist_id is None:
                try:
                    ranklist = RankList.objects.create(**ranklist_info)
                except IntegrityError:
                    raise RPCIntegrityError
            else:
                try:
                    ranklist = RankList.objects.get(id=ranklist_id)
                except:
                    raise RPCNotFoundException
                for k, v in ranklist_info.iteritems():
                    setattr(ranklist, k, v)
                ranklist.save()

            new_services = []
            services_res = []
            for service_data in service_info:
                rankservice, _ = RankListService.objects.get_or_create(rank_list_id=ranklist.id,
                                                                       service_id=service_data['id'])
                rankservice.message = service_data['message']
                rankservice.rank = service_data['rank']
                rankservice.save()
                new_services.append(rankservice.service_id)
                if rankservice.service.end_time and rankservice.service.end_time < ranklist.special.end_time:
                    services_res.append(u'{}有效期不足'.format(rankservice.service.name))
                if not rankservice.service.is_online:
                    services_res.append(u'{}已下线'.format(rankservice.service.name))
            if services_res:
                raise ValueError
        RankListService.objects.filter(rank_list_id=ranklist.id).exclude(service_id__in=new_services).delete()
    except ValueError:
        return {
            'code': 0,
            'message': ','.join(services_res)
        }
    return {
        'code': 1,
        'id': ranklist.id
    }


@bind_context(uri_pre + '/add_serivice')
def add_serivice(ctx, service_ids):
    serivices_info = []
    for service_id in service_ids:
        service_data = {}
        service = Service.objects.get(id=service_id)
        service_data['id'] = service.id
        service_data['name'] = service.name
        service_data['is_online'] = '上线' if service.is_online else '下线'
        service_data['doctor_id'] = service.doctor_id
        service_data['doctor_name'] = service.doctor.name
        service_data['hospital_name'] = service.doctor.hospital.name
        serivices_info.append(service_data)
    return serivices_info


@transaction.atomic
@bind_context(uri_pre + '/rankboard/edit')
def rankboard_edit(ctx, rankboard_id, rankboard_info):
    show_regions = rankboard_info.pop('show_regions')
    show_cities = rankboard_info.pop('show_cities')
    data_item_tags = rankboard_info.pop('data_item_tags')
    data_city_tags = rankboard_info.pop('data_city_tags')
    to_rankboards = rankboard_info.pop('to_rankboards')
    data_tags = data_item_tags + data_city_tags

    if rankboard_id is None:
        rankboard = RankBoard.objects.create(**rankboard_info)
    else:
        rankboard = RankBoard.objects.get(id=rankboard_id)
        if rankboard_info['is_online'] and \
                not rankboard.rankboarddata_set.filter(status=RANKBOARD_DATA_TYPE.ONLINE).exists():
            message = u'榜单数据池为空，榜单不允许上线'
            raise GaiaRPCFaultException(error=1, message=message, data=None)
        for k, v in rankboard_info.iteritems():
            setattr(rankboard, k, v)
        rankboard.save()

    # many to many fields change
    chage_dict = {
        'RankboardShowRegion': {
            'val': 'show_regions',
            'col': 'region_id',
        },
        'RankboardShowCity': {
            'val': 'show_cities',
            'col': 'city_id',
        },
        'RankboardDataTag': {
            'val': 'data_tags',
            'col': 'tag_id',
        },
        'RankboardConnect': {
            'val': 'to_rankboards',
            'col': 'to_rankboard_id',
        },
    }
    for k, v in chage_dict.items():
        new_val = set(eval(v['val']))
        m_val = getattr(rankboard, v['val']).values_list('id', flat=True)
        old_val = set(map(str, m_val))
        m = eval(k)
        for sv in new_val - old_val:
            m.objects.get_or_create(**{'rankboard_id': rankboard.id, v['col']: sv})
        m.objects.filter(**{'rankboard_id': rankboard.id,
                            '{}__in'.format(v['col']): old_val - new_val}).delete()
    return {
        'id': rankboard.id,
    }


@transaction.atomic
@bind_context(uri_pre + '/rankboard/data/action')
def rankboard_data_action(ctx, rankboard_id, action, items):
    rankboard = RankBoard.objects.get(id=rankboard_id)
    if action == 'add_data':
        if rankboard.type == RANKBOARD_TYPE.SERVICE:
            [RankboardData.objects.get_or_create(
                rankboard_id=rankboard_id, service_id=obj) for obj in items]
        if rankboard.type == RANKBOARD_TYPE.HOSPITAL:
            [RankboardData.objects.get_or_create(
                rankboard_id=rankboard_id, hospital_id=obj) for obj in items]
        if rankboard.type == RANKBOARD_TYPE.DOCTOR:
            [RankboardData.objects.get_or_create(
                rankboard_id=rankboard_id, doctor_id=obj) for obj in items]
    if action == 'put':
        RankboardData.objects.filter(id__in=items).update(status=RANKBOARD_DATA_TYPE.DRAFT)
    if action == 'delete':
        RankboardData.objects.filter(id__in=items).delete()
    if action == 'online_data':
        old_val = set(rankboard.rankboarddata_set.exclude(
            status=RANKBOARD_DATA_TYPE.NORMAL
        ).values_list('id', flat=True))
        new_val = set()
        for item in items:
            data = RankboardData.objects.get(id=item['id'])
            data.describe = item['describe']
            data.rank = item['rank']
            data.status = RANKBOARD_DATA_TYPE.ONLINE
            data.save()
            new_val.add(data.id)
        RankboardData.objects.filter(id__in=old_val - new_val).update(status=RANKBOARD_DATA_TYPE.NORMAL)
    if action == 'delete_all':
        RankboardData.objects.filter(rankboard_id=rankboard_id, status=RANKBOARD_DATA_TYPE.NORMAL).delete()
    if action == 'get_value':
        r = get_rpc_remote_invoker()
        tag_ids = list(rankboard.data_tags.filter(tag_type__in=[
            TAG_TYPE.BODY_PART, TAG_TYPE.BODY_PART_SUB_ITEM, TAG_TYPE.ITEM_WIKI
        ]).values_list('id', flat=True))
        city_tag_ids = list(rankboard.data_tags.filter(
            tag_type=TAG_TYPE.CITY
        ).values_list('id', flat=True))
        filters = {
            'tag_ids': tag_ids,
            'city_tag_ids': city_tag_ids,
        }
        if rankboard.type == RANKBOARD_TYPE.SERVICE:
            result = r['api/service/filter_v2'](count=30, filters=filters).unwrap()
            for obj in result['service_list']:
                RankboardData.objects.get_or_create(rankboard_id=rankboard.id,
                                                    service_id=obj['service_id'])
        if rankboard.type == RANKBOARD_TYPE.DOCTOR:
            result = r['api/doctor/filter'](limit=30, filters=filters).unwrap()
            for obj in result:
                RankboardData.objects.get_or_create(rankboard_id=rankboard.id,
                                                    doctor_id=obj['did'])
        if rankboard.type == RANKBOARD_TYPE.HOSPITAL:
            result = r['api/doctor/officer_filter'](limit=30, filters=filters).unwrap()
            for obj in result:
                RankboardData.objects.get_or_create(rankboard_id=rankboard.id,
                                                    hospital_id=obj['hospital_id'])
        rankboard.datapool_last_modified = datetime.now()
        rankboard.save()
