# coding=utf-8
from __future__ import unicode_literals, absolute_import, print_function

import random

from django.db.models import Q
from gm_types.gaia import RANKBOARD_DATA_TYPE
from gm_types.gaia import WORLD_AREA_TYPE

from api.models import City, Tag, ServiceItem
from api.models.ranklist import RankBoard, RankboardData, RankboardDataTag, RankboardShowCity, RankboardShowRegion
from api.models.ranklist import RankboardConnect, RankBoardBatch
from rpc.decorators import bind, bind_context
from search.views.service import get_location_tag_id_by_city_id
from api.tool.geo_tool import get_city_by_options
from rpc.tool.log_tool import info_logger


def get_board_random_index(board_nums, count):
    return random.sample(xrange(board_nums), count) if board_nums >= count \
        else random.sample(xrange(board_nums), board_nums)


@bind('api/ranklist/get_rank_detail')
def get_rank_detail(rank_id):
    try:
        rank = RankBoard.objects.get(id=rank_id, is_online=True)
    except RankBoard.DoesNotExist:
        return

    data = {
        'id': rank.id,
        'type': rank.get_rank_type(),
        'banner': rank.image,
        'title': rank.title,
        'desc': rank.describe,
    }
    if rank.share:
        data.update({'share_data': rank.share.to_dict()})
    return data


def get_randboard_card_by_ids(rank_id_list):
    ranks = RankBoard.objects.filter(id__in=rank_id_list, is_online=True)
    result = [{
                  'id': rank.id,
                  'type': rank.get_rank_type(),
                  'banner': rank.image,
                  'title': rank.title,
                  'desc': rank.describe,
              } for rank in ranks] if ranks else []
    return result


@bind_context('api/ranklist/get_relate_ranklist')
def get_relate_ranklist(ctx, rank_id):
    ranks = RankboardConnect.objects.filter(rankboard_id=rank_id, to_rankboard__is_online=True)
    result = []
    for rank in ranks:
        result.append(rank.to_rankboard.get_related_data())
    return result


@bind_context('api/ranklist/get_rank_data')
def get_rank_data(ctx, rank_id, lat, lon, start_num=0, count=10):
    rank = RankBoard.objects.get(id=rank_id)
    result = []
    datas = RankboardData.objects.filter(rankboard=rank,
                                         status=RANKBOARD_DATA_TYPE.ONLINE,
                                         rankboard__is_online=True).order_by('rank')[start_num:start_num + count]
    for data in datas:
        result.append(data.get_data(lat, lon))
    return result


@bind_context('api/ranklist/get_service_relatelist')
def get_service_relate_ranklist(ctx, service_id, count):
    datas = RankboardData.objects.filter(service_id=service_id,
                                         status=RANKBOARD_DATA_TYPE.ONLINE,
                                         rankboard__is_online=True).order_by('-id')[0:count]
    result = []
    for data in datas:
        result.append(data.rankboard.get_related_data())
    return result


@bind_context('api/ranklist/get_tag_relatelist')
def get_tag_relate_ranklist(ctx, tag_ids=[], count=3):
    datas = RankboardDataTag.objects.filter(tag_id__in=tag_ids, rankboard__is_online=True).order_by('-id')[0:count]
    result = []
    for data in datas:
        result.append(data.rankboard.get_data_for_backend())
    return result


@bind_context('api/ranklist/get_tag_city_relatelist')
def get_tag_city_relate_ranklist(ctx, tag_ids=[], count=3, current_city_id=None):
    rbdts = RankboardShowCity.objects.filter(rankboard__data_tags__in=tag_ids,
                                             rankboard__is_online=True).order_by('-id')[:count]

    def _get_rank_city_info(res):
        result = []
        for r in res:
            result.append(r.rankboard.get_data_for_backend())
        return result

    if current_city_id is None:
        result = _get_rank_city_info(rbdts)
        return result

    # current_city_id is not None
    city_related_rbdts = RankboardShowCity.objects.filter(rankboard__data_tags__in=tag_ids,
                                                          city_id=current_city_id,
                                                          rankboard__is_online=True).order_by('-id')[:count]
    if city_related_rbdts:
        # 去重
        _r = list(city_related_rbdts)

        for r in rbdts:
            if r not in city_related_rbdts:
                _r.append(r)
        result = _get_rank_city_info(_r[:count])
    else:
        result = _get_rank_city_info(rbdts)
    return result


@bind('api/ranklist/query')
def search_ranklist(query, count=2, city_id=None):
    search_query = Q(title__contains=query) & Q(is_online=True)
    result = []
    city_data = []
    if city_id:
        city_query = RankBoard.build_city_query(city_id)
        if city_query:
            search_query_city = city_query & search_query
            city_data = RankBoard.objects.filter(search_query_city).order_by('-id')[0:count]

    no_city_query = RankBoard.build_no_city_query()
    no_city_query = no_city_query & search_query
    no_city_datas = RankBoard.objects.filter(no_city_query).order_by('-id')[0:count]

    datas = list(city_data) + list(no_city_datas)
    for data in datas[0:count]:
        result.append(data.get_data_for_backend())

    return result


@bind_context('api/ranklist/get_service_relatelist_for_backend')
def get_service_relate_ranklist(ctx, service_id, count):
    datas = RankboardData.objects.filter(service_id=service_id,
                                         status=RANKBOARD_DATA_TYPE.ONLINE,
                                         rankboard__is_online=True).order_by('-id')[0:count]
    result = []
    for data in datas:
        result.append(data.rankboard.get_data_for_backend())
    return result


@bind_context('api/ranklist/get_city_relatelist')
def get_city_relate_ranklist(ctx, city_id, count=7):
    if city_id in WORLD_AREA_TYPE:
        rank_board_nums = RankBoard.objects.filter(is_online=True).count()
        board_index = get_board_random_index(rank_board_nums, count)
        board_datas = [RankBoard.objects.filter(is_online=True)[i] for i in board_index]

    elif city_id:
        rank_board_nums = RankboardShowCity.objects.filter(city_id=city_id, rankboard__is_online=True).count()
        board_index = get_board_random_index(rank_board_nums, count)
        board_datas = [RankboardShowCity.objects.filter(city_id=city_id, rankboard__is_online=True)[i].rankboard
                       for i in board_index]

        if len(board_datas) < count:
            city = get_city_by_options(city_id=city_id)
            if city:
                region = city.province.region
                exist_ids = [x.id for x in board_datas]
                rank_board_nums = RankboardShowRegion.objects.filter(region=region, rankboard__is_online=True).exclude(
                    rankboard_id__in=exist_ids).count()
                board_index = get_board_random_index(rank_board_nums, count - len(board_datas))
                board_datas += [RankboardShowRegion.objects.filter(region=region, rankboard__is_online=True).exclude(
                    rankboard_id__in=exist_ids)[i].rankboard for i in board_index]
    else:
        board_datas = []

    result = []
    for data in board_datas:
        result.append(data.get_data_for_backend())

    return result


@bind('api/ranklist/get_list')
def get_list_ranklist(ids):
    rankboards = RankBoard.objects.filter(id__in=ids)
    result_dict = {}
    for b in rankboards:
        result_dict.update({b.id: b.get_data_for_backend()})
    result = [result_dict[_id] for _id in ids if result_dict.get(_id, None)]
    return result


@bind('api/aggregation_ranklist/search')
def search_ranklist(query, count=2):
    """
    聚合榜单搜索，临时解决方案
    :param query:
    :param count:
    :return:
    """
    try:
        tag = Tag.objects.get(name=query)
    except Tag.DoesNotExist:
        tag = None

    queryset = RankBoardBatch.objects.filter(is_online=True)
    if tag:
        board_ids = queryset.filter(tags=tag).values_list('id', flat=True)
        board_ids = list(board_ids)
    else:
        board_ids = []

    if len(board_ids) < 2:
        name_query = queryset.filter(name__contains=query).values_list('id', flat=True)
        board_ids += list(name_query)

    if len(board_ids) < 2:
        describe_query = queryset.filter(describe__contains=query).values_list('id', flat=True)
        board_ids += list(describe_query)

    return list(set(board_ids))[:count]


@bind('api/aggregation_ranklist/get_list')
def get_aggregation_ranklist(ids):
    rankboards = RankBoardBatch.objects.filter(id__in=ids)
    result_dict = {}
    for b in rankboards:
        result_dict.update({b.id: b.get_data_for_backend()})
    result = [result_dict[_id] for _id in ids if result_dict.get(_id, None)]
    return result


@bind_context('api/aggregation_ranklist/service')
def get_aggregation_ranklist_service(ctx, rank_id, current_city_id, lat, lng, start_num=0, count=10):
    """

    :param ctx:
    :param current_city_id:
    :param rank_id:
    :param lat:
    :param lng:
    :param start_num:
    :param count:
    :return:
    """
    _, city_tagid = get_location_tag_id_by_city_id(current_city_id)
    try:
        rank = RankBoardBatch.objects.get(id=rank_id)
    except RankBoardBatch.DoesNotExist:
        return []

    or_filters = []
    for tag in rank.tags.all():
        or_filters.append({
            'tag_id': tag.id
        })

    data = ctx.rpc_remote["doris/search/query_sku"](
        query='', user_city_tag_id=city_tagid, offset=start_num, size=count,
        or_filters=or_filters, sort_with_submission=True).unwrap()
    info_logger.info({
        'url': 'doris/search/query_sku',
        'query': '',
        'user_city_tag_id': city_tagid,
        'offset': start_num,
        'size': count,
        'or_filters': or_filters,
        'sort_with_submission': True,
        'res': data,
    })

    sku_ids = data['sku_ids']
    rank_mode = data["rank_mode"]
    services = []
    for sku_id in sku_ids:
        service_item = ServiceItem.objects.get(id=sku_id)
        service = RankBoardBatch.get_data_by_id(service_item.service_id, lat, lng)
        service['rank_mode'] = rank_mode
        services.append(service)

    return services


@bind('api/aggregation_ranklist/detail')
def get_aggregation_ranklist_detail(rank_id):
    """

    :param rank_id:
    :return:
    """
    try:
        rank = RankBoardBatch.objects.get(id=rank_id, is_online=True)
    except RankBoard.DoesNotExist:
        return

    data = {
        'id': rank.id,
        'type': rank.get_rank_type(),
        'banner': rank.image,
        'title': rank.name,
        'desc': rank.describe,
        'share_data': {
            'title': rank.share_title,
            'content': rank.share_content,
            'moments': rank.share_content,
            'weibo': rank.share_content,
            'image': rank.share_image,
        }
    }
    return data
