# -*- coding: UTF-8 -*-

from django.db import IntegrityError
from django.db.models import Q
from django.db import transaction
from django.conf import settings
import pinyin

from agile.models.tag import TagMapCity
from rpc.decorators import bind_context
from rpc.exceptions import RPCIntegrityError, RPCNotFoundException
from rpc.tool.dict_mixin import to_dict
from api.models.area import Country, Province, City, Area, Region
from rpc.tool.datatables import DataTable
from rpc.tool.log_tool import info_logger
from ..datatables import CityDT
from ..queries.area import CountryDQ, CityDQ, ProvinceDQ, RegionDQ

country_pre = 'hera/country'
province_pre = 'hera/province'
city_pre = 'hera/city'
area_pre = 'hera/area'
region_pre = 'hera/region'


@bind_context(country_pre + '/query')
def country_query(ctx, options):
    dqobj = CountryDQ()
    return dqobj.process(**options)


@bind_context(city_pre + '/query')
def city_query(ctx, options):
    dqobj = CityDQ()
    return dqobj.process(**options)


@bind_context(province_pre + '/query')
def province_query(ctx, options):
    dqobj = ProvinceDQ()
    return dqobj.process(**options)


@bind_context(country_pre + '/list')
def country_datatable(ctx, req_data):
    dtobj = DataTable(Country)
    return dtobj.process(req_data)


@bind_context(country_pre + '/get')
def country_detail(ctx, country_id, options=None):
    try:
        country = Country.objects.get(id=country_id)
    except:
        raise RPCNotFoundException
    if options is None:
        options = {
            'fields': None,
            'excludes': None,
            'expands': None,
        }
    data = to_dict(country, **options)
    return data


@bind_context(country_pre + '/choices')
def country_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(name__contains=q)
    query = Country.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': obj.name,
        } for obj in query[start_pos: start_pos + num]
    ]
    return {'total_count': total_count, 'results': results, 'page': page, 'num': num}


@bind_context(country_pre + '/edit')
def country_edit(ctx, country_id=None, country_info=None):
    if country_info is None:
        return None

    country_info['tag_id'] = country_info.pop('tag')

    if country_id is None:
        try:
            country = Country.objects.create(**country_info)
        except IntegrityError:
            raise RPCIntegrityError
    else:
        try:
            country = Country.objects.get(id=country_id)
            for k, v in country_info.iteritems():
                setattr(country, k, v)
            country.save()
        except:
            info_logger.info(__import__('traceback').format_exc())
            raise RPCNotFoundException

    return country.id


@bind_context(province_pre + '/list')
def province_datatable(ctx, req_data):
    dtobj = DataTable(Province)
    return dtobj.process(req_data)


@bind_context(province_pre + '/get')
def province_detail(ctx, province_id, options=None):
    try:
        province = Province.objects.get(id=province_id)
    except:
        raise RPCNotFoundException
    if options is None:
        options = {
            'fields': None,
            'excludes': None,
            'expands': None,
        }
    data = to_dict(province, **options)
    return data


@bind_context(province_pre + '/edit')
def province_edit(ctx, province_id=None, province_info=None):
    if province_info is None:
        return None

    province_info['country_id'] = province_info.pop('country')
    province_info['tag_id'] = province_info.pop('tag')

    if province_id is None:
        try:
            province_info['id'] = pinyin.get(province_info.get('name'))
            province = Province.objects.create(**province_info)
        except IntegrityError:
            raise RPCIntegrityError
    else:
        try:
            province = Province.objects.get(id=province_id)
            for k, v in province_info.iteritems():
                setattr(province, k, v)
            province.save()
        except:
            info_logger.info(__import__('traceback').format_exc())
            raise RPCNotFoundException

    return province.id


@bind_context(city_pre + '/list')
def city_datatable(ctx, req_data):
    dtobj = CityDT(City)
    return dtobj.process(req_data, ['id', 'name', 'province__name'])


@bind_context(city_pre + '/get')
def city_detail(ctx, city_id, options=None):
    try:
        city = City.objects.get(id=city_id)
    except:
        raise RPCNotFoundException
    related_tag_v3 = list(TagMapCity.objects.using(settings.SLAVE_DB_NAME).filter(
            city_id=city.id,
    ).values_list('tag_id', flat=True))
    if options is None:
        options = {
            'fields': None,
            'excludes': None,
            'expands': None,
        }
    data = to_dict(city, **options)
    data['tag_v3'] = related_tag_v3

    return data


@bind_context(province_pre + '/choices')
def province_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(name__contains=q)
    query = Province.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': obj.name,
        } for obj in query[start_pos: start_pos + num]
    ]
    return {'total_count': total_count, 'results': results, 'page': page, 'num': num}


@bind_context(province_pre + '/specialchoices')
def province_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(country_id=settings.COUNTRY_CHINA_ID)
    query = Province.objects.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': obj.name,
        } for obj in query[start_pos: start_pos + num]
    ]
    return {'total_count': total_count, 'results': results, 'page': page, 'num': num}


@bind_context(city_pre + '/edit')
def city_edit(ctx, city_id=None, city_info=None):
    if city_info is None:
        return None

    city_info['province_id'] = city_info.pop('province')
    city_info['tag_id'] = city_info.pop('tag')
    band_tag_ids = city_info.pop('tag_v3', [])

    tag_map = []
    if city_id is None:
        try:
            city_info['id'] = pinyin.get_pinyin(city_info['name'])
            city = City.objects.create(**city_info)
        except IntegrityError:
            raise RPCIntegrityError

        for _tag_id in band_tag_ids:
            tag_map.append(
                TagMapCity(
                    tag_id=_tag_id,
                    city_id=city.id,
                )
            )
        TagMapCity.objects.bulk_create(tag_map)

    else:
        try:
            city = City.objects.get(id=city_id)
            for k, v in city_info.iteritems():
                setattr(city, k, v)
            city.save()
        except:
            info_logger.info(__import__('traceback').format_exc())
            raise RPCNotFoundException
        related_tag_ids = set(TagMapCity.objects.using(settings.SLAVE_DB_NAME).filter(
            city_id=city.id,
        ).values_list('tag_id', flat=True))
        valid_ids = set(band_tag_ids) - related_tag_ids
        invalid_ids = related_tag_ids - set(band_tag_ids)
        TagMapCity.objects.filter(city_id=city.id, tag_id__in=invalid_ids).delete()
        tag_map = []
        for _tag_id in valid_ids:
            tag_map.append(
                TagMapCity(
                    tag_id=_tag_id,
                    city_id=city.id,
                )
            )
        TagMapCity.objects.bulk_create(tag_map)

    return city.id


@bind_context(area_pre + '/choices')
def area_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(area_name__contains=q)
    query = Area.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.area_name, obj.phone_prefix),
        } for obj in query[start_pos: start_pos + num]
    ]
    return {'total_count': total_count, 'results': results, 'page': page, 'num': num}


@bind_context(region_pre + '/query')
def region_query(ctx, options):
    dqobj = RegionDQ()
    return dqobj.process(**options)


@bind_context(region_pre + '/get')
def region_detail(ctx, region_id, options=None):
    try:
        region = Region.objects.get(id=region_id)
    except:
        raise RPCNotFoundException
    if options is None:
        options = {
            'fields': None,
            'excludes': None,
            'expands': None,
        }
    data = to_dict(region, **options)
    data['province'] = list(region.province_set.values_list('id', flat=True))
    return data


def province_has_region(error_province):
    error_message=[]
    for obj in error_province:
        error_message.append(u'{}已配置到{}大区中'.format(obj.name, obj.region.name))
    return {
        'code': -1,
        'message': ','.join(error_message)
    }

@transaction.atomic()
@bind_context(region_pre + '/edit')
def region_edit(ctx, region_id=None, region_info=None):
    if region_info is None:
        return None

    error_message=[]
    new_provices = set(region_info.pop('province'))
    region = None
    old_provices = set()

    if region_id is not None:
        region = Region.objects.get(id=region_id)
        old_provices = set(list(region.province_set.values_list('id', flat=True)))

    add_provices = Province.objects.filter(id__in=(new_provices - old_provices))
    error_provices = add_provices.filter(region_id__isnull=False)
    if error_provices.exists():
        for obj in error_provices:
            error_message.append(u'{}已配置到{}大区中'.format(obj.name, obj.region.name))
        return {
            'code': -1,
            'message': ','.join(error_message)
        }
    if (region is None or region.english_name != region_info['english_name'])\
            and Region.objects.filter(english_name=region_info['english_name']).exists():
        return {
            'code': 0,
            'id': region_id
        }

    if (region is None or region.name != region_info['name'])\
            and Region.objects.filter(name=region_info['name']).exists():
        return {
            'code': 0,
            'id': region_id
        }

    if region_id is None:
        region = Region.objects.create(**region_info)
    else:
        for k, v in region_info.iteritems():
            setattr(region, k, v)
        region.save()

    for provice in add_provices:
        provice.region_id = region.id
        provice.save()
    Province.objects.filter(id__in=(old_provices - new_provices)).update(region_id=None)
    return {
        'code': 1,
        'id': region.id
    }


@bind_context(region_pre + '/choices')
def region_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(name__contains=q)
    query = Region.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': obj.name,
        } for obj in query[start_pos: start_pos + num]
    ]
    return {'total_count': total_count, 'results': results, 'page': page, 'num': num}
